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

Allow filter strategies to set parameters (for invalidating the Doctrine cache) (Case 142739) #19

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

janopae
Copy link
Member

@janopae janopae commented Apr 28, 2022

As discussed in #18 (comment), this PR provides a better way to invalidate the Doctrine cache when the filter SQL changes.

#18 Already provides a backwards-compatible fix that generates the full filter SQL and adds it as a parameter. This PR allows the FilterStrategies to set and retrieve their parameters themselves.

This presents a breaking change, as the interface for FilterStrategies is changed.

I didn't find a way to only set the parameters if needed. If I knew a hook to only execute code always before a database query is performed, I would have used it in the first place instead of relying on the OnRequestDependencyInjector.

@janopae janopae marked this pull request as ready for review May 2, 2022 21:24
@janopae janopae requested a review from MalteWunsch May 2, 2022 21:24
@janopae
Copy link
Member Author

janopae commented May 8, 2023

@MalteWunsch ping

@janopae
Copy link
Member Author

janopae commented May 30, 2023

public function getFilterSql(string $visibilityFieldAlias): string
public function addParameters(ParameterCollection $parameters): void
{
// not needed, as $this->visibleValue is immutable
Copy link
Member

Choose a reason for hiding this comment

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

Not so sure about that: $this->visibleValue has no type checks, so it could be a mutable object with a __toString() method. But maybe that is considered a misuse and out of scope for this PR. (Could be solved in a separate PR, e.g. with a typed constructor signature, if you like)

Copy link
Member Author

Choose a reason for hiding this comment

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

Having a mutable object with a __toString() method would require ignoring the @param annotation and passing a wrong type to the constructor. So I would indeed consider it a misuse.

Would you consider passing a wrong type according to the docblock as something to be officially supported? That would mean that changing a DockBlock type declaration into a PHP type declaration was a breaking change.

The constructor uses a dockblock type declaration because it's a union type, and union types got introduced in PHP 8. This library however also supports PHP >=7.2. So we sadly can't turn it into a PHP type declaration until we drop support for PHP 7.

Copy link
Member

Choose a reason for hiding this comment

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

If this is a misuse, the // not needed comment is fine with me.

I just would like to make accidental misuse harder. If you want to support PHP 7.2, what about something like this?

    /**
     * @param string|int|bool $visibleValue
     */
    public function __construct($visibleValue)
    {
        if (!is_string($visibleValue) && !is_int($visibleValue) && !is_bool($visibleValue)) {
          throw new InvalidArgumentException();
        }
        $this->visibleValue = $visibleValue;
    }

Comment on lines +14 to +17
* factors other than parameters added to the collection, the cache won't be invalidated, which will have undesired
* consequences!
*
* So please add all parameters to the collection.
Copy link
Member

Choose a reason for hiding this comment

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

"undesired consequences" and "please" seem too vague for my taste. Would you mind explaining it in more detail (or simply remove the unprecise wording)?

Copy link
Member

Choose a reason for hiding this comment

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

which will have undesired consequences!

suggestion: which will have undesired consequences such as objects being visible for users who should not see them. (no acclamation mark)

Adds all parameters to the collection

seems to contradict

So please add all parameters to the collection.

Either the method adds all parameters or the user has to do it. Maybe "Adds the parameters" would be better, as it avoids using the identical wording.

So please add all parameters to the collection.

Suggestion: To avoid this, make sure that all parameters are added to the collection.

Copy link
Member

Choose a reason for hiding this comment

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

This class feels a bit weird to me: it cannot be used without an SQLFilter and only delegates to its methods of the name. While it's nice to have the ParameterCollection as an interface to state what exactly is needed, I'm not sure if this is worthwhile?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is one of the reasons I waited for a review – I hoped you had a strong opinion on this.

Removing the class and the interface is always easier than re-introducing it, and I think the interface makes the bundle easier to use and avoids mistakes.

On the other hand, the interface should probably be adjusted when the Doctrine SQLFilter interface changes, making this just duplicated code.

Do we need a third opinion on this?

Copy link
Member Author

@janopae janopae Jun 1, 2023

Choose a reason for hiding this comment

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

I just realised that I forgot to add support for setParameterList to the interface/the SQLFilterAsParamterCollection class. Removing the interface and using the SQLFilter directly would avoid mistakes like this in the first place.

Copy link
Member

Choose a reason for hiding this comment

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

Still no strong oppinion here, but I've given it some more thought:

You want to declare the type of the parameter for FilterStrategy->addParameters($parameters). If you would declare it as SQLFilter, the FilterStrategy interface becomes tightly coupled to the SQLFilter. Which you probably want to avoid, as exchanging the FilterStrategy is your bundle's ultimate extension point.

To preserve the freedom you currently have for FilterStrategy implementations, you should keep the ParameterCollection interface (and therefore the SQLFilterAsParameterCollection implementation).

I'm not entirely convinced that the FilterStrategy is a good extension point - maybe I lack the imagination for a useful non trivial implementation without an SQLFilter. But as I consider the FilterStrategy central for the bundle, my bottom line suggestion is: keep the ParameterCollection and SQLFilterAsParameterCollection 👍

Copy link
Member Author

@janopae janopae Feb 13, 2024

Choose a reason for hiding this comment

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

You want to declare the type of the parameter for FilterStrategy->addParameters($parameters). If you would declare it as SQLFilter, the FilterStrategy interface becomes tightly coupled to the SQLFilter. Which you probably want to avoid, as exchanging the FilterStrategy is your bundle's ultimate extension point.

To preserve the freedom you currently have for FilterStrategy implementations, you should keep the ParameterCollection interface (and therefore the SQLFilterAsParameterCollection implementation).

I'm not entirely convinced that the FilterStrategy is a good extension point - maybe I lack the imagination for a useful non trivial implementation without an SQLFilter.

Keeping the FilterStrategy Interface decoupled from the SQLFilter was indeed a reason why I introduced this interface, but it was not because I actually wanted to allow any other implementations to be used. Using other implementations than the SQLFilter-based SQLFilterAsParameterCollection is actually not even possible using the current implementation of this bundle, as the code that calls addParameters creates an instance of this class.

The ParameterCollection interface doesn't add any freedom to users of this bundle (except in unit tests, where the interface indeed eases mocking or the creation of test implementations). FilterStrategys that don't work together with an SQLFilter behind the scenes are not possible using this bundle.

The ParameterCollection interface was rather introduced for restraining the freedom of the implementor. I wanted to disallow calling any other methods on the SQLFilterthan the ones needed for managing parameters. Also, I thought that working with an SQLFilter instance in a FilterStrategy might be confusing if you don't understand the internals of this bundle. Even if you do understand the bundle, I think it would be odd (as the FilterStrategy is there to be called by an SQLFilter implementation, not to call it).

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm still not sure whether these reasons suffice as an argument for the added complexity, or whether additional documentation is needed in order to make sure that people who try to understand the implementation don't stumple over this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants