Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added FilterPersister to customize the way filters are stored #4331

Merged
merged 24 commits into from
Mar 13, 2018
Merged

Added FilterPersister to customize the way filters are stored #4331

merged 24 commits into from
Mar 13, 2018

Conversation

yann-eugone
Copy link
Contributor

@yann-eugone yann-eugone commented Feb 16, 2017

I am targetting this branch, because this feature as a backward compatibility.

Changelog

### Added
- Added some `Sonata\AdminBundle\Filter\Persister\FilterPersisterInterface` to externalise filter persistence to a dedicated class
- Added some `Sonata\AdminBundle\Filter\Persister\SessionFilterPersister` to store filters to session (as done now)

### Changed
- Changed `Sonata\AdminBundle\Admin\AbstractAdmin::getFilterParameters` to use the configured filter persister component

To do

  • Update the tests
  • Update the documentation
  • Add an upgrade note

Subject

I had to persist filters accross sessions, so I decided to persist values to a database.
But as all the code remains in the AbstractAdmin, I had to redefine the whole method in my admin base class.

I did this PR so we can extend easily the way filters are persisted. We can also create more out-of-the-box filter persisters, such as : ORM, ODM, Redis...

*/
protected $persistFilters = false;
protected $filterPersister;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a BC break

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right... if we want to do it cleanly, we should remove this property

@OskarStark
Copy link
Member

If we can stay BC 3.x is fine, otherwise we should deprecate the current logic and do this PR against master

/**
* @param FilterPersisterInterface $filterPersister
*/
public function setFilterPersister($filterPersister)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use type hinting here, and make it optional so that null can be passed.

* Whether or not to persist the filters in the session.
*
* @var bool
* @var FilterPersisterInterface|bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FilterPersisterInterface|null

@@ -766,16 +766,28 @@ public function getFilterParameters()
{
$parameters = array();

// if filter persister was configured with `true`, use the default persister (session)
if ($this->filterPersister === true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null

@@ -766,16 +766,28 @@ public function getFilterParameters()
{
$parameters = array();

// if filter persister was configured with `true`, use the default persister (session)
if ($this->filterPersister === true) {
$this->filterPersister = new SessionFilterPersister($this->request->getSession());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be done with DI, BTW? If no filterPersister is set, inject this default service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my aim was to avoid deprecation, but yeah i can do it like it, but the boolean mode will have to be removed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting rid of the boolean mode is a good thing, b/c then it will be easier to reason on the type of filterPersister, and use native checks like type hinting.

if ($filters == array() && $this->request->query->get('filters') != 'reset') {
$filters = $this->request->getSession()->get($this->getCode().'.filter.parameters', array());
// if filter persistence is configured
if ($this->filterPersister instanceof FilterPersisterInterface) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What else can it be?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not a big fan of falsy tests, i rather test type of my object

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big fan either, it was an honest question b/c I hadn't seen it could be boolean yet.

// if persist filters is a boolean use session persister as default
// if persist filters is a string, use service reference instead
if (is_bool($persistFilters)) {
$definition->addMethodCall('setPersistFilters', array($persistFilters));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If true, inject default service, if false, don't do anything.

@yann-eugone
Copy link
Contributor Author

Should I close this PR, and reopen onto the master branch, with service injection ?

@greg0ire
Copy link
Contributor

Should I close this PR, and reopen onto the master branch, with service injection ?

You mean it can't be BC?

*/
public function reset($admin)
{
$this->session->remove($admin.'.filter.parameters');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please isolate the pattern building in a dedicated method.

* Set persisted filters for given admin.
*
* @param string $admin The admin code
* @param array $filters The filters to persist
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document what the array is supposed to look like (can it be multidimensional? What type are the keys, what are they?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure is the same than the filter values. It depends on what filters you are using.

@yann-eugone
Copy link
Contributor Author

@greg0ire I think so. We need to remove the persistFilters property (it may be confusing), and use service setter injection only.

this is a BC right ?

@greg0ire
Copy link
Contributor

Removing a protected property is not BC. It's a BC-break and the property should be deprecated instead.

Can you try making the PR on 3.x? If we can't find a solution, then you'll have to rebase on master, but right now it's hard for me to imagine what would be a BC-break.

@yann-eugone
Copy link
Contributor Author

Ok, I juste pushed a new version.

*
* @var FilterPersisterInterface|null
*/
protected $filterPersister;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about making it private?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a code for myself, everything is private, but here, other properties are protected, i just tried to code like the rest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, good thinking, but please make it private anyway, we're trying to change things.

* @var bool
*
* @deprecated since 3.x, to be removed in 4.0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use magic to trigger deprecation notices here, but since it's the admin class, which is already big, and other properties could be deprecated, this could become a maintenance nightmare, so I'd say leave it like that.

*
* @return string The storage key
*/
protected function buildStorageKey($admin)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private please

*
* @author Yann Eugoné <eugone.yann@gmail.com>
*/
class SessionFilterPersister implements FilterPersisterInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it final please

* "_page" => {int page num},
* "_sort_by" => {string sort property},
* "_sort_order" => {string sort order (ASC|DESC)},
* "_per_page" => {int count rows per page}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

@greg0ire
Copy link
Contributor

Looks BC to me.

}

$definition->addMethodCall('setPersistFilters', array($persistFilters));
// if boolean config value provided, use the default persister : session
if (is_bool($persistFilters) && $persistFilters) {
Copy link
Member

@jordisala1991 jordisala1991 Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to check if is_bool($persistFilters)? This property is set from container parameter or from the attributes, and we can cast it previously on the other case.

Maybe we can change from scalarNode to booleanNode on the Bundle Configuration?

Just to have properties that we know they are a concrete type and can't be other things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code may also receive value from the tag of an admin service. I think that allowing true to be provided, may be a short but efficient way to configure the persistence.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. So if it is a boolean and true, we just change its value to the default persister, otherwise we chech if the value is there set by config (other provider or default) and then we call to setFilterPersister. It is something like that? was confused at first

Copy link
Contributor Author

@yann-eugone yann-eugone Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you got it. configuring persist_filters to true means

i don't care how you persist it, just do it

@jordisala1991
Copy link
Member

This is super nice!

We should remember to update other bundles mocking some classes that extends AbstractAdmin, because those test will broke.

@greg0ire
Copy link
Contributor

We should remember to update other bundles mocking some classes that extends AbstractAdmin, because those test will broke.

@OskarStark see? Don't mock what you don't own ;)

@jordisala1991
Copy link
Member

Agree with that, but when you are extending a class that you dont own. Either you mock it or you dont test your logic. Ideally extend classes is not a good practice in TDD but in some cases like sonataMedia we are not TDDing, but just adding test for the current code. If I have to choose between fragile test or fragile code IMO prefer to fix test all day long.

@OskarStark
Copy link
Member

@OskarStark see? Don't mock what you don't own ;)

👍

@OskarStark
Copy link
Member

@yann-eugone can you please fix StyleCi?

@yann-eugone
Copy link
Contributor Author

@OskarStark just pushed the fix, but not sure to understand the problem with AbstractAdmin::$filter property

@yann-eugone
Copy link
Contributor Author

@greg0ire @OskarStark @jordisala1991 @core23 just rebased the whole Pull Request for the 2nd time, hope everything will be OK this time

@greg0ire greg0ire merged commit 2579fc4 into sonata-project:3.x Mar 13, 2018
@greg0ire
Copy link
Contributor

Thanks a lot for this!

@OskarStark
Copy link
Member

Thank you very much, great work @yann-eugone 👍

@yann-eugone
Copy link
Contributor Author

Happy to finally find a way to contribute 😄

$persistFilters = (bool) $attributes['persist_filters'];
} else {
$persistFilters = (bool) $container->getParameter('sonata.admin.configuration.filters.persist');
$persistFilters = $attributes['persist_filters'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be located here? @soullivaneuh , before we had a cast to (bool) and now we don't. Maybe the value is getting there but not with the correct type?

Copy link
Contributor

@kunicmarko20 kunicmarko20 Apr 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, "true" would also be considered as a true so not sure that is it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"true" !== true

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if strict is used 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (isset($attributes['persist_filters'])) {
$persistFilters = $attributes['persist_filters'];
}
$filtersPersister = $container->getParameter('sonata.admin.configuration.filters.persister');
// override default configuration with admin config if set
if (isset($attributes['filter_persister'])) {
$filtersPersister = $attributes['filter_persister'];
}
// configure filters persistence, if configured to
if ($persistFilters) {

and it is not

pestaa pushed a commit to Crosssec/SonataAdminBundle that referenced this pull request Aug 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants