Чтобы избежать определения отдельных полей для TestChildEntityA и TestChildEntityB, я определил baseEntities в TestOwnerEntity в качестве их родителя, который является абстрактным классом.Я знаю, что абстрактные классы не могут быть созданы ни в PHP 7, ни в каком-либо другом языке программирования, но это не создание экземпляра TestBaseEntity.Сначала я получил сообщение об ошибке ниже, когда я вызвал getBaseEntities из TestOwnerEntity (getActionAB в контроллере), но позже понял, что addBaseEntities и removeBaseEntities могут вызвать ту же проблему.getBaseEntities работает нормально, если я заменю "@var TestBaseEntity []" в TestOwnerEntity на "@var TestChildEntityA []" или @var TestChildEntityB [] ", но это будет означать, что мне нужно определить два поля вместо одного, а такжеметод get для обоих методов - это то, чего я хочу избежать.
Ошибка
Примечание: неопределенный индекс: owner
$this->switchPersisterContext($offset, $limit);
$criteria = [];
$parameters = [];
$owningAssoc = $this->class->associationMappings[$assoc['mappedBy']]; // Highlighted
$sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']);
$tableAlias = $this->getSQLTableAlias($owningAssoc['inherited'] ?? $this-
>class->name);
Это классы, которые есть в моем приложении:
Базовый объект
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({"testA" = "TestChildEntityA", "testB" = "TestChildEntityB"})
* @ORM\Entity(repositoryClass="App\Repository\TestBaseEntityRepository")
*/
abstract class TestBaseEntity
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
abstract public function getOwner(): TestOwnerEntity;
abstract public function setOwner(TestOwnerEntity $owner): void;
}
Дочерний объект A
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\TestChildEntityARepository")
*/
class TestChildEntityA extends TestBaseEntity
{
/**
* @var TestOwnerEntity
* @ORM\ManyToOne(targetEntity="TestOwnerEntity", inversedBy="baseEntities")
* @ORM\JoinColumn(nullable=true)
*/
private $owner;
/**
* @return TestOwnerEntity
*/
public function getOwner(): TestOwnerEntity
{
return $this->owner;
}
/**
* @param TestOwnerEntity $owner
*/
public function setOwner(TestOwnerEntity $owner): void
{
$this->owner = $owner;
}
}
Дочерний объект B
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\TestChildEntityBRepository")
*/
class TestChildEntityB extends TestBaseEntity
{
/**
* @var TestOwnerEntity
* @ORM\ManyToOne(targetEntity="TestOwnerEntity", inversedBy="baseEntities")
* @ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* @return TestOwnerEntity
*/
public function getOwner(): TestOwnerEntity
{
return $this->owner;
}
/**
* @param TestOwnerEntity $owner
*/
public function setOwner(TestOwnerEntity $owner): void
{
$this->owner = $owner;
}
}
Владелец объекта
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\TestOwnerEntityRepository")
*/
class TestOwnerEntity
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
public function __construct()
{
$this->baseEntities = new ArrayCollection();
}
/**
* @var TestBaseEntity[]|ArrayCollection
*
* @ORM\OneToMany(
* targetEntity="TestBaseEntity",
* mappedBy="owner",
* orphanRemoval=true,
* cascade={"persist"}
* )
*/
private $baseEntities;
public function getBaseEntities()
{
return $this->baseEntities;
}
public function addBaseEntities(TestBaseEntity $baseEntity): void
{
$baseEntity->setOwner($this);
if (!$this->baseEntities->contains($baseEntity)) {
$this->baseEntities->add($baseEntity);
}
}
public function removeBaseEntities(TestBaseEntity $baseEntity): void
{
$this->baseEntities->removeElement($baseEntity);
}
}
Контроллер
<?php
/**
* Created by PhpStorm.
* User: User
* Date: 05/02/2019
* Time: 21:00
*/
namespace App\Controller;
use App\Entity\TestBaseEntity;
use App\Entity\TestChildEntityA;
use App\Entity\TestChildEntityB;
use App\Entity\TestOwnerEntity;
use App\Repository\TestBaseEntityRepository;
use App\Repository\TestChildEntityARepository;
use App\Repository\TestChildEntityBRepository;
use App\Repository\TestOwnerEntityRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class TestController extends AbstractController
{
/**
* @Route("/")
*/
public function indexAction()
{
$owner = new TestOwnerEntity();
$objA = new TestChildEntityA();
$objB = new TestChildEntityB();
$objA->setOwner($owner);
$objB->setOwner($owner);
// Calling addBaseEntities is successful when $owner has been newly instantiated
$owner->addBaseEntities($objA);
$owner->addBaseEntities($objB);
$em = $this->getDoctrine()->getManager();
$em->persist($owner);
$em->persist($objA);
$em->persist($objB);
$em->flush();
return new Response("done");
}
/**
* @Route("/getAB")
*/
public function getActionAB(TestChildEntityBRepository $repo, TestOwnerEntityRepository $ownerrepo)
{
$owner = $ownerrepo->findAll()[0];
// getBaseEntities fails because baseEntities is defined using an abstract class in TestOwnerEntity
$b = $owner->getBaseEntities()[0];
return new Response($b->getId());
}
/**
* @Route("/addA")
*/
public function addActionA(TestOwnerEntityRepository $ownerrepo)
{
$a = new TestChildEntityA();
$owner = $ownerrepo->findAll()[0];
$a->setOwner($owner);
// $owner->addBaseEntities($objA); addBaseEntities fails when $owner is retreived from the database
// $a->setOwner($owner); setOwner is sufficient to add $a to the database but it's best practice to also
// append to the entity array in TestOwnerEntity. Removing the check "$this->baseEntities->contains($baseEntity)"
// in addBaseEntities stops the error.
$em = $this->getDoctrine()->getManager();
$em->persist($owner);
$em->persist($a);
$em->flush();
return new Response($a->getOwner()->getId());
}
/**
* @Route("/delA")
*/
public function delActionA(TestOwnerEntityRepository $ownerrepo, TestChildEntityARepository $arepo)
{
$owner = $ownerrepo->findAll()[0];
// removeBaseEntities also fails
$owner->removeBaseEntities($arepo->findAll()[0]);
$em = $this->getDoctrine()->getManager();
$em->flush();
return new Response("done");
}
}
Если это невозможно, как я это сделал, предложите лучшую альтернативу. TestBaseEntity представляет класс для уведомлений, которые пользователь получит в приложении, которое яЯ работаю над тем, чтобы дочерние A и B были соответственно уведомлениями администратора и уведомлениями форума, которые я хочу включить в один и тот же список, потому что не имеет смысла, что они живут в разных почтовых ящиках.
Редактировать:Я пытался использовать приведение, но это не поддерживается в PHP.
MySQL Table Schema