Easyadminbundle: "new" action doesn't work with many to many relation

Created on 2 Nov 2016  路  5Comments  路  Source: EasyCorp/EasyAdminBundle

Hello,
At first, sorry for my badly english

Then, my configuration :

"php": ">=5.5.9",
"symfony/symfony": "3.1.*",
"doctrine/orm": "^2.5",
"doctrine/doctrine-bundle": "^1.6",
"doctrine/doctrine-cache-bundle": "^1.2",
"symfony/swiftmailer-bundle": "^2.3",
"symfony/monolog-bundle": "^2.8",
"symfony/polyfill-apcu": "^1.0",
"sensio/distribution-bundle": "^5.0",
"sensio/framework-extra-bundle": "^3.0.2",
"incenteev/composer-parameter-handler": "^2.0",
"friendsofsymfony/user-bundle": "dev-master",
"javiereguiluz/easyadmin-bundle": "^1.15",
"symfony/assetic-bundle": "^2.7.1",
"leafo/scssphp": "~0.6",
"patchwork/jsqueeze": "~1.0",
"liuggio/excelbundle": "dev-master"

I have a problem with a "new" action. I have a many-to-many relation between "user" entity and "userGroup" entity. So, I have three tables = users, user_group and user_group_user(join table).

When I want to create a userGroup (include users already subscribe), the group is created without any user inside.

A part of my "config.yml"

UserGroup:
    class: AppBundle\Entity\UserGroup
    disabled_actions: ['search']
    label: app.menu.usergroups
    new:
        fields:
            - nom
            - users

My form is correct, users are available in the list.

report1

But they are not subcribe in the group.

Strange thing, when I connect a user to a group, it works.

report2

User:
    class: AppBundle\Entity\User
    disabled_actions: ['search', 'delete']
    label: app.menu.users
    list:
        fields:
            - id
            - username
            - { property: 'matricule', format: '%01.0f'}
            - nom
            - prenom
            - email
            - { property: 'groupUser', template: 'group_user.html.twig' }
           # - groupUser
            #- roles
            - { property: 'roles', template: 'role.html.twig', label_colors: ['first', 'success', 'info'] }
            - enabled
            - lastLogin
    new:
        fields:
            - matricule
            - nom
            - prenom
            - email
            - groupUser
            - { property: 'vueGenerale', type: 'choice', type_options: {expanded: true, choices: {'Oui': '1', 'Non': '0' } } }
            - { property: 'enabled', type: 'checkbox', type_options: { attr: {checked: true}} }
            - { property: 'plainPassword', type: 'text', type_options: { required: true, attr:{value:'cfgservices'} } }
            - { property: 'roles', type: 'choice', type_options: { multiple: true, empty_data: null, choices: { 'SALARIES': 'ROLE_USER', 'MANAGER': 'ROLE_MANAGER', 'DG': 'ROLE_DG', 'DRH': 'ROLE_DRH' } } }

Can you help me ?

bug

Most helpful comment

Thanks to this comment in StackOverflow, I've found the solution: you must set the by_reference option to false in some field of your Symfony form:

UserGroup:
    # ...
    new:
        fields:
            - nom
            - { property: 'users', type_options: { 'by_reference': false } }

My questions: is this a feature or a bug of the Symfony Form component? Can we do something to enable this option by default? Would that be dangerous for some users? Thanks!

All 5 comments

I can confirm this error. For example, in the EasyAdmin Demo app:

  1. Product and Category are related many-to-many.
  2. If you create a product, its categories are saved.
  3. If you create a category, its products are lost.

Sadly, for now I don't know how to solve this problem 馃槙

Thanks to this comment in StackOverflow, I've found the solution: you must set the by_reference option to false in some field of your Symfony form:

UserGroup:
    # ...
    new:
        fields:
            - nom
            - { property: 'users', type_options: { 'by_reference': false } }

My questions: is this a feature or a bug of the Symfony Form component? Can we do something to enable this option by default? Would that be dangerous for some users? Thanks!

@javiereguiluz : This is documented in and right above the "Doctrine: Cascading Relations and saving the "Inverse" side" section of the "How to Embed a Collection of Forms" cookbook and in http://symfony.com/doc/current/reference/forms/types/form.html#by-reference.
This is a common and well-known "issue", but not with the Symfony Form component.

@jordan-garay as you can see, it seems that this problem is something commonly suffered when using Doctrine + Symfony Forms. Therefore, I'm closing it because there's nothing left we can do from this bundle.

In the demo application I've added a small example + help note for future reference: https://github.com/javiereguiluz/easy-admin-demo/commit/452d402a0600d29228fb6ed366ae256c0d2f2180#diff-8b369a630fd4a6cad1d6bf3c574530bb

