Magic Doctrine2 finders when field has underscore?
Asked Answered
B

1

6

I'm having problems using find*() magic methods of Doctrine2 when the field has an underscore in between.

$repository->findByName("Hello"); // Works
$repository->findByIsEnabled(true);

Entity 'Acme\SecurityBundle\Entity\Package' has no field 'isEnabled'. You can therefore not call 'findByIsEnabled' on the entities' repository.

This is the simple entity definition in YAML for replicating the error:

Acme\SecurityBundle\Entity\Package:
  type: entity
  repositoryClass: Acme\SecurityBundle\Repository\PackageRepository
  table: security_package
  id:
    id:
      type: integer
      generator: { strategy: AUTO }
  fields:
    name:
      type: string
      length: 255
      unique: true
    is_enabled:
      type: boolean
Brehm answered 11/3, 2012 at 21:51 Comment(0)
C
16

I recall having had the same problem and think I solved it by writing something like this :

$repository->findBy(array('is_enabled' => true));

Let's look at the code :

<?php
/**
 * Adds support for magic finders.
 *
 * @return array|object The found entity/entities.
 * @throws BadMethodCallException  If the method called is an invalid find* method
 *                                 or no find* method at all and therefore an invalid
 *                                 method call.
 */
public function __call($method, $arguments)
{   
    if (substr($method, 0, 6) == 'findBy') {
        $by = substr($method, 6, strlen($method));
        $method = 'findBy';
    } else if (substr($method, 0, 9) == 'findOneBy') {
        $by = substr($method, 9, strlen($method));
        $method = 'findOneBy';
    } else {
        throw new \BadMethodCallException(
            "Undefined method '$method'. The method name must start with ".
            "either findBy or findOneBy!"
        );  
    }   

    if ( !isset($arguments[0])) {
        // we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
        throw ORMException::findByRequiresParameter($method.$by);
    }

    $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));

    if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
        return $this->$method(array($fieldName => $arguments[0]));
    } else {
        throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
    }
}

The key line is here:

$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));

Now let's have a look to classify :

<?php
/**
 * Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName'
 *
 * @param string  $word  Word to classify
 * @return string $word  Classified word
 */
public static function classify($word)
{
    return str_replace(" ", "", ucwords(strtr($word, "_-", "  ")));
}

It looks like you're supposed to write your fields "likeThis" if you want this to work.

Concordant answered 11/3, 2012 at 22:13 Comment(6)
Yes i'm doing exactly the same now (as a workaround), but i prefer the "magic" way. Is this a bug?Brehm
Can you find an example in the documentation? I can't. Similar problem in this other question, maybe the magic part is being discouraged.Concordant
Yes, but in Symfony documentation, take a look: symfony.com/doc/current/book/doctrine.html, middle of the page.Brehm
I think these slides explain well why there is a "witch hunt" : slideshare.net/jwage/doctrine-2-not-the-same-old-php-ormConcordant
Taken a quick look, got the point that magic is bad... but it doesn't answer the question :)Brehm
I updated my answer with doctrine code... obviously, this is not supposed to work at all.Concordant

© 2022 - 2024 — McMap. All rights reserved.