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

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.      * @psalm-param Mapping\ClassMetadata $class
  60.      */
  61.     public function __construct(EntityManagerInterface $emMapping\ClassMetadata $class)
  62.     {
  63.         $this->_entityName $class->name;
  64.         $this->_em         $em;
  65.         $this->_class      $class;
  66.     }
  67.     /**
  68.      * Creates a new QueryBuilder instance that is prepopulated for this entity name.
  69.      *
  70.      * @param string $alias
  71.      * @param string $indexBy The index for the from.
  72.      *
  73.      * @return QueryBuilder
  74.      */
  75.     public function createQueryBuilder($alias$indexBy null)
  76.     {
  77.         return $this->_em->createQueryBuilder()
  78.             ->select($alias)
  79.             ->from($this->_entityName$alias$indexBy);
  80.     }
  81.     /**
  82.      * Creates a new result set mapping builder for this entity.
  83.      *
  84.      * The column naming strategy is "INCREMENT".
  85.      *
  86.      * @param string $alias
  87.      *
  88.      * @return ResultSetMappingBuilder
  89.      */
  90.     public function createResultSetMappingBuilder($alias)
  91.     {
  92.         $rsm = new ResultSetMappingBuilder($this->_emResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
  93.         $rsm->addRootEntityFromClassMetadata($this->_entityName$alias);
  94.         return $rsm;
  95.     }
  96.     /**
  97.      * Creates a new Query instance based on a predefined metadata named query.
  98.      *
  99.      * @deprecated
  100.      *
  101.      * @param string $queryName
  102.      *
  103.      * @return Query
  104.      */
  105.     public function createNamedQuery($queryName)
  106.     {
  107.         Deprecation::trigger(
  108.             'doctrine/orm',
  109.             'https://github.com/doctrine/orm/issues/8592',
  110.             'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
  111.             $queryName,
  112.             $this->_class->name
  113.         );
  114.         return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
  115.     }
  116.     /**
  117.      * Creates a native SQL query.
  118.      *
  119.      * @deprecated
  120.      *
  121.      * @param string $queryName
  122.      *
  123.      * @return NativeQuery
  124.      */
  125.     public function createNativeNamedQuery($queryName)
  126.     {
  127.         Deprecation::trigger(
  128.             'doctrine/orm',
  129.             'https://github.com/doctrine/orm/issues/8592',
  130.             'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
  131.             $queryName,
  132.             $this->_class->name
  133.         );
  134.         $queryMapping $this->_class->getNamedNativeQuery($queryName);
  135.         $rsm          = new Query\ResultSetMappingBuilder($this->_em);
  136.         $rsm->addNamedNativeQueryMapping($this->_class$queryMapping);
  137.         return $this->_em->createNativeQuery($queryMapping['query'], $rsm);
  138.     }
  139.     /**
  140.      * Clears the repository, causing all managed entities to become detached.
  141.      *
  142.      * @deprecated 2.8 This method is being removed from the ORM and won't have any replacement
  143.      *
  144.      * @return void
  145.      */
  146.     public function clear()
  147.     {
  148.         Deprecation::trigger(
  149.             'doctrine/orm',
  150.             'https://github.com/doctrine/orm/issues/8460',
  151.             'Calling %s() is deprecated and will not be supported in Doctrine ORM 3.0.',
  152.             __METHOD__
  153.         );
  154.         $this->_em->clear($this->_class->rootEntityName);
  155.     }
  156.     /**
  157.      * Finds an entity by its primary key / identifier.
  158.      *
  159.      * @param mixed    $id          The identifier.
  160.      * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
  161.      *                              or NULL if no specific lock mode should be used
  162.      *                              during the search.
  163.      * @param int|null $lockVersion The lock version.
  164.      *
  165.      * @return object|null The entity instance or NULL if the entity can not be found.
  166.      * @psalm-return ?T
  167.      */
  168.     public function find($id$lockMode null$lockVersion null)
  169.     {
  170.         return $this->_em->find($this->_entityName$id$lockMode$lockVersion);
  171.     }
  172.     /**
  173.      * Finds all entities in the repository.
  174.      *
  175.      * @psalm-return list<T> The entities.
  176.      */
  177.     public function findAll()
  178.     {
  179.         return $this->findBy([]);
  180.     }
  181.     /**
  182.      * Finds entities by a set of criteria.
  183.      *
  184.      * @param int|null $limit
  185.      * @param int|null $offset
  186.      * @psalm-param array<string, mixed> $criteria
  187.      * @psalm-param array<string, string>|null $orderBy
  188.      *
  189.      * @psalm-return list<T> The objects.
  190.      */
  191.     public function findBy(array $criteria, ?array $orderBy null$limit null$offset null)
  192.     {
  193.         $persister $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
  194.         return $persister->loadAll($criteria$orderBy$limit$offset);
  195.     }
  196.     /**
  197.      * Finds a single entity by a set of criteria.
  198.      *
  199.      * @psalm-param array<string, mixed> $criteria
  200.      * @psalm-param array<string, string>|null $orderBy
  201.      *
  202.      * @return object|null The entity instance or NULL if the entity can not be found.
  203.      * @psalm-return ?T
  204.      */
  205.     public function findOneBy(array $criteria, ?array $orderBy null)
  206.     {
  207.         $persister $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
  208.         return $persister->load($criterianullnull, [], null1$orderBy);
  209.     }
  210.     /**
  211.      * Counts entities by a set of criteria.
  212.      *
  213.      * @psalm-param array<string, mixed> $criteria
  214.      *
  215.      * @return int The cardinality of the objects that match the given criteria.
  216.      *
  217.      * @todo Add this method to `ObjectRepository` interface in the next major release
  218.      */
  219.     public function count(array $criteria)
  220.     {
  221.         return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->count($criteria);
  222.     }
  223.     /**
  224.      * Adds support for magic method calls.
  225.      *
  226.      * @param string $method
  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. }