Search code examples
symfonydoctrinemappedsuperclass

Symfony 6: create Repository class for MappedSuperclass


I have a MappedSuperclass entity Person, and a number of child entities:

  • Teacher
  • Student
  • Janitor

I want to implement a Repository class for all these entities with a method findByString() like this:

public function findByString($searchStr)
{
    return $this->createQueryBuilder('C')
       ->where('C.name like :val')
       ->orWhere('C.surnames like :val')
       ->orWhere('C.dni like :val')
       ->setParameter('val', "%$searchStr%")
       ->orderBy('C.surnames', 'ASC')
       ->getQuery()
       ->getResult();
}

My first approach was to create a PersonRepository and then extend all the other repositories from this class, but something's wrong with the entity class in the repository.

When calling $teacherRepository->findByString($search_str); I get this error:

Cannot autowire service "App\Repository\PersonRepository": argument "$className" of method "__construct()" is type-hinted "string", you should configure its value explicitly.

<?php
namespace App\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Tools\Pagination\Paginator;

use App\Entity\Person;

/**
 * @method Person|null find($id, $lockMode = null, $lockVersion = null)
 * @method Person|null findOneBy(array $criteria, array $orderBy = null)
 * @method Person[]    findAll()
 * @method Person[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class PersonRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry, string $className)
    {
        parent::__construct($registry, $className);
    }

    public function findByString($searchStr)
    {
        return $this->createQueryBuilder('C')
           ->where('C.name like :val')
           ->orWhere('C.surnames like :val')
           ->orWhere('C.dni like :val')
           ->setParameter('val', "%$searchStr%")
           ->orderBy('C.surnames', 'ASC')
           ->getQuery()
           ->getResult();
    }

And then, TeacherRepository like this:

class TeacherRepository extends PersonRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Teacher::class);
    }
}

If I use the standard __contruct() in PersonRepository with Person::class like this:

public function __construct(ManagerRegistry $registry)
{
    parent::__construct($registry, Person::class);
}

Then I get this error:

An exception occurred while executing a query: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'person' doesn't exist

What is the correct approach in this case? or is it possible to create a Repository class for a MappedSuperClass and then have all child classes inherit its methods?


Solution

  • The problem is that autowire does not know that PersonRepository is just a base class and should not be a service. Autowire gets confused by the string argument.

    So make PersonRepository abstract and autowire will ignore it. You can also remove the constructor as it does not do anything.

    abstract class PersonRepository
    {
        public function findByString($searchStr)
        {
    

    Another approach would be to move the findBy stuff into a trait and eliminate the PersonRepository class completely.