Skip to content

ClosureExpressionVisitor > Don't duplicate the accessor when the field already starts with it #131

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

Merged
merged 1 commit into from
Mar 5, 2019

Conversation

ruudk
Copy link
Contributor

@ruudk ruudk commented Aug 30, 2017

This PR tries to fix an issue I am facing in my application that uses Doctrine2 ORM.

I have the following setup:

class Platform {
    // ..

    /**
     * @ORM\OneToMany(targetEntity="PaymentMethod", mappedBy="platform")
     */
    private $paymentMethods;
}

class PaymentMethod {
    // ...

    /**
     * @ORM\Column(type="boolean")
     */
    private $isActive = true;
}

I'm trying to get all PaymentMethods from the Platform that are active:

class Platform {
    // ..

    public function getActivePaymentMethods() : Collection
    {
        $criteria = Criteria::create()
            ->where(Criteria::expr()->eq('isActive', true));

        return $this->paymentMethods->matching($criteria);
    }
}

When I call $platform->getActivePaymentMethods() I get:

  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Cannot access private property PaymentMethod::$isActive

I can fix this of course by changing my Criteria to Criteria::expr()->eq('active', true) but then I will get in troubles when it tries to run the expression on the PersistentCollection instead of the ArrayCollection.

This fix makes the getter a bit smarter. When you prefix the field with is* it doesn't re-apply is as an accessor. Instead of isIsActive it will just do isActive.

@ruudk
Copy link
Contributor Author

ruudk commented Dec 3, 2017

Is there anything I can do to bring this PR forward? I'm currently using this fork in my projects, would be great to rely on a stable tag again :D 🙏

@@ -49,7 +49,12 @@ public static function getObjectFieldValue($object, $field)
$accessors = ['get', 'is'];

foreach ($accessors as $accessor) {
$accessor .= $field;
// Don't duplicate the accessor when the field already starts with it
if (0 === strpos($field, $accessor)) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm afraid this check on its own will lead to false positives: issue, island, isolation. Better heuristic would be to look for is followed by a capital letter

Copy link
Member

Choose a reason for hiding this comment

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

But then it can be worse performace-wise, not sure if it's worth the hassle

@ruudk
Copy link
Contributor Author

ruudk commented Dec 5, 2017

@malarzm I implemented a different approach: f965654

This way, it will always call $object->$field() when the method exists.

@ruudk
Copy link
Contributor Author

ruudk commented Dec 5, 2017

I added another approach, using a nice regex, in 7fa9e35. I think this is the winner. What do you think?

I will cleanup/squash the commits once its ok.

@ruudk
Copy link
Contributor Author

ruudk commented Sep 21, 2018

@malarzm what does it take to get this merged? Thanks :)

@ruudk ruudk force-pushed the expression-visitor branch from b0e0ac0 to ed85eda Compare October 2, 2018 17:58
@ruudk
Copy link
Contributor Author

ruudk commented Oct 2, 2018

Rebased the PR, squashed it and applied Doctrine CS.

@Ocramius Ocramius added the Bug label Oct 2, 2018
@Ocramius Ocramius added this to the 1.6.0 milestone Oct 2, 2018
@ruudk
Copy link
Contributor Author

ruudk commented Mar 4, 2019

@Ocramius do you think this can be merged? If not, what is needed to get it to a mergable state?

Copy link
Member

@SenseException SenseException left a comment

Choose a reason for hiding this comment

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

I would like to have this PR merged to prevent keeping it longer on hold for a second year. Except my question on the Regex, there's nothing to add from my side.

