E
E
enigma20302021-09-24 11:57:13
symfony
enigma2030, 2021-09-24 11:57:13

Class Table Inheritance - Querying Inherited Classes on Inherited Entity Fields?

Good afternoon. The question may seem trivial. But there is no way to find the information accessible to the brain.

Created https://www.doctrine-project.org/projects/doctrine... tables .

Tell me where you can find documentation, but rather an example of how to filter by the fields of an Inherited entity using the createQueryBuilder method in the repository?

https://www.doctrine-project.org/projects/doctrine... documentation suggests using the createQuery method.

More precisely, how can you access the fields of the child table.

PS If you look at the generated SQL, then there is already a LEFT JOIN to the required table.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Alexander, 2021-09-24
@Minifets

Via INSTANCE OF.
In queryBuilder it is in expressions.

$queryBuilder->expr()->isInstanceOf('alias', 'TargetClass')

M
Maxim Fedorov, 2021-09-24
@Maksclub


The solution will not work, because the search will be by the field of the parent entity

$qb->expr()->andX(
    $qb->expr()->andX(
        $qb->expr()->isInstanceOf('sub1',  'TargetClass'),
        $qb->expr()->eq('sub1.sub1Field',  $sub1FieldValue)
    )
);


UPD2 (recommendation): Do not solve such tasks through ORM, but just do SQL .
UPD1: Solution without instanceOf() .
You can find a more beautiful solution, but on your knee
  • The code is not perfect, but it works as an option
  • Expressions (equality, <> and so on can also be moved to the parameter passing level)
  • Or replace it with the form Expr\Andx(), which will give a lot of flexibility
  • Well, add checks

Essences:

/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"cat" = "Cat", "dog" = "Dog"})
 *
 * @ORM\Entity(repositoryClass="AnimalRepository")
 */
class Animal
{
  /**
   * @ORM\Id
   * @ORM\Column(type="integer", nullable=true)
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  public ?string $id = null;
}


/** @ORM\Entity */
class Cat extends Animal
{
  /**
   * @ORM\Column(type="string")
   */
  public $meow;
}


/** @ORM\Entity */
class Dog extends Animal
{
  /**
   * @ORM\Column(type="string")
   */
  public $woof;
}


repository:
class AnimalRepository extends EntityRepository
{
  /**
   * @param array $filters
   *
   * @return \Generator<Animal::class>|Animal[]
   */
  public function filter(array $filters): \Generator
  {
    foreach ($filters as $filter) {
      [$entityClass, $conditions] = $filter;

      if (!is_a($entityClass, Animal::class, true)) {
        throw new \LogicException('Класс должен быть ребенком ' . Animal::class);
      }

      yield  from $this->getSubEntities($entityClass, [$conditions]);
    }
  }

  private function getSubEntities(string $subEntityClassName, array $conditions): \Generator
  {
    $qb = $this->getEntityManager()->createQueryBuilder();
    $qb->select('e')->from($subEntityClassName, 'e');

    foreach ($conditions as $condition) {
      [$param, $paramValue] = $condition;

      $qb
        ->andWhere("e.$param = :$param")
        ->setParameter($param, $paramValue);
    }

    foreach ($qb->getQuery()->getResult() as $entity) {
      yield $entity;
    }
  }
}

Test (passing)
class AnimalRepositoryCest
{
  /** @var EntityManagerInterface */
  private $em;

  public function _before(FunctionalTester $I): void
  {
    $this->em = $I->grabService('doctrine.orm.entity_manager');
  }

  public function testSuccessFilter(): void
  {
    $cat1 = new Cat();
    $cat1->meow = 'meow1';

    $cat2 = new Cat();
    $cat2->meow = 'meow2';

    $dog1 = new Dog();
    $dog1->woof = 'woof1';

    $dog2 = new Dog();
    $dog2->woof = 'woof2';

    $this->em->persist($cat1);
    $this->em->persist($cat2);
    $this->em->persist($dog1);
    $this->em->persist($dog2);
    $this->em->flush();

    /** @var AnimalRepository $animalRepository */
    $animalRepository = $this->em->getRepository(Animal::class);
    assertInstanceOf(AnimalRepository::class, $animalRepository);

    // этот код можно в репозитории сокрыть
    $resultIter = $animalRepository->filter([
      Cat::class, ,  // тут точка расширения гибкости добавления условий, чтобы все не только eq() было
      Dog::class, ,
    ]);
    $result = iterator_to_array($resultIter);
    assertCount(2, $result);

    assertInstanceOf(Cat::class, $result[0]);
    assertSame('meow2', $result[0]->meow);

    assertInstanceOf(Dog::class, $result[1]);
    assertSame('woof1', $result[1]->woof);
  }
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question