-1

Lets say we have two Repository classes:

class CarrierRepository extends ServiceEntityRepository
{
    const ENTITY = 'carrier';

    /** @var PaginatorInterface $paginator */
    private $paginator;

    public function __construct(ManagerRegistry $registry, PaginatorInterface $paginator)
    {
        $this->paginator = $paginator;
        parent::__construct($registry, Carrier::class);
    }
.
.
.

class LocationRepository extends ServiceEntityRepository
{
    const ENTITY = 'location';

    /** @var PaginatorInterface $paginator */
    private $paginator;

    public function __construct(ManagerRegistry $registry, PaginatorInterface $paginator)
    {
        $this->paginator = $paginator;
        parent::__construct($registry, Location::class);
    }
.
.
.

And in both of these repository classes we have the same logic like this:

    public function search(array $searchParams)
    {
        $parameters = [];
        $qb = $this->createQueryBuilder(self::ENTITY);

        foreach ($searchParams['data'] as $key => $searchParam) {

            if (!empty($searchParam['param'])) {
                $entity = self::ENTITY;
                $operator = 'LIKE';
                $column = $searchParam['key'];
                $parameters[$searchParam['key']] = '%'.$searchParam['param'].'%';
.
.
.

As you can see, both Repository classes have the same dependencies and the same logic - except the entity class - in one class it is "carrier" and in the other it is "location". I think it will be a good idea to merge this into a BaseRepository class like this:

class BaseRepository extends ServiceEntityRepository
{
    private $entity;

    /** @var PaginatorInterface $paginator */
    private $paginator;

    public function __construct($entity, ManagerRegistry $registry, PaginatorInterface $paginator)
    {
        $this->entity = $entity;
        $this->paginator = $paginator;
        parent::__construct($registry, $this->entity);
    }

    public function search(array $searchParams)
    {
        $parameters = [];
        $qb = $this->createQueryBuilder(self::ENTITY);

        foreach ($searchParams['data'] as $key => $searchParam) {

            if (!empty($searchParam['param'])) {
                $entity = self::ENTITY;
                $operator = 'LIKE';
                $column = $searchParam['key'];
                $parameters[$searchParam['key']] = '%'.$searchParam['param'].'%';
.
.
.

and to extend my other both repositories instead of

ServiceEntityRepository

with my BaseRepository like this (example for the CarrierRepository):

class CarrierRepository extends BaseRepository
{
    const ENTITY = 'carrier';

    /** @var PaginatorInterface $paginator */
    private $paginator;

    public function __construct(ManagerRegistry $registry, PaginatorInterface $paginator)
    {
        $this->paginator = $paginator;
        parent::__construct(Carrier::class, $registry, $paginator);
    }
.
.
.

This does not work - it will give me this error:

Cannot autowire service "App\Repository\BaseRepository": argument "$entity" of method "__construct()" has no type-hint, you should configure its value explicitly.

Question: How can I implement a BaseRepository for all my EntityRepositories that uses the same properties and logic, so I do not have to implement the whole logic in all my Repository classes again and again? I want to have a Base Repository in which is the whole logic and all of my Repository classes can use the same code.

goldlife
  • 1,949
  • 3
  • 29
  • 48
  • Fooling around with Doctrine repositories can be very tricky. There is no way for autowire to know which entity class name should be injected. So you need to manually define the repositories as services in which case you extend from EntityRepository instead of ServiceEntityRepository and make a factory service. Consider moving your search functionality into a trait and avoid a major headache. – Cerad Nov 26 '20 at 21:53

1 Answers1

3

Here is how I made BaseRepository:

abstract class BaseRepository extends ServiceEntityRepository
{
    protected string $entityClass;

    public function __construct(ManagerRegistry $registry, string $entityClass)
    {
        $this->entityClass = $entityClass;

        parent::__construct($registry, $entityClass);
    }
}

class CompanyUserRepository extends BaseRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, CompanyUser::class);
    }
}

The point is to make BaseRepository abstract, to disable "autowire" of this class.

Urmat Zhenaliev
  • 1,497
  • 8
  • 22