@enigma2030

Class Table Inheritance — Querying Inherited Classes по полям Inherited сущности?

Добрый день. Возможно вопрос покажется банальным. Но никак не получается найти доступную мозгу информацию.

Созданы таблицы https://www.doctrine-project.org/projects/doctrine....

Подскажите где можно найти документацию, а лучше пример как сделать фильтрацию по полям Inherited сущности используя метод createQueryBuilder в репозитории?

https://www.doctrine-project.org/projects/doctrine... документация предлагает использовать метод createQuery.

Точнее как можно обраться к полям дочерней таблицы.

P.S. Если смотреть сгенерированный SQL то там уже есть LEFT JOIN к необходимой таблице.
  • Вопрос задан
  • 84 просмотра
Решения вопроса 1
@enigma2030 Автор вопроса
Спасибо Максим Федоров и Александр за предложенные варианты.

Максим Федоров ваш вариант взял на заметку, на более детальное ознакомление для применения.
Александр решил все же разнести в разные репозитории и подключать необходимый в зависимости от переданного типа.

Возникшая сложность скорее всего из-за использования createQueryBuilder для решение поставленной задачи с использованием Class Table Inheritance.

С createQuery проще, написал SQL как тебе надо и все, но хотелось попробовать по максимуму использовать функционал ORM и метода createQueryBuilder.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Minifets
@Minifets
Hello world!!!
Через INSTANCE OF.
В queryBuilder он есть в выражениях.

$queryBuilder->expr()->isInstanceOf('alias', 'TargetClass')
Ответ написан
Maksclub
@Maksclub
maksfedorov.ru

Решение не заработает, тк поиск будет по полю родительской сущности
$qb->expr()->andX(
    $qb->expr()->andX(
        $qb->expr()->isInstanceOf('sub1',  'TargetClass'),
        $qb->expr()->eq('sub1.sub1Field',  $sub1FieldValue)
    )
);



UPD2 (рекомендация): Не решать такие задачи через ORM, а делать просто SQL.

UPD1: решение без instanceOf().
Можно найти решение по красивее, но вот на коленке
  • Код не идеальный, но работает, как вариант
  • Можно выражения (равенство, <> и прочее тоже вынести на уровень передачи параметра)
  • Или заменить на вида Expr\Andx(), что даст много гибкости
  • Ну и проверок добавить


Сущности:

/**
 * @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;
		}
	}
}


Тест (проходит)
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, [['meow' => 'meow2']],  // тут точка расширения гибкости добавления условий, чтобы все не только eq() было
			Dog::class, [['woof' => 'woof1']],
		]);
		$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);
	}
}

Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы