vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php line 232

Open in your IDE?
  1. <?php
  2. /*
  3.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14.  *
  15.  * This software consists of voluntary contributions made by many individuals
  16.  * and is licensed under the MIT license. For more information, see
  17.  * <http://www.doctrine-project.org>.
  18.  */
  19. namespace Doctrine\ORM;
  20. use BadMethodCallException;
  21. use Doctrine\Common\Collections\Collection;
  22. use Doctrine\Common\Collections\Criteria;
  23. use Doctrine\Common\Collections\Selectable;
  24. use Doctrine\Deprecations\Deprecation;
  25. use Doctrine\Inflector\Inflector;
  26. use Doctrine\Inflector\InflectorFactory;
  27. use Doctrine\ORM\Mapping\ClassMetadata;
  28. use Doctrine\ORM\Query\ResultSetMappingBuilder;
  29. use Doctrine\Persistence\ObjectRepository;
  30. use function array_slice;
  31. use function lcfirst;
  32. use function sprintf;
  33. use function strpos;
  34. use function substr;
  35. /**
  36.  * An EntityRepository serves as a repository for entities with generic as well as
  37.  * business specific methods for retrieving entities.
  38.  *
  39.  * This class is designed for inheritance and users can subclass this class to
  40.  * write their own repositories with business-specific methods to locate entities.
  41.  *
  42.  * @template T
  43.  * @template-implements Selectable<int,T>
  44.  * @template-implements ObjectRepository<T>
  45.  */
  46. class EntityRepository implements ObjectRepositorySelectable
  47. {
  48.     /** @var string */
  49.     protected $_entityName;
  50.     /** @var EntityManager */
  51.     protected $_em;
  52.     /** @var ClassMetadata */
  53.     protected $_class;
  54.     /** @var Inflector */
  55.     private static $inflector;
  56.     /**
  57.      * Initializes a new <tt>EntityRepository</tt>.
  58.      */
  59.     public function __construct(EntityManagerInterface $emMapping\ClassMetadata $class)
  60.     {
  61.         $this->_entityName $class->name;
  62.         $this->_em         $em;
  63.         $this->_class      $class;
  64.     }
  65.     /**
  66.      * Creates a new QueryBuilder instance that is prepopulated for this entity name.
  67.      *
  68.      * @param string $alias
  69.      * @param string $indexBy The index for the from.
  70.      *
  71.      * @return QueryBuilder
  72.      */
  73.     public function createQueryBuilder($alias$indexBy null)
  74.     {
  75.         return $this->_em->createQueryBuilder()
  76.             ->select($alias)
  77.             ->from($this->_entityName$alias$indexBy);
  78.     }
  79.     /**
  80.      * Creates a new result set mapping builder for this entity.
  81.      *
  82.      * The column naming strategy is "INCREMENT".
  83.      *
  84.      * @param string $alias
  85.      *
  86.      * @return ResultSetMappingBuilder
  87.      */
  88.     public function createResultSetMappingBuilder($alias)
  89.     {
  90.         $rsm = new ResultSetMappingBuilder($this->_emResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
  91.         $rsm->addRootEntityFromClassMetadata($this->_entityName$alias);
  92.         return $rsm;
  93.     }
  94.     /**
  95.      * Creates a new Query instance based on a predefined metadata named query.
  96.      *
  97.      * @deprecated
  98.      *
  99.      * @param string $queryName
  100.      *
  101.      * @return Query
  102.      */
  103.     public function createNamedQuery($queryName)
  104.     {
  105.         Deprecation::trigger(
  106.             'doctrine/orm',
  107.             'https://github.com/doctrine/orm/issues/8592',
  108.             'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
  109.             $queryName,
  110.             $this->_class->name
  111.         );
  112.         return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
  113.     }
  114.     /**
  115.      * Creates a native SQL query.
  116.      *
  117.      * @deprecated
  118.      *
  119.      * @param string $queryName
  120.      *
  121.      * @return NativeQuery
  122.      */
  123.     public function createNativeNamedQuery($queryName)
  124.     {
  125.         Deprecation::trigger(
  126.             'doctrine/orm',
  127.             'https://github.com/doctrine/orm/issues/8592',
  128.             'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
  129.             $queryName,
  130.             $this->_class->name
  131.         );
  132.         $queryMapping $this->_class->getNamedNativeQuery($queryName);
  133.         $rsm          = new Query\ResultSetMappingBuilder($this->_em);
  134.         $rsm->addNamedNativeQueryMapping($this->_class$queryMapping);
  135.         return $this->_em->createNativeQuery($queryMapping['query'], $rsm);
  136.     }
  137.     /**
  138.      * Clears the repository, causing all managed entities to become detached.
  139.      *
  140.      * @deprecated 2.8 This method is being removed from the ORM and won't have any replacement
  141.      *
  142.      * @return void
  143.      */
  144.     public function clear()
  145.     {
  146.         Deprecation::trigger(
  147.             'doctrine/orm',
  148.             'https://github.com/doctrine/orm/issues/8460',
  149.             'Calling %s() is deprecated and will not be supported in Doctrine ORM 3.0.',
  150.             __METHOD__
  151.         );
  152.         $this->_em->clear($this->_class->rootEntityName);
  153.     }
  154.     /**
  155.      * Finds an entity by its primary key / identifier.
  156.      *
  157.      * @param mixed    $id          The identifier.
  158.      * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
  159.      *                              or NULL if no specific lock mode should be used
  160.      *                              during the search.
  161.      * @param int|null $lockVersion The lock version.
  162.      *
  163.      * @return object|null The entity instance or NULL if the entity can not be found.
  164.      * @psalm-return ?T
  165.      */
  166.     public function find($id$lockMode null$lockVersion null)
  167.     {
  168.         return $this->_em->find($this->_entityName$id$lockMode$lockVersion);
  169.     }
  170.     /**
  171.      * Finds all entities in the repository.
  172.      *
  173.      * @psalm-return list<T> The entities.
  174.      */
  175.     public function findAll()
  176.     {
  177.         return $this->findBy([]);
  178.     }
  179.     /**
  180.      * Finds entities by a set of criteria.
  181.      *
  182.      * @param int|null $limit
  183.      * @param int|null $offset
  184.      * @psalm-param array<string, mixed> $criteria
  185.      * @psalm-param array<string, string>|null $orderBy
  186.      *
  187.      * @return object[] The objects.
  188.      * @psalm-return list<T>
  189.      */
  190.     public function findBy(array $criteria, ?array $orderBy null$limit null$offset null)
  191.     {
  192.         $persister $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
  193.         return $persister->loadAll($criteria$orderBy$limit$offset);
  194.     }
  195.     /**
  196.      * Finds a single entity by a set of criteria.
  197.      *
  198.      * @psalm-param array<string, mixed> $criteria
  199.      * @psalm-param array<string, string>|null $orderBy
  200.      *
  201.      * @return object|null The entity instance or NULL if the entity can not be found.
  202.      * @psalm-return ?T
  203.      */
  204.     public function findOneBy(array $criteria, ?array $orderBy null)
  205.     {
  206.         $persister $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
  207.         return $persister->load($criterianullnull, [], null1$orderBy);
  208.     }
  209.     /**
  210.      * Counts entities by a set of criteria.
  211.      *
  212.      * @psalm-param array<string, mixed> $criteria
  213.      *
  214.      * @return int The cardinality of the objects that match the given criteria.
  215.      *
  216.      * @todo Add this method to `ObjectRepository` interface in the next major release
  217.      */
  218.     public function count(array $criteria)
  219.     {
  220.         return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->count($criteria);
  221.     }
  222.     /**
  223.      * Adds support for magic method calls.
  224.      *
  225.      * @param string  $method
  226.      * @param mixed[] $arguments
  227.      * @psalm-param list<mixed> $arguments
  228.      *
  229.      * @return mixed The returned value from the resolved method.
  230.      *
  231.      * @throws ORMException
  232.      * @throws BadMethodCallException If the method called is invalid.
  233.      */
  234.     public function __call($method$arguments)
  235.     {
  236.         if (strpos($method'findBy') === 0) {
  237.             return $this->resolveMagicCall('findBy'substr($method6), $arguments);
  238.         }
  239.         if (strpos($method'findOneBy') === 0) {
  240.             return $this->resolveMagicCall('findOneBy'substr($method9), $arguments);
  241.         }
  242.         if (strpos($method'countBy') === 0) {
  243.             return $this->resolveMagicCall('count'substr($method7), $arguments);
  244.         }
  245.         throw new BadMethodCallException(sprintf(
  246.             'Undefined method "%s". The method name must start with ' .
  247.             'either findBy, findOneBy or countBy!',
  248.             $method
  249.         ));
  250.     }
  251.     /**
  252.      * @return string
  253.      */
  254.     protected function getEntityName()
  255.     {
  256.         return $this->_entityName;
  257.     }
  258.     /**
  259.      * @return string
  260.      */
  261.     public function getClassName()
  262.     {
  263.         return $this->getEntityName();
  264.     }
  265.     /**
  266.      * @return EntityManager
  267.      */
  268.     protected function getEntityManager()
  269.     {
  270.         return $this->_em;
  271.     }
  272.     /**
  273.      * @return Mapping\ClassMetadata
  274.      */
  275.     protected function getClassMetadata()
  276.     {
  277.         return $this->_class;
  278.     }
  279.     /**
  280.      * Select all elements from a selectable that match the expression and
  281.      * return a new collection containing these elements.
  282.      *
  283.      * @return LazyCriteriaCollection
  284.      * @psalm-return Collection<int, T>
  285.      */
  286.     public function matching(Criteria $criteria)
  287.     {
  288.         $persister $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
  289.         return new LazyCriteriaCollection($persister$criteria);
  290.     }
  291.     /**
  292.      * Resolves a magic method call to the proper existent method at `EntityRepository`.
  293.      *
  294.      * @param string $method The method to call
  295.      * @param string $by     The property name used as condition
  296.      * @psalm-param list<mixed> $arguments The arguments to pass at method call
  297.      *
  298.      * @return mixed
  299.      *
  300.      * @throws ORMException If the method called is invalid or the requested field/association does not exist.
  301.      */
  302.     private function resolveMagicCall(string $methodstring $by, array $arguments)
  303.     {
  304.         if (! $arguments) {
  305.             throw ORMException::findByRequiresParameter($method $by);
  306.         }
  307.         if (self::$inflector === null) {
  308.             self::$inflector InflectorFactory::create()->build();
  309.         }
  310.         $fieldName lcfirst(self::$inflector->classify($by));
  311.         if (! ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName))) {
  312.             throw ORMException::invalidMagicCall($this->_entityName$fieldName$method $by);
  313.         }
  314.         return $this->$method([$fieldName => $arguments[0]], ...array_slice($arguments1));
  315.     }
  316. }