Простое doctrine наследование с помощью MappedSuperclass
October 5, 2018
ru
This is an archived post from thewebland.net. Content may be outdated.
Стала тут передо мной задача, расширить некий функционал.
Приведенное наследование, может использоваться и в друхих ситуациях. Я же, просто описываю свой конкретный кейс.
Все есть “псевдо”-код
Дано: REST сервис который обрабатывает входящие данные. т.е: POST запрос с огомным payload.
Обработка: Реализован патерн Chain Of Responsibility в котором, собственно и происходит вся обработка.
Из входящего payload, цепь строит некую структуру связанных между собой объектов, обрабатывает их и пишет в базу.
Задача: Добавить дополнительные данные внутри цепи к вложенному объекту и отправить в другой сервис (не записывая его в базу - (почему? - это уже совсем другая история)).
Пример нискольких елементов цепи
1
<?php
2
3
namespase ApiBundle\Service\Processor;
4
5
use AppBundle\Entity\Data;
6
7
class DataProcessor extends AbstractProcessor {
8
9
public function process($requestData, $chainData)
10
{
11
$data = new Data();
12
13
...
14
15
$this->em->persist();
16
17
return $chainData;
18
}
19
}
1
<?php
2
3
namespase ApiBundle\Service\Processor;
4
5
use AppBundle\Entity\Data;
6
7
class DummyRelation extends AbstractProcessor {
8
9
public function process($requestData, $chainData)
10
{
11
$data = new DummyRelation();
12
13
...
14
15
$this->em->persist();
16
17
return $chainData;
18
}
19
}
В AbstractProcessor, также лежит логика, в конечном итоге, вызывая $this->em->flush();
Почему бы не написать обработку после выполнения цепи? - Основная причина это сложная структура данных, и если использовать, например, пост процессор, алгоритм обработки усложняеться на 2_n_ раз. -
2_n_ - ничем не обоснованное видение сложности автора. :) А если, в случае с пост процессором, всю структуру (дерево) нужно обойти несколько раз.
Был выбран более эфективный, по мнению автора, алгоритм, который обрабатывает нужные данные, в нужном месте цепи, без обхода всех объектов (дерева).
Цель:
Расширить класс сущности без создания таблицы в базе данных.
Не захламлять класс entity полями которые не относятся к таблице.
Использовать наследника для записи в базу, что бы не использовать всякого рода датамаперы.
Т.e. (моя структура (псевдо))
1
src/
2
- ApiBundle
3
-- Model
4
--- DataResponse.php
5
--- DataResponse
6
---- SomeDummyRelationResponse.php
7
---- DummyRelationResponse.php
8
-- Service
9
--- DataProcessor.php
10
--- AbstractDataProcessor.php
11
--- DummyRelationProcessor.php
12
- AppBundle
13
-- Entity
14
--- Data.php
15
--- Data
16
---- SomeDummyRelation.php
17
---- DummyRelationResponse.php
Давайте посмотрим на пример кода (на самом деле псевдокода) сущности:
В данном случае нам не важно что лежит в классах связи, их описывать я не буду (хотя может и стоило, напишите в комментариях).
Первоначально я просто отнаследовал модель от ентити, но из этого ничего не вышло и начались валится ероры.
1
[Doctrine\ORM\Mapping\MappingException]
2
Class "Bla\Bla" sub class of "Blah\Blah" is not a valid entity or mapped super class.
Doctrine говорит нам, что есть класс, и он расширяет сущность. Сделайте что-нибудь, чтобы решить эту проблему, потому что я не знаю, как использовать этот класс как сущность или что-то другое? Для решения этой проблемы есть некоторое решение.
Для того что бы расширить класс сущности и не создавать новую таблицу в своей базе, я буду использовать MappedSuperClass. Посмотрим на примере:
class SomyDummyRelationResponse extends SomeDummyRelation {
11
12
...
13
}
1
<?php
2
3
namespace ApiBundle\Model\Data;
4
5
use AppBundle\Entity\Data\DummyRelation;
6
7
/**
8
*@ORM\MappedSuperclass
9
*/
10
class DummyRelationResponse extends DummyRelation {
11
12
...
13
}
Обновил свои процессоры:
1
<?php
2
3
namespase ApiBundle\Service\Processor;
4
5
use ApiBundle\Model\DataResponse;
6
7
class DataProcessor extends AbstractProcessor {
8
9
public function process($requestData, $chainData)
10
{
11
$data = new DataResponse();
12
13
...
14
15
$this->em->persist();
16
17
return $chainData;
18
}
19
}
1
<?php
2
3
namespase ApiBundle\Service\Processor;
4
5
use ApiBundle\Model\Data\DummyRelationResponse;
6
7
class DummyRelation extends AbstractProcessor {
8
9
public function process($requestData, $chainData)
10
{
11
$data = new DummyRelationResponse();
12
13
...
14
15
$this->em->persist();
16
17
return $chainData;
18
}
19
}
Думал, заживем …
Появилась новая проблема. Я не могу записать в базу - что эсть плохо. А как мы помним в цепи вызываеться this−>em−>persist()ивконцеthis->em->flush().
MappedSuperclass - не может быть сущностью, он не зависит от запросов и постоянных отношений, определяемых MappedSuperclass, должены быть однонаправленными. Это означает, что ассоциации «один-ко-многим» вообще невозможны для MappedSuperclass. Кроме того, ассоциации Many-to-Many возможны только в том случае, если MappedSuperclass используется только в одном объекте на данный момент. Для дальнейшей поддержки наследования необходимо использовать одиночные или объединенные функции наследования таблиц.
С помощью ResolveTargetEntityListener вы можете разделить свои пакеты, сохраняя их структуру неизменной, но все же имея возможность определять отношения между различными объектами. Используя этот метод, ваши пакеты станет легче поддерживать.