当抽象父级为返回类型

时间:2019-02-24 14:50:37

标签: symfony doctrine-orm php-7 discriminator

为避免为TestChildEntityA和TestChildEntityB定义单独的字段,我在TestOwnerEntity中将baseEntities定义为其父类,这是一个抽象类。我知道既不能在PHP 7中也不能在任何其他编程语言中实例化抽象类,但这不是实例化TestBaseEntity。当我从TestOwnerEntity(控制器中的getActionAB)调用getBaseEntities时,最初收到以下错误,但后来意识到addBaseEntities和removeBaseEntities可能导致相同的问题。如果我将TestOwnerEntity中的“ @var TestBaseEntity []”替换为“ @var TestChildEntityA []”或@var TestChildEntityB [],则getBaseEntities可以正常工作,但这意味着我必须定义两个字段,而不仅仅是一个字段,并且一种我都想避免的get方法。

错误

注意:未定义索引:所有者

$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

0 个答案:

没有答案