Skip to content

Allow typed finder arguments #16782

Closed
othercorey wants to merge 1 commit into5.xfrom
finder-arguments
Closed

Allow typed finder arguments #16782
othercorey wants to merge 1 commit into5.xfrom
finder-arguments

Conversation

@othercorey
Copy link
Copy Markdown
Contributor

@othercorey othercorey commented Oct 1, 2022

refs #12787

This does not remove the $options parameter. Users must specify an empty array if they want to by-pass it.

The $options parameter is now optional.

@othercorey othercorey added this to the 4.5.0 milestone Oct 1, 2022
@othercorey
Copy link
Copy Markdown
Contributor Author

othercorey commented Oct 1, 2022

@robertpustulka I remember arguing about this back when I knew nothing. I hope this looks reasonable.

@othercorey othercorey force-pushed the finder-arguments branch 2 times, most recently from db87083 to e8a8e13 Compare October 1, 2022 08:16
@othercorey othercorey changed the title Allow typed finder arguments after the $options array Allow typed finder arguments Oct 9, 2022
@othercorey othercorey requested review from ADmad and markstory October 9, 2022 05:32
@othercorey othercorey force-pushed the finder-arguments branch 5 times, most recently from 6d5903b to 7c78e5b Compare October 9, 2022 09:31
/** @psalm-suppress ReferenceReusedFromConfusingScope */
$options = $options + $opts;
} else {
array_unshift($args, $opts);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Users will receive a PHP error if they don't have a proper array parameter for options. I can't create exceptions for all edge cases leading to this in all finders so going to rely on PHP errors to tell users their finder can't support options.

$reflected = new ReflectionFunction($callable);
$param = $reflected->getParameters()[1] ?? null;
if ($param?->name === 'options' && !$param->isDefaultValueAvailable()) {
$args = [[]];
Copy link
Copy Markdown
Contributor Author

@othercorey othercorey Oct 9, 2022

Choose a reason for hiding this comment

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

This adds an empty options array when calling find with no arguments and the finder expects $options.

An example of this is Table::findAll() and calling it with ->find().

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's quite unfortunate that the $options arg doesn't default to an empty array currently. It would have made no practical difference.

Perhaps we can add the default value now to core finders and throw a warning when this if returns true so that we can at least remove this reflection in the future.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps we can add the default value now to core finders and throw a warning when this if returns true so that we can at least remove this reflection in the future.

This sounds like a great inclusion into 4.5.0. We could do the detection and deprecation warning there, and in 5.x we can enforce a default options value, or we can start spreading options as named parameters.

I think the point I want to make, is that if we're going to break compatibility why not do what would be best going forward, and then we can figure out how we add deprecations and backporting to 4.5 to make the upgrade process understandable and incrementally adoptable.

$reflected = new ReflectionFunction($callable);
$param = $reflected->getParameters()[1] ?? null;
if ($param?->name === 'options' && !$param->isDefaultValueAvailable()) {
$args = [[]];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's quite unfortunate that the $options arg doesn't default to an empty array currently. It would have made no practical difference.

Perhaps we can add the default value now to core finders and throw a warning when this if returns true so that we can at least remove this reflection in the future.

if ($opts) {
if (is_array($options)) {
/** @psalm-suppress ReferenceReusedFromConfusingScope */
$options = $options + $opts;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we use options after this merge? Or are you relying on the ref from the previous block to mutate the options in-place? We should leave a comment for future us if that is the solution that is used here.

Comment on lines +1076 to +1092
$options = null;
if ($args) {
if (array_key_exists(0, $args)) {
$options = &$args[0];
} elseif (array_key_exists('options', $args)) {
$options = &$args['options'];
}
}

if ($opts) {
if (is_array($options)) {
/** @psalm-suppress ReferenceReusedFromConfusingScope */
$options = $options + $opts;
} else {
array_unshift($args, $opts);
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should there be a function for this? There are a few implementations of this in these changes. Having a function might let us contain the reference tricks into code into that is less frequently changed and easier to remove when the time comes.

$reflected = new ReflectionFunction($callable);
$param = $reflected->getParameters()[1] ?? null;
if ($param?->name === 'options' && !$param->isDefaultValueAvailable()) {
$args = [[]];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps we can add the default value now to core finders and throw a warning when this if returns true so that we can at least remove this reflection in the future.

This sounds like a great inclusion into 4.5.0. We could do the detection and deprecation warning there, and in 5.x we can enforce a default options value, or we can start spreading options as named parameters.

I think the point I want to make, is that if we're going to break compatibility why not do what would be best going forward, and then we can figure out how we add deprecations and backporting to 4.5 to make the upgrade process understandable and incrementally adoptable.

* Here, the finder "findByCategory" does have an `$options` parameter:
*
* ```
* function findByCategory(SelectQuery $query, array $options, int $category): SelectQuery
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the spirit of doing what is best going forward, I would like to propose we drop support for $options entirely. Instead we only support named parameters in 5.x. This is a meaningful break in backwards compatibility, but I think it will provide a long term improvement in developer experience. With named parameters we get typed checked optional parameter support. It's everything we ever wanted from $options.

I feel like deprecating $options in 4.5 is something we can do. Users that would want to upgrade to 5.x will need to upgrade PHP before they can use named parameters. I don't feel bad about this as encouraging them to upgrade PHP will be something they would need to do anyways.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Making $options options is definitely complicated. It looks like code we don't want to support.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Are you suggesting we drop $options and force users to call applyOptions() directly? I'm a little confused when you say drop it but then say support it with named parameters.

Copy link
Copy Markdown
Member

@ADmad ADmad Oct 11, 2022

Choose a reason for hiding this comment

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

I would be okay with dropping $options if we retain a way to still be able to pass values for the query clauses to the find() call. Something like find('foo', select: $a, order: $b, custom: 'bar').

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We could do that. All of the query-specific options would then become reserved keywords for parameter names.

Comment on lines +2620 to +2622
$query->applyOptions($options);
/** @psalm-suppress ReferenceReusedFromConfusingScope */
$options = $query->getOptions();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should do this after the options juggling function is completed.

@othercorey
Copy link
Copy Markdown
Contributor Author

We don't want to commit this optional $options implementation. Will look for alternatives.

@othercorey othercorey closed this Oct 13, 2022
@othercorey othercorey deleted the finder-arguments branch October 13, 2022 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants