Symfony2 ManytoMany двунаправленное отношение - как сохранить вручную

Я работаю над формой с двумя полями ввода и кнопкой отправки. Первое поле - это простое раскрывающееся меню (категория), а другое - поле ввода тегов (тег), в котором вы можете одновременно вводить несколько тегов. Оба поля принимают только предопределенные параметры ввода.

Значения опций категории жестко закодированы в javascript:

categories = [
                        {"id": 1, "categoryname": "standard"},
                        {"id": 2, "categoryname": "premium"},
                        {"id": 3, "categoryname": "gold"}
                    ];

Параметры тега извлекаются из таблицы tag в базе данных. Вот скриншот таблиц базы данных:

введите описание изображения здесь

Объекты Category и Tag связаны с двунаправленным отношением Doctrine ManytoMany, причем категория является стороной-владельцем.

Примечание: я не использую Symfony formType для создания формы, вместо этого я использовал javascript для этого.

javascript отлично работает, и я получаю входные данные в своем контроллере. Проблема заключается в том, что я никогда не сохранял отношение ManytoMany вручную. Прочитал документы, но не уверен, что я что-то пропустил.

Вот объект тега (Tag.php):

<?php

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\Category;

/**
 * Tag
 *
 * @ORM\Table(name="tag")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
 */
class Tag {

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

    /**
     * 
     * @var string
     *
     * @ORM\Column(name="TagName", type="string")
     */
    protected $tagname;

    /**
     * @ORM\ManyToMany(targetEntity="Category", mappedBy="tags")
     */
    protected $categories;

    /**
     * @return ArrayCollection 
     */
    public function __construct() {
        $this->categories = new ArrayCollection();
    }

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


    /**
     * Set id
     *
     * @return Tag 
     */
    public function setId($id)
    {
        return $this->id = $id;
    }

    /**
     * Set tagname
     *
     * @param string $tagname
     * @return Tag
     */
    public function setTagname($tagname)
    {
        $this->tagname = $tagname;

        return $this;
    }

    /**
     * Get tagname
     *
     * @return string 
     */
    public function getTagname()
    {
        return $this->tagname;
    }

    /**
     * Add categories
     *
     * @param \AppBundle\Entity\Category $categories
     * @return Tag
     */
    public function addCategory(\AppBundle\Entity\Category $categories)
    {
        $this->categories[] = $categories;

        return $this;
    }

    /**
     * Remove categories
     *
     * @param \AppBundle\Entity\Category $categories
     */
    public function removeCategory(\AppBundle\Entity\Category $categories)
    {
        $this->categories->removeElement($categories);
    }

    /**
     * Get categories
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getCategories()
    {
        return $this->categories;
    }
}

Вот объект Category (Category.php):

<?php

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\Tag;

/**
 * Category
 *
 * @ORM\Table(name="category")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
 */
class Category {

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

    /**
     * 
     * @var string
     *
     * @ORM\Column(name="CategoryName", type="string")
     */
    protected $categoryname;

    /**
     * 
     * @var string
     *
     * @ORM\Column(name="Description", type="string")
     */
    protected $description;

    /**
     * @ORM\ManyToMany(targetEntity="Tag", cascade={"persist"}, inversedBy="categories")
     */
    protected $tags;

    /**
     * @return ArrayCollection 
     */
    public function __construct() {
        $this->tags = new ArrayCollection();
    }

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

    /**
     * Set id
     *
     * @return Category 
     */
    public function setId($id) {
        return $this->id = $id;
    }

    /**
     * Set categoryname
     *
     * @param string $categoryname
     * @return Category
     */
    public function setCategoryname($categoryname) {
        $this->categoryname = $categoryname;

        return $this;
    }

    /**
     * Get categoryname
     *
     * @return string 
     */
    public function getCategoryname() {
        return $this->categoryname;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return Category
     */
    public function setDescription($description) {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string 
     */
    public function getDescription() {
        return $this->description;
    }

    /**
     * Add tags
     *
     * @param \AppBundle\Entity\Tag $tags
     * @return Category
     */
    public function addTag(\AppBundle\Entity\Tag $tags) {
        $this->tags[] = $tags;

        return $this;
    }

    /**
     * Remove tags
     *
     * @param \AppBundle\Entity\Tag $tags
     */
    public function removeTag(\AppBundle\Entity\Tag $tags) {
        $this->tags->removeElement($tags);
    }

    /**
     * Get tags
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getTags() {
        return $this->tags;
    }

}

Вот контроллер (DefaultController.php):

/**
 * @Route("/formsubmit", options={"expose"=true}, name="my_route_to_submit")
 */
public function submitAction(Request $request) {
    $jsonString = file_get_contents('php://input');
    $form_data = json_decode($jsonString, true);

    $em = $this->getDoctrine()->getManager();

    // set category details
    $categoryId = $form_data[0]['id'];
    $category = $em->getRepository('AppBundle:Category')->findOneById($categoryId);

    // set tags
    $len = count($form_data[1]);

    for ($i = 0; $i < $len; $i++) {
        $tagId = $form_data[1][$i]['id'];
        $tag = $em->getRepository('AppBundle:Tag')->findOneById($tagId);
        $category->addTag($tag);
    }

    // persist/save in database
    $em->persist($category);
    $em->flush();
}

$form_data - это массив с категорией ввода и добавлением тегов. Это выглядит так:

$form_data = [
            ['id' => 3, 'categoryname' => 'gold'],
            [
                ['id' => 1, 'tagname' => 'wifi'],
                ['id' => 4, 'tagname' => 'geyser'],
                ['id' => 2, 'tagname' => 'cable']
            ]
        ];

Тем не менее он не сохраняется. var_dump($category); отображает выделенный объект категории с категорией id и categoryname, но связанное с ним свойство tags пусто.

Вот скриншот выхода:

введите описание изображения здесь

Любые идеи?

Быстрый вопрос на стороне. Нужно ли добавлять cascade={"persist"} к обеим сторонам определения отношений здесь?

EDIT: Здесь я жестко закодировал $form_data вместо использования входных данных, как я сделал выше. DefaultController.php:

    /**
     * @Route("/formsubmit", options={"expose"=true}, name="my_route_to_submit")
     */
    public function submitAction(Request $request) {
        $form_data = [
            ['id' => 3, 'categoryname' => 'gold'],
            [
                ['id' => 1, 'tagname' => 'wifi'],
                ['id' => 4, 'tagname' => 'geyser'],
                ['id' => 2, 'tagname' => 'cable']
            ]
        ];

        $em = $this->getDoctrine()->getManager();

        // set category details
        $categoryId = $form_data[0]['id'];
        $category = $em->getRepository('AppBundle:Category')->findOneById($categoryId);

        // set tags
        $len = count($form_data[1]);

        for ($i = 0; $i < $len; $i++) {
            $tagId = $form_data[1][$i]['id'];
            $tag = $em->getRepository('AppBundle:Tag')->findOneById($tagId);
//            $tag->addCategory($category);
            $category->addTag($tag);
        }
        var_dump($category);
        exit;

        // persist/save in database
        $em->persist($category);
        $em->flush();
    }

Выход контроллера:

введите описание изображения здесь

Как вы видите, свойство tags объекта категории по-прежнему пусто.

Надеюсь, это поможет лучше понять проблему. Ожидающий ответ...

Ответ 1

При получении соответствующего объекта тега выполните

 $tag = $em->getRepository('AppBundle:Tag')->findOneById($tagId);

Не является ли значение $tag массивом наборов?

Итак, возможно ли сделать следующее?

    $category->addTag($tag[0]);