从一个实体到来自差异表的许多实体的symfony关系

时间:2019-06-13 11:14:58

标签: database symfony doctrine-orm symfony4 relation

我有一个带有 Comment 实体的symfony应用,我想将其与一个Relation属性链接到许多实体。但是,对于许多实体,我不是指许多对象,而是指许多类

在我的应用中,注释可能涉及不同的事情:一个绘图实体,一个文本实体,一个复合 strong>实体或照片实体。每个实体对应一种艺术品,每个艺术品具有不同的属性和不同的页面。并且每个人都可以评分和评论

问题是:当我想在Comment实体中创建 relation属性时,我必须指出仅一个实体。我想要一些关于图形实体的评论,一些关于文本的评论。

我看到了一个解决方案:按实体创建一个实体注释作为链接,但我的应用程序将有很多实体,并且我的代码是如此重复,这对将来的更改等不利。

是否可以将一个实体链接到许多不同类型的实体?

谢谢。

编辑4:下面的所有以前的编辑都是不可能的!

我允许这样做,以便初学者可以看到我的一些代码,以详细说明一个继承映射示例(我尝试时可以拥有它,但我并不了解所有内容),但是我才意识到我犯了一个愚蠢的错误:

 * @DiscriminatorMap({"comment" = "Comment", "comment" = "CommentDrawing", "commentphoto" = "CommentPhoto", "commenttexte" = "CommentTexte"})
  • 我忘记了“ commentcompo” =“ CommentCompo”
  • 我写了“ comment” =“ Comment”和“ comment” =“ CommentDrawing”(均由“ comment”引用)

这就是为什么我不得不将“注释”作为区分词而不是“注释绘图”。

对于那个愚蠢的错误很抱歉,太多的代码行我没注意到。

对于那些也第一次使用继承映射并需要信息的人,我现在尝试用表格填充评论。如果您想做同样的事情(如果您来这里寻求答案,您可能会这样做),我认为这是解决方案

https://symfony.com/doc/current/form/inherit_data_option.html

编辑

由于下面的Jakumi的答案,我使用了继承映射。我选择了第一个解决方案:一个表解决方案。但是我试过了,我没有没有错误消息,但这对我没用,而且下班后我仍然不明白我做错了什么。

我的评论类(我的评论层次结构中的最高,其中CommentDrawing,CommentCompo,CommentPhoto,CommentTexte作为子级)(评论是抽象的,因为我告诉我一个错误,该错误指示我将其抽象化以解决该错误):

<?php

namespace App\Entity;

use App\Entity\Comment;
use App\Entity\CommentPhoto;
use App\Entity\CommentTexte;
use App\Entity\CommentDrawing;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\InheritanceType;
use Doctrine\ORM\Mapping\DiscriminatorMap;
use Doctrine\ORM\Mapping\DiscriminatorColumn;

/**
 * @ORM\Entity(repositoryClass="App\Repository\CommentRepository")
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="discriminator", type="string")
 * @DiscriminatorMap({"comment" = "Comment", "comment" = "CommentDrawing", "commentphoto" = "CommentPhoto", "commenttexte" = "CommentTexte"})
 */
abstract class Comment
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    /**
     * @ORM\Column(type="text")
     */
    private $content;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
     * @ORM\JoinColumn(nullable=false)
     */
    private $author;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCreatedAt(): ?\DateTimeInterface
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeInterface $createdAt): self
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getContent(): ?string
    {
        return $this->content;
    }

    public function setContent(string $content): self
    {
        $this->content = $content;

        return $this;
    }

    public function getAuthor(): ?User
    {
        return $this->author;
    }

    public function setAuthor(?User $author): self
    {
        $this->author = $author;

        return $this;
    }
}

这是我的 CommentDrawing实体(我创建了其他CommentSomething类,但现在我尝试使用的唯一一个是CommentDrawing):

<?php

namespace App\Entity;

use App\Entity\Comment;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\CommentDrawingRepository")
 */
class CommentDrawing extends Comment
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Dessin", inversedBy="commentsDrawing")
     */
    private $drawing;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getDrawing(): ?Dessin
    {
        return $this->drawing;
    }

    public function setDrawing(?Dessin $drawing): self
    {
        $this->drawing = $drawing;

        return $this;
    }
}