Some improvements related to this (and also the Demo?) cause I'm using it as base for development and I still have the issue adding "by_reference"

I'm developing entities which have others nested in a hierarchy way and it's quite strange (and not pretty) than the lower entity is the only capable to "decide" which is taking as his "owner
Also, but I don't put them here to make the post so extensive: I have relations between different entities and I have the same problem: I only can define the relation from the side where I have a unique value (parent)

This is a Zone: which is nested within others Zone and should have ScoutGroups (other entity)
Zone.php

<?php
namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Class Zone [Strongly inspired by "Category", i think]
 * @ORM\Table(name="zone")
 * @ORM\Entity
 */
class Zone
{
    protected $id = null;
    protected $name;


    /**
     * ScoutGroups in the zone.
     * @var ScoutGroup[]
     * @ORM\OneToMany(targetEntity="ScoutGroup", mappedBy="zone")
     **/
    protected $scoutGroups;

    /**
     * Childrens of a Zone
     * @var Zone
     * @ORM\OneToMany(targetEntity="Zone", mappedBy="parent")
     */
    protected $children;

    /**
     * The zone parent.
     * @var Zone
     * @ORM\ManyToOne(targetEntity="Zone", inversedBy="children")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
     **/
    protected $parent;

    public function __construct()
    {
        $this->children = new ArrayCollection();
        $this->scoutGroups = new ArrayCollection();
    }

    public function __toString()
    {
        return $this->getName();
    }

    /**
     * Set the parent zone.
     * @param Zone $parent
     */
    public function setParent($parent)
    {
        $this->parent = $parent;
    }

    /**
     * Get the parent zone.
     * @return Zone
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Get the children zones.
     * @return Zone []
     */
    public function getChildren()
    {
        return $this->children;
    }

    /**
     * Set all childrens of the Zone.
     * @param Children[] $products
     */
    public function setChildren($products)
    {
        $this->children->clear();
        $this->children = new ArrayCollection($products);
    }

    /**
     * Add a Children Zone.
     * @param $product Children The children zone of another bigger
     */
    public function addChildren($product)
    {
        if ($this->children->contains($product)) {
            return;
        }

        $this->children>add($product);
        $product->setParent($this);
    }

    /**
     * @param ScoutGroup $product
     */
    public function removeChildren($product)
    {
        if (!$this->children->contains($product)) {
            return;
        }

        $this->children->removeElement($product);
        $product->setParent(null);
    }

    /**
     * Return all ScoutGroups associated to the zone.
     * @return ScoutGroup[]
     */
    public function getScoutGroups()
    {
        return $this->scoutGroups;
    }

    /**
     * Set all ScoutGroups in the Zone.
     * @param ScoutGroups[] $products
     */
    public function setScoutGroups($products)
    {
        $this->scoutGroups->clear();
        $this->scoutGroups = new ArrayCollection($products);
    }

    /**
     * Add a ScoutGroup in the Zone.
     * @param $product ScoutGroup The scoutGroup to associate
     */
    public function addScoutGroup($product)
    {
        if ($this->scoutGroups->contains($product)) {
            return;
        }

        $this->scoutGroups>add($product);
        $product->setZone($this);
    }

    /**
     * @param ScoutGroup $product
     */
    public function removeScoutGroup($product)
    {
        if (!$this->scoutGroups->contains($product)) {
            return;
        }

        $this->scoutGroups->removeElement($product);
        $product->setZone(null);
    }
}

Zone.yml:

easy_admin:
    entities:
        Zone:
            class: AppBundle\Entity\Zone
            label: 'Demarcacions'
            list:
                title: 'Llista de Demarcacions'
                actions:
                    - { name: 'show', label: '', icon: 'search' }
                    - { name: 'edit', label: '', icon: 'edit' }
                    - { name: 'delete', label: '', icon: 'trash' }
                    - { name: 'scoutGroups', label: '', icon: 'users'}
                fields:
                    - { property: 'name', label: 'Nom' }
                    - { property: 'children', label: 'SubZones'}
                    - { property: 'scoutGroups', label: 'Agrupaments' }
                    - { property: 'parent', label: 'Superior'}
            form:
                fields:
                    - { property: 'name', label: 'Nom' }
                    - { property: 'children', label: 'SubZones', type_options: {by_reference:false} }
                    - { property: 'scoutGroups', label: 'Agrupaments' }
                    - { property: 'parent', label: 'Superior'}

I'm not sure if the error is made by my Doctrine definitions or it's related to this issue...

Was this page helpful?
0 / 5 - 0 ratings