On a side note: method_exists also returns true when the method is private (https://3v4l.org/4pBgc). This part of the library was always prone to bugs, but maybe we can handle this differently in another PR, if it is desired.


return $object->$accessor();
if (preg_match('/^is[A-Z]+/', $field) === 1 && method_exists($object, $field)) {
Copy link
Member

Choose a reason for hiding this comment

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

@Ocramius Do we need this Regex check? Having only the method_exists(), we could even use accessor-methods that haven't a is or get prefix.

@alcaeus
Copy link
Member

alcaeus commented Mar 5, 2019

I agree, let's get this merged. TBH, I would keep the logic as-is and rather refactor this in a separate pull request.

@ruudk could you rebase one last time? Thanks!

alcaeus
alcaeus previously approved these changes Mar 5, 2019
@ruudk
Copy link
Contributor Author

ruudk commented Mar 5, 2019

Done. Should I squash all the commits into 1 or do you do this on merge?

@alcaeus
Copy link
Member

alcaeus commented Mar 5, 2019

If you don't mind I'd appreciate a squash before merging - less work for me 😉

@ruudk ruudk force-pushed the expression-visitor branch from 1695209 to 5752bc9 Compare March 5, 2019 08:31
@ruudk
Copy link
Contributor Author

ruudk commented Mar 5, 2019

Done :)

@alcaeus
Copy link
Member

alcaeus commented Mar 5, 2019

phpcs is still complaining: https://travis-ci.org/doctrine/collections/jobs/501915949#L558

You can fix this by running vendor/bin/phpcbf lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php.

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
When your property is called `private $isActive = true` with `function isActive()` as the getter, it should be possible to query it with `isActive = true` without getting errors.
@ruudk ruudk force-pushed the expression-visitor branch from 5752bc9 to f43cf44 Compare March 5, 2019 17:05
@ruudk
Copy link
Contributor Author

ruudk commented Mar 5, 2019

Fixed it manually...

PHPCS doesn't work really nice, see:

phpcbf lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php
ERROR: Referenced sniff "Doctrine" does not exist

Run "phpcbf --help" for usage information

                                                                                                                                                                                                                   0.000001d 0.000019h 0.001133m 0.068s | 18:06
/w/collections (expression-visitor|✔) $ vendor/bin/phpcbf lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php
PHP Fatal error:  Uncaught Error: Class 'PHP_CodeSniffer\Runner' not found in /Volumes/CS/www/collections/vendor/bin/phpcbf:17
Stack trace:
#0 {main}
  thrown in /Volumes/CS/www/collections/vendor/bin/phpcbf on line 17

Fatal error: Uncaught Error: Class 'PHP_CodeSniffer\Runner' not found in /Volumes/CS/www/collections/vendor/bin/phpcbf:17
Stack trace:
#0 {main}
  thrown in /Volumes/CS/www/collections/vendor/bin/phpcbf on line 17
                                                                                                                                                                                                                    0.000001d 0.000019h 0.001167m 0.07s | 18:06
/w/collections (expression-visitor|✔) $ composer install


  [Symfony\Component\Process\Exception\ProcessFailedException]
  The command "'/Volumes/CS/www/collections/vendor/bin/phpcs' '--config-show' 'installed_paths'" failed.

  Exit Code: 255(Unknown error)

  Working directory: /Volumes/CS/www/collections

  Output:
  ================

  Fatal error: Uncaught Error: Class 'PHP_CodeSniffer\Runner' not found in /Volumes/CS/www/collections/vendor/bin/phpcs:17
  Stack trace:
  #0 {main}
    thrown in /Volumes/CS/www/collections/vendor/bin/phpcs on line 17


  Error Output:
  ================
  PHP Fatal error:  Uncaught Error: Class 'PHP_CodeSniffer\Runner' not found in /Volumes/CS/www/collections/vendor/bin/phpcs:17
  Stack trace:
  #0 {main}
    thrown in /Volumes/CS/www/collections/vendor/bin/phpcs on line 17


Nothing works.

@alcaeus
Copy link
Member

alcaeus commented Mar 5, 2019

Thanks for the heads up, and sorry you had to fix it manually. Let me take another look at that after taking care of your PR 👍

@alcaeus
Copy link
Member

alcaeus commented Mar 5, 2019

FWIW, build completed successfully but build result wasn’t reported back correctly: https://travis-ci.org/doctrine/collections/builds/502138875.

Thanks @ruudk!

@alcaeus alcaeus merged commit a99d26d into doctrine:master Mar 5, 2019
@ruudk
Copy link
Contributor Author

ruudk commented Mar 5, 2019

Wow, thanks for merging it 🙌🥳

@alcaeus
Copy link
Member

alcaeus commented Mar 5, 2019

Don’t celebrate too soon, need to dig into the changes before releasing a new version. I hope to be able to do it shortly.

@alcaeus alcaeus added Enhancement and removed Bug labels Mar 6, 2019
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.

None yet

5 participants