Невозможно вернуть подкласс, если абстрактный родительский тип является возвращаемым - PullRequest
0 голосов
/ 24 февраля 2019

Чтобы избежать определения отдельных полей для 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

...