这是所指的 Dessin实体(dessin是用法语绘制的,创建时忘记了用英语名称命名),这是不是Comment的子类,这是CommentDrawing的主题(由ManyToOne链接),它本身就是Comment的子类:

<?php

namespace App\Entity;

use Cocur\Slugify\Slugify;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Entity(repositoryClass="App\Repository\DessinRepository")
 * @ORM\HasLifecycleCallbacks
 * @UniqueEntity(
 *      fields = {"nom"},
 *      message = "Un autre dessin contient le même nom. Merci de le changer. Vérifiez aussi que vous avez entré un slug unique, ou vide. Si le slug est en double, ça engendrera un bug.")
 * @UniqueEntity(
 *      fields = {"url"},
 *      message = "Un autre dessin contient la même url, vous vous apprêtez à poster deux fois le même dessin. Merci de changer l'url.")
 * )
 */
class Dessin
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    // Some code I hidden because it's useless to show

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $slug;

    // Some code I hidden because it's useless to show

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\CommentDrawing", mappedBy="drawing")
     */
    private $commentsDrawing;

    public function __construct()
    {
        // Some code I hidden
        $this->commentsDrawing = new ArrayCollection();
    }

    // Some code I hidden

    public function getId(): ?int
    {
        return $this->id;
    }

    // Some code I hidden

    public function getSlug(): ?string
    {
        return $this->slug;
    }

    public function setSlug(string $slug): self
    {
        $this->slug = $slug;

        return $this;
    }

    // Some code I hidden

    /**
     * @return Collection|CommentDrawing[]
     */
    public function getCommentsDrawing(): Collection
    {
        return $this->commentsDrawing;
    }

    public function addCommentsDrawing(CommentDrawing $commentsDrawing): self
    {
        if (!$this->commentsDrawing->contains($commentsDrawing)) {
            $this->commentsDrawing[] = $commentsDrawing;
            $commentsDrawing->setDrawing($this);
        }

        return $this;
    }

    public function removeCommentsDrawing(CommentDrawing $commentsDrawing): self
    {
        if ($this->commentsDrawing->contains($commentsDrawing)) {
            $this->commentsDrawing->removeElement($commentsDrawing);
            // set the owning side to null (unless already changed)
            if ($commentsDrawing->getDrawing() === $this) {
                $commentsDrawing->setDrawing(null);
            }
        }

        return $this;
    }
}

为验证此方法是否正常运行,我使用phpmyadmin在数据库中手动创建了一个注释:

comment in phpmyadmin

然后,我尝试使用与我的评论主题相对应的图形var在页面中在上方显示图形注释的内容。没啥事儿。所以我直接尝试将其转储到Controller中,无法获得评论

要获取评论,我使用了Drawing实体的$ commentsDrawing属性(您可以在上面的代码中看到它)。这是我用来转储应包含注释的绘图var的代码(我将dump()函数放到show函数中,即以URL中建立的绘图段为参数调用。我确定show ()函数可以正常工作并显示良好的绘图,因为我之前对其进行了测试)。它是 DrawingController

<?php

namespace App\Controller;

use App\Entity\Dessin;
use App\Form\DrawingType;
use App\Entity\CategorieDessin;
use App\Service\PaginationService;
use App\Repository\DessinRepository;
use App\Repository\CategorieDessinRepository;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class DrawingsController extends AbstractController
{
    // I hide the rest of the code because it's useless

    /**
     * show a drawing
     * 
     * @Route("/dessin/{slug}", name="drawing_show")
     *
     * @return Response
     */
    public function show(Dessin $drawing)
    {
        dump($drawing);
        die();

        return $this->render('drawings/show.html.twig', [
            'drawing' => $drawing
        ]);
    }
}

dump()显示的内容:

result of dump on drawing

如您所见,在注释图>集合中,没有元素

如果我在没有dump()的情况下进行了同样的操作,则会得到图形,没有错误,但也没有注释。

我真的看不到我做错了什么...有人可以帮忙吗?

编辑2

当我这样做时:

/**
 * show a drawing
 * 
 * @Route("/dessin/{slug}", name="drawing_show")
 *
 * @return Response
 */
public function show(Dessin $drawing, CommentDrawingRepository $repo)
{
    $comments = $repo->findAll();

    dump($comments);
    die();

    return $this->render('drawings/show.html.twig', [
        'drawing' => $drawing
    ]);
}

我得到一个空数组

