简体   繁体   中英

Symfony Many to Many does not update on both sides

I am creating an Asset Management Web App with Symfony 3.3.

I have two entities, Asset and User.

User:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User extends BaseUser
{
    /**
     * @ORM\ManyToMany(targetEntity="Asset", mappedBy="users")
     */
    private $assets;

    /**
     * Add asset
     *
     * @param \AppBundle\Entity\Asset $asset
     *
     * @return User
     */
    public function addAsset(\AppBundle\Entity\Asset $asset)
    {
        $this->assets[] = $asset;

        return $this;
    }

    /**
     * Remove asset
     *
     * @param \AppBundle\Entity\Asset $asset
     */
    public function removeAsset(\AppBundle\Entity\Asset $asset)
    {
        $this->assets->removeElement($asset);
    }

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

Asset:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Asset
 *
 * @ORM\Table(name="asset")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\AssetRepository")
 */
class Asset
{
    /**
     * @ORM\ManyToMany(targetEntity="User", inversedBy="assets")
     * @ORM\JoinTable(name="asset_user")
     */
    private $users;

    /**
     * Add user
     *
     * @param \AppBundle\Entity\User $user
     *
     * @return Asset
     */
    public function addUser(\AppBundle\Entity\User $user)
    {
        $this->users[] = $user;
        return $this;
    }

    /**
     * Remove user
     *
     * @param \AppBundle\Entity\User $user
     */
    public function removeUser(\AppBundle\Entity\User $user)
    {
        $this->users->removeElement($user);
    }

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

They have a Many to Many relationship. If I add Users in an Asset, everything is fine, but if I add assets in a user. It won't persist.

Adding my Form types:

AssetUserAssignType:

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;


class AssetUserAssignType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('users', CollectionType::class, array(
                    'label' => 'Custodians',
                    'entry_type' => EntityType::class,
                    'entry_options' => array(   'class'=>'AppBundle:User',
                                                'label'=>false,
                                                'choice_label'=>'employee_number',
                                                ),
                    'allow_delete' =>true,
                    'allow_add' => true,
                    'allow_delete' => true,
                    'prototype' => true,
                    'by_reference' => false,
                    'attr'=> array('class'=>'user_collection'),
                ));
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Asset'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_assetassign';
    }


}

UserAssetAssignType:

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;


class UserAssetAssignType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('assets', CollectionType::class, array(
                    'label' => 'Assets',
                    'entry_type' => EntityType::class,
                    'entry_options' => array(   'class'=>'AppBundle:Asset',
                                                'label'=>false,
                                                'choice_label'=>'code',
                                                ),
                    'allow_delete' =>true,
                    'allow_add' => true,
                    'allow_delete' => true,
                    'prototype' => true,
                    'by_reference' => false,
                    'attr'=> array('class'=>'asset_collection'),
                ));
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_assetassign';
    }


}

If you need more code, I'll paste them here also, but I think this is enough.

This is because in a Doctrine relation, the owning side (the entity which define column name and inversedBy property) always take priority on the inverse side (the other one).

As mentioned in documentation here : http://symfony.com/doc/current/doctrine/associations.html#setting-information-from-the-inverse-side a good practice is to update default setters (including add and remove functions) to make sure two entities are always synchronized.

In example :

public function addAsset(\AppBundle\Entity\Asset $asset)
{
    $this->assets[] = $asset;
    $asset->addUser($this);

    return $this;
}

public function removeAsset(\AppBundle\Entity\Asset $asset)
{
    $this->assets[] = $asset;
    $asset->removeUser($this);

    return $this;
}

As you already have set the by_reference to false in your forms (required to make sure setters are called by Form component), it will do the trick.

Hope it will help.

You have not implemented the database correctly, If the relationship is many to many, There should be an associate entity, So among User and Asset there must be an entity called UserAsset. Then user has many UserAsset and If you get one UserAsset, there is only one user for that record. One Asset can be in Many UserAsset records and If you get one UserAsset record, only one asset should associate with that record.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM