отношение symfony от одной сущности ко многим сущностям из разных таблиц - PullRequest
2 голосов
/ 13 июня 2019

У меня есть приложение Symfony с сущностью Comment , и я хотел бы связать его со свойством отношения со многими сущностями. Но под многими сущностями я имею в виду не много объектов, а множество классов .

A комментарий может быть о разных вещах в моем приложении: Рисунок сущность, Текст сущность, Compo сущность, или Фотография сущность. Каждый объект соответствует типу художественного произведения, у каждого из которых разные свойства и разные страницы. И каждый может быть оценен и прокомментирован .

Проблема в том, что когда я хочу создать свойство отношения в моей сущности Comment, я должен указать только ОДНУ сущность . Я хочу, чтобы некоторые комментарии касались рисованной сущности, некоторые - о тексте и т. Д.

Я вижу решение: создать один комментарий к сущности по сущности для ссылки, но в моем приложении будет очень много сущностей, и мой код будет дублирован, что не очень хорошо для будущих изменений и т. Д.

Есть ли способ связать одну сущность с множеством разных типов сущностей?

Спасибо.

РЕДАКТИРОВАТЬ 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

РЕДАКТИРОВАТЬ :

Благодаря ответу Джакуми ниже я использовал отображение наследования . Я выбрал первое решение: решение за одним столом . Но я попробовал, у меня нет сообщения об ошибке , но это не сработало для меня , и после нескольких часов я все еще не понимаю, что я сделал неправильно.

Мой Класс комментариев ( самый верхний класс моей иерархии комментариев , который имеет 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;
    }
}

А вот сущность Дессин это относится к (Дессин рисует по-французски, я забыл назвать его английским именем, когда создавал его), это не дочерний класс класса 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

Затем я попытался показать содержание комментария к рисунку выше на странице, используя переменную рисунка, соответствующую чертежу, являющемуся предметом моего комментария. Ничего не случилось. Так что Я непосредственно пытался сбросить его в контроллер, невозможно получить комментарий .

Чтобы получить комментарий, я использовал свойство $ commentsDrawing объекта Drawing (вы можете увидеть его в приведенном выше коде). Вот код, который я использовал для создания дампа var чертежа, который должен содержать комментарий (я поместил функцию dump () в функцию show, то есть вызов с параметром draw в качестве параметра, созданного в 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
        ]);
    }
}

Что показывает дамп ():

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 :

Ну, я добавил новый CommentDrawing прямо из моего DrawingController:

/**
 * 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 ]

2 голосов
/ 14 июня 2019

Ничто не говорит о том, что вы не можете иметь несколько ManyToOne отношений в пределах одной сущности. Поэтому он может быть не таким элегантным и чистым, как отображение наследования, как предложено Джакуми, но он работает без особых усилий. Я сделал это в проекте в соответствии с тем, что вы описываете с помощью единственной сущности Comment, которая ссылается на несколько других сущностей одновременно. Для моего класса Comment у меня есть другие сущности (Admission и Referral), каждая из которых указывает на несколько Comment с, но Admission и Referral в остальном ничем не похожи и поэтому не имеют смысла для меня чтобы каждый из них расширил некоторый абстрактный класс (как это было предложено Арли Хиксом в комментариях к вашему вопросу). Мои причины для этого не имеют отношения к вашему проекту, но он хорошо работает для меня.

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;
}
2 голосов
/ 13 июня 2019

сама ассоциация, скорее всего, является неправильным местом для этой абстракции. В любом случае, то, что вы, вероятно, в некоторой степени ищете, - это отображение наследования.

Для справки:

https://www.doctrine -project.org / проекты / Доктрина-ОРМ / ы / 2,6 / ссылки / наследования mapping.html

решение за одним столом:

Один из вариантов - наследование одной таблицы. Родительский класс получает метод "getSubject ()", который дочерние классы переопределяют, и содержит идентификатор пользователя + рейтинг + текст комментария. Дочерними классами являются DrawingComment и т. Д., И каждый из них имеет непосредственное отношение к теме комментария.

Преимущества: простая, понятная семантика, при необходимости может различаться по имени класса (или дополнительной функции / свойству), ссылочная целостность стабильна, статистика довольно проста, поиск возможен независимо от типа темы комментария

Недостатки: дискриминатор-колонка несколько неудобна в использовании и немного снижает скорость.

решение многих таблиц:

Оставьте отображение наследования, создайте абстрактный класс (+ интерфейс?) Или признак (+ интерфейс) для общего кода. хранить каждый тип комментария в своем собственном классе / сущности / таблице. Если вам не важен тип комментария, вы все равно можете использовать интерфейсные функции для его чистой обработки.

преимущества: то же, что и раньше - за исключением возможности поиска, нет столбца дискриминатора, поскольку каждый тип комментария имеет свою собственную таблицу

недостатки: возможность поиска немного снижена

В конечном счете, это вопрос вкуса или привычки. И это зависит от общих и разных полей разных типов комментариев. если - кроме предмета - все они имеют одни и те же поля, я бы сделал наследование одной таблицы. чем больше они отличаются, тем больше я склоняюсь к решению с множеством таблиц.

...