编辑3

好吧,我直接从我的DrawingController中添加了一个新的CommentDrawing:

/**
 * show a drawing
 * 
 * @Route("/dessin/{slug}", name="drawing_show")
 *
 * @return Response
 */
public function show(Dessin $drawing, CommentDrawingRepository $repo, DessinRepository $repoDrawing, UserRepository $repoUser, ObjectManager $manager)
{
    $drawing = $repoDrawing->findAll()[0];
    $user = $repoUser->findAll()[0];

    $comment = new CommentDrawing();
    $comment->setCreatedAt(new \DateTime())
            ->setContent("un test")
            ->setAuthor($user)
            ->setDrawing($drawing);

    $manager->persist($comment);
    $manager->flush();

    $comments = $repo->findAll();

    dump($comments);

    return $this->render('drawings/show.html.twig', [
        'drawing' => $drawing
    ]);
}

有效。该评论已在数据库中注册,转储显示一条评论,并且该评论出现在我的页面中。

我试图理解为什么用phpmyadmin添加的注释不起作用,以及令人惊讶的是:这两种注释之间的区别是,用phpmyadmin添加的注释具有 commentdrawing 作为标识符值,并且教义中添加的那个只是作为值的注释 ......这是抽象类的价值!我认为判别器的值对告诉教义要考虑的列很有用,我已经不明白了……但是,问题已解决。谢谢您的帮助!

phpmyadmin

注意:第二个是有效的...为什么?

2 个答案:

答案 0 :(得分:2)

关联本身很可能是进行此抽象的错误位置。无论如何,您可能正在寻找的是继承映射。

供参考:

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/inheritance-mapping.html

一种表解决方案:

一个选择是做单表继承。父类获得子类重写的方法“ getSubject()”,并保存用户ID +等级+注释文本。子类是DrawingComment等,它们各自与注释的主题具有一对一的关联。

优点:简单明了的语义,可以根据需要通过类名(或额外的功能/属性)进行区分,引用完整性稳定,可以轻松进行统计,无论注释主题类型如何均可搜索

缺点:鉴别器列使用起来有些笨拙,并且会稍微降低速度。

许多表格解决方案:

保留继承映射,为通用代码创建抽象类(+接口?)或特征(+ interface)。将每种注释类型存储在其自己的类/实体/表中。当您不在乎评论的类型时,仍然可以使用界面函数来对其进行整洁的处理。

优点:与以前相同-除了可搜索性之外,没有区分符列,因为每种注释类型都有自己的表

缺点:可搜索性略有降低

最终,这是口味或习惯的问题。并且它取决于不同注释类型的公共字段和不同字段。如果(除了主题之外)它们都共享相同的字段,那么我将进行单表继承。它们之间的差异越大,我越倾向于使用多表解决方案。

答案 1 :(得分:2)

没有什么可以说您在一个实体中不能有多个ManyToOne关系。因此,它可能不像Jakumi所建议的那样继承继承映射那么优雅和简洁,但是可以轻松工作。我已经在一个项目中按照您使用单个Comment实体(同时引用多个其他实体)所描述的内容进行了处理。对于我的Comment类,我有其他实体(AdmissionReferral),每个实体都指向多个Comment,但分别指向AdmissionReferral否则没有其他相似之处,因此让我每个人都扩展一些抽象类对我来说毫无意义(正如Arleigh Hix在对您的问题的评论中所建议的那样)。我这样做的原因与您的项目无关,但对我来说很好。

Comment类:

/**
 * @ORM\Entity
 */
class Comment
{
    /**
     * @ORM\Column(type="text")
     */
    private $comment;

    /**
     * @ORM\ManyToOne(
     *     targetEntity="App\Entity\Referral",
     *     inversedBy="comments"
     * );
     */
    private $referral;

    /**
     * @ORM\ManyToOne(
     *     targetEntity="App\Entity\Admission",
     *     inversedBy="comments"
     * );
     */
    private $admission;

    // getters and setters omitted
}

然后我的Admission实体(和我的Referral实体)当然也有关系的另一面:

/**
 * @ORM\Entity
 */
class Admission
{
    public function __construct()
    {
        $this->comments = new ArrayCollection();
    }

    /**
     * @ORM\OneToMany(targetEntity="Comment",
     *     mappedBy="admission",
     * )
     */
    private $comments;
}