Symfony 4 с Doctrine - сохранить ManyToOne - PullRequest
0 голосов
/ 03 марта 2020

У меня проблема с моим symfony кодом: метод обновления моего репозитория

 /**
     * @param MenuModel $menu
     * @return MenuEntity|MenuModel
     * @throws RepositoryException
     */
    public function updateMenu(MenuModel $menu)
    {
        try {
            $transformedMenu = $this->menuTransformer->transform($menu);
            $transformedMenu = $this->getEntityManager()->merge($transformedMenu);
            $this->getEntityManager()->flush($transformedMenu);
            $this->getEntityManager()->detach($transformedMenu);

            return $this->menuTransformer->transform($transformedMenu);
        } catch (\Exception $e) {
            throw new RepositoryException($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
        }
    }

Сущность My Element:

/**
 * Element.
 *
 * @ORM\HasLifecycleCallbacks
 * @ORM\Table(name="element")
 * @ORM\ChangeTrackingPolicy("DEFERRED_EXPLICIT")
 * @ORM\Entity(repositoryClass="CP\API\Repository\ElementRepository")
 */
class Element extends \CP\RestBundle\Model\Element
{
    use Traits\SystemObjectTrait;
    use Traits\ChannelsTrait;
    use Traits\PropertiesTrait;
    use Traits\WorkflowTrait;

    /**
     * @ORM\ManyToOne(targetEntity="Menu", inversedBy="contents")
     * @ORM\JoinColumn(name="menu_id", referencedColumnName="id")
     */
    protected $menu;

    /**
     * Element constructor.
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @param int $id
     *
     * @return Element
     */
    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    /**
     * Update publication by modification when empty
     * Update status according to publication, unpublication and archiving
     *
     * @ORM\PrePersist()
     */
    protected function prePersist()
    {
        $this->updatePublication();
        $this->updateStatus();
    }

    /**
     * Update publication by modification when empty
     * Update status according to publication, unpublication and archiving
     *
     * @ORM\PreUpdate()
     */
    public function preUpdate()
    {
        $this->updatePublication();
        $this->updateStatus();
    }

    /**
     * Increases object version
     */
    public function increaseVersion()
    {
        ++$this->version;
    }


    /**
     * @return mixed
     */
    public function getMenu()
    {
        return $this->menu;
    }

    /**
     * @param mixed $menu
     */
    public function setMenu($menu): void
    {
        $this->menu = $menu;
    }


}

Сущность My Menu

<?php

namespace CP\API\Entity;

use CP\API\Entity\Traits\TimestampableTrait;
use CP\Model\Configuration;
use CP\Model\Content;
use CP\Model\Language;
use CP\Model\MenuTranslation;
use CP\RestBundle\Model\Locator;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;


/**
 * @ORM\HasLifecycleCallbacks()
 * @ORM\Table(name="custom_menu")
 * @ORM\Entity(repositoryClass="CP\API\Repository\MenuRepository")
 */
class Menu
{
    use TimestampableTrait;

    /**
     * @var int
     *
     * @ORM\Id()
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;


    /**
     * @ORM\OneToMany(targetEntity="CP\API\Entity\Element",mappedBy="menu",cascade={"persist"})
     */
    protected $contents;

    public function __construct(Menu $parent = null)
    {
        $dateTime = new \DateTime();
        $this->creation = $dateTime;
        $this->modification === null && $this->setModification($dateTime);
        $this->contents = new ArrayCollection();
    }

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


    /**
     * @param int $id
     */
    public function setId(int $id): void
    {
        $this->id = $id;
    }


    /**
     * @return ArrayCollection
     */
    public function getContents(): ArrayCollection
    {
        return $this->contents;
    }

    /**
     * @param ArrayCollection $contents
     */
    public function setContents(ArrayCollection $contents): void
    {
        $this->contents = $contents;
    }

    /**
     * @param Element $element
     * @return $this
     */
    public function addContent(Element $element): self
    {
        if (!$this->contents->contains($element)) {
            $this->contents[] = $element;
            $element->setMenu($this);
        }
        return $this;
    }

    /**
     * @param Element $element
     * @return $this
     */
    public function removeContent(Element $element): self
    {
        if ($this->contents->contains($element)) {
            $this->contents->removeElement($element);
            if ($element->getMenu() === $this) {
                $element->setMenu(null);
            }
        }
        return $this;
    }
}

Моя модель меню

<?php

namespace CP\Model;

use CP\RestBundle\Model\Element;
use CP\RestBundle\Model\Locator;
use CP\RestBundle\Model\Product;
use CP\RestBundle\Model\Traits\TimestampableTrait;

class Menu extends BaseModel
{

    /** @var Content[] */
    protected $content;

    /**
     * @return array|null
     */
    public function getContent(): ?array
    {
        return $this->content;
    }

    /**
     * @param Content[] $content
     */
    public function setContent(array $content): void
    {
        $this->content = $content;
    }

}

Мой преобразователь из модели в сущность

public function transform($object)
{
     if ($object instanceof Menu) {
            $menuData = new MenuEntity();
            if ($object->getId())
                $menuData->setId($object->getId());


            if ($object->getContent() instanceof Content) {
                $contentEntity = new ContentEntity();
                $content = $object->getContent();

                $contentEntity->setId($content->getId());
                $menuData->addContent($this->attachToEntityManager($contentEntity));
            }

            return $menuData;
}

 private function attachToEntityManager($object)
    {
        try {
            $attachedObject = $this->entityManager->merge($object);
            return $attachedObject;
        } catch (ORMException $e) {
            throw new ModelTransformationException(sprintf('Model transformation error, object could not be attached to Entity Manager: %s in %s',
                $e->getMessage(), $e->getFile() . ':' . $e->getLine()));
        }
    }

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

Есть идеи?

1 Ответ

0 голосов
/ 04 марта 2020

Можете ли вы дать нам больше контекста того, что делает ваш код? Конструктор MenuEntity странный, без использования $ parent, логическое сравнение не используется. Почему вам нужно использовать методы отсоединения и слияния entityManager - Mcsky

Правильно, я не вижу смысла в использовании отсоединения и / или слияния здесь. Обычно со связью OneToMany вы используете диспетчер сущностей и сохраняете оба отношения, flu sh, и у вас должна быть сущность со связью OneToMany.

Я надеюсь, что это может быть полезно, особенно эта часть: https://symfony.com/doc/current/doctrine/associations.html#saving связанных сущностей

пример:

        // relates this product to the category
        $product->setCategory($category);

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($category);
        $entityManager->persist($product);
        $entityManager->flush();
...