У меня есть эта модель БД:
Затем я создал эти объекты (я просто оставлю часть отношения, так как другой не относится к теме):
Orders.php
class Orders { /** * @ORM\ManyToOne(targetEntity="Person", inversedBy="orders") * @ORM\JoinColumn(name="person_id", referencedColumnName="id") * */ protected $person; public function setPerson(Person $person) { $this->person = $person; return $this; } public function getPerson() { return $this->person; } }
Person.php
class Person { /** * @ORM\OneToMany(targetEntity="NaturalPerson", mappedBy="person") * */ private $naturals; /** * @ORM\OneToMany(targetEntity="LegalPerson", mappedBy="person") * */ private $legals; /** * @ORM\OneToMany(targetEntity="Orders", mappedBy="person") * */ private $orders; public function __construct() { $this->naturals = new ArrayCollection(); $this->legals = new ArrayCollection(); $this->orders = new ArrayCollection(); } public function getNaturals() { return $this->naturals; } public function getLegals() { return $this->legals; } public function getOrders() { return $this->orders; } }
NaturalPerson.php
class NaturalPerson { /** * @ORM\Id * @ORM\ManyToOne(targetEntity="Person", inversedBy="naturals") * @ORM\JoinColumn(name="person_id", referencedColumnName="id") */ protected $person; /** * @ORM\Column(name="identification_type", type="ci_type", nullable=false) * @DoctrineAssert\Enum(entity="Tanane\FrontendBundle\DBAL\Types\CIType") */ protected $identification_type; /** * @ORM\Column(name="ci", type="integer", nullable=false) */ protected $ci; public function setPerson(Person $person) { $this->person = $person; return $this; } public function getPerson() { return $this->person; } public function setIdentificationType($identification_type) { $this->identification_type = $identification_type; return $this; } public function getIdentificationType() { return $this->identification_type; } public function setCI($ci) { $this->ci = $ci; return $this; } public function getCI() { return $this->ci; } }
Я опустил LegalPerson
так как это почти то же самое, что и NaturalPerson
вот и проблема. Отображение выглядит хорошо, но как я получаю связанные записи из Orders
?
Идея этого заключается в каждом Orders
мне нужно знать, к которому принадлежит Person
(Заказы), а также дополнительной информации, хранящейся в NaturalPerson
или LegalPerson
зависимости от типа person.type
.
Смотрите этот код:
public function getOrdersAction() { $response = array(); $em = $this->getDoctrine()->getManager(); $entities = $em->getRepository("FrontendBundle:Orders")->findAll(); if (!$entities) { $response['message'] = "No se encontraron resultados"; } $orders = array(); foreach ($entities as $entity) { $personType = $entity->getPerson()->getPersonType(); $order = array(); $order[] = $entity->getNickname(); // Here I'm trying to access to `Naturals` methods from `Orders` if ($personType == 1) { $order[] = $entity->getPerson()->getNaturals()[0]->getIdentificationType() . $entity->getPerson()->getNaturals()[0]->getCI(); } elseif ($personType == 2) { $order[] = $entity->getPerson()->getLegals()[0]->getIdentificationType() . $entity->getPerson()->getLegals()[0]->getRIF(); } $orders[] = $order; } $response['data'] = $orders; return new JsonResponse($response); }
Но я получаю эту ошибку:
Ошибка: вызов функции-члена getIdentificationType () для не-объекта в строке /var/www/html/tanane/src/Tanane/BackendBundle/Controller/OrderController.php 115
Возможно, мое сопоставление неверно, так как я должен иметь OneToOne
между Person
и NaturalPerson
(и это звучит неправильно для моей логики, как показывает DER), или, может быть, нет, но тогда я не знаю, как получить связанные свойства только для одной записи, я прочитал здесь, а также здесь, но они не говорили об этой части, или я не вижу ее, никаких советов? идеи? чаевые?
Попытка использовать репозитории и DQL для решения проблемы
Я создаю функцию в классе Repository
для извлечения данных и не усложняюсь, поскольку, по-видимому, моя проблема, поэтому я сделал это:
public function getOrders($person_type = 1) { $qb = $this->getEntityManager()->createQueryBuilder(); $qb ->select('ord.*, ps.*') ->from("FrontendBundle:Orders", "ord") ->join('FrontendBUndle:Person', 'ps', 'WITH', 'ps.id = ord.person_id') ->orderBy('ord.created', 'DESC'); if ($person_type == 1) { $qb ->select('np.*') ->join('FrontendBundle:NaturalPerson', 'np', 'WITH', 'ps.id = np.person'); // Join NaturalPerson table } elseif ($person_type == 2) { $qb ->select('lp.*') ->join('FrontendBundle:LegalPerson', 'lp', 'WITH', 'ps.id = lp.person'); // Join NaturalPerson table } return $qb->getQuery()->getResult(); }
Я еще не тестировался, поэтому, возможно, это не сработает, но если идея состоит в том, чтобы получить дополнительную информацию для обеих таблиц, то с помощью этого DQL я сделал, как я $person_type
который находится внутри таблицы Person
? Это становится немного сложным, по крайней мере для меня
Запуск необработанного запроса, чтобы увидеть, являются ли столбцы NULL
Я создаю этот простой запрос только для теста, если результаты NULL
:
SELECT ord.id, ord.person_id as ord_person_id, ord.nickname, ps.id, ps.description, np.person_id as natural_person_id, np.identification_type, np.ci FROM orders ord LEFT JOIN person ps ON ord.person_id = ps.id LEFT JOIN natural_person np ON np.person_id = ps.id WHERE ps.person_type = 1;
И этот запрос возвращает:
Таким образом, здесь нет столбцов NULL
CRUD для создания новых заказов
// Set Person entity $entityPerson = new Person(); $person_type === 1 ? $entityPerson->setDescription($orders['nat']['person']['description']) : $entityPerson->setDescription($orders['leg']['person']['description']); $person_type === 1 ? $entityPerson->setContactPerson($orders['nat']['person']['contact_person']) : $entityPerson->setContactPerson($orders['leg']['person']['contact_person']); $entityPerson->setPersonType($person_type); $em->persist($entityPerson); $em->flush(); ... if ($person_type === 1) { // Set NaturalPerson entity $entityNatural = new NaturalPerson(); $entityNatural->setIdentificationType($orders['nat']['identification_type']); $entityNatural->setCI($orders['nat']['ci']); $em->persist($entityNatural); $em->flush(); } elseif ($person_type === 2) { // Set LegalPerson entity $entityLegal = new LegalPerson(); $entityLegal->setIdentificationType($orders['leg']['identification_type']); $entityLegal->setRIF($orders['leg']['rif']); $em->persist($entityLegal); $em->flush(); }
Поскольку LegalPerson
и NaturalPerson
являются специализациями Person
я бы рекомендовал использовать то, что Doctrine вызывает Class Table Inheritance ( документация ).
Вам придется:
Person.php
/** * @ORM\Table(name="person") * @ORM\Entity * @ORM\InheritanceType("JOINED") * @ORM\DiscriminatorColumn(name="discr", type="string") * @ORM\DiscriminatorMap({ * "natural" = "NaturalPerson", * "legal" = "LegalPerson", * }) */ class Person { /** * @ORM\OneToMany(targetEntity="Orders", mappedBy="person") * */ private $orders; public function __construct() { $this->orders = new ArrayCollection(); } public function getOrders() { return $this->orders; } }
NaturalPerson.php
/** * @ORM\Table(name="natural_person") * @ORM\Entity */ class NaturalPerson extends Person { /** * @ORM\Column(name="identification_type", type="ci_type", nullable=false) * @DoctrineAssert\Enum(entity="Tanane\FrontendBundle\DBAL\Types\CIType") */ protected $identification_type; /** * @ORM\Column(name="ci", type="integer", nullable=false) */ protected $ci; public function setIdentificationType($identification_type) { $this->identification_type = $identification_type; return $this; } public function getIdentificationType() { return $this->identification_type; } public function setCI($ci) { $this->ci = $ci; return $this; } public function getCI() { return $this->ci; } }
Order.php
остается неизменным.
Как вы можете видеть, теперь и NaturalPerson
и LegalPerson
расширяют Person
. Поскольку вы изменили определение сущностей, вам придется обновить схему базы данных.
Теперь в вашем Controller
вам нужно только сделать это:
foreach ($entities as $entity) { $person = $entity->getPerson(); $order = array(); $order[] = $entity->getNickname(); if ($person instanceof NaturalPerson) { $order[] = $person->getIdentificationType() . $person->getCI(); } else // it has to be LegalPerson { $order[] = $person->getIdentificationType() . $person->getRIF(); } $orders[] = $order; }
Не забудьте добавить инструкцию use
для NaturalPerson
!
Таким образом, вы работаете только с экземплярами NaturalPerson
или LegalPerson
. Я уверен, что вы можете еще больше улучшить это.
Наконец, вам придется изменить свой CRUD для этого. Вы больше не работаете с Person
(фактически, он должен быть abstract
), поэтому теперь вам нужно обрабатывать CRUD для NaturalPerson
и для LegalPerson
отдельно. Каждый будет иметь свой Type
, Controller
, представления и т. Д.
Теперь ваш код будет выглядеть так:
if ($person_type === 1) { $entityPerson = new NaturalPerson(); $entityPerson->setDescription($orders['nat']['person']['description']); $entityPerson->setContactPerson($orders['nat']['person']['contact_person']); $entityPerson->setIdentificationType($orders['nat']['identification_type']); $entityPerson->setCI($orders['nat']['ci']); $em->persist($entityPerson); $em->flush(); } elseif ($person_type === 2) { $entityPerson = new LegalPerson(); $entityPerson->setDescription($orders['leg']['person']['description']); $entityPerson->setContactPerson($orders['leg']['person']['contact_person']); $entityPerson->setIdentificationType($orders['leg']['identification_type']); $entityPerson->setRIF($orders['leg']['rif']); $em->persist($entityPerson); $em->flush(); }
Возможно, проблема в другом. Вы можете забыть назначить LegalPerson
NaturalPerson
или LegalPerson
для Person
. Поэтому вам нужно проверить его до вызова getIdentificationType()
:
if($personType == 1){ if(null !== $natural = $entity->getPerson()->getNaturals()[0]){ $order[] = $natural->getIdentificationType() . $natural->getCI(); } }elseif($personType == 2){ if(null !== $legal = $entity->getPerson()->getLegals()[0]){ $order[] = $legal->getIdentificationType() . $legal->getRIF(); } }