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

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