Many to Many Relationship Setter

2019-03-21 16:55发布

问题:

I have a Many to Many relationship between my Entities: PurchaseOrder and Supplier. When I want do add a Supplier to an order in my Symfony project, I always get this error message:

Property "suppliers" is not public in class "Acme\AppBundle\Entity\PurchaseOrder". Maybe you should create the method "setSuppliers()"?

When I make a setSuppliers() function by myself in the PurchaseOrder Entity:

public function setSuppliers(\Acme\AppBundle\Entity\Supplier $suppliers )
{
    $this->suppliers = $suppliers;

    return $this;
}

I get this error message:

Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in /var/www/symfony/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 519 and defined in /var/www/symfony/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php line 47

Any ideas?

/**
 * @Route("order/{id}/supplieradd", name="order_supplieradd")
 * @Secure(roles="ROLE_ADMIN")
 */
public function newSupplierAction(Request $request, $id)
{
    $purchaseOrder = $this->getDoctrine()
    ->getRepository('AcmeAppBundle:PurchaseOrder')
    ->find($id);

    if (!$purchaseOrder) {
        throw $this->createNotFoundException(
                'No order found for id '.$id
        );
    }

    $form = $this->createForm(new AddSupplierType(), $purchaseOrder);

    // process the form on POST
    if ($request->isMethod('POST')) {
        $form->bind($request);
        if ($form->isValid()) {             

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

            $em->persist($purchaseOrder);
            $em->flush();

            return new Response('Added Supplier to Order with ID '.$articleOrder->getId());
        }
    }

    return $this->render('AcmeAppBundle:BasicData:newSupplier.html.twig', array(
            'form' => $form->createView(),
            'id' => $id,
    ));
}

And my AddSupplierType.php

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('suppliers', 'entity', array(
         'class' => 'AcmeAppBundle:Supplier',
         'property' => 'name',
    ));
}

Some parts of the PurchaseOrder and Supplier entities:

 class PurchaseOrder{
  ...
         /**
         * @ORM\ManyToMany(targetEntity="Supplier", mappedBy="purchaseOrders")     
         */
    private $suppliers;

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

/**
 * Add suppliers
 *
 * @param \Acme\AppBundle\Entity\Supplier $suppliers
 * @return PurchaseOrder
 */
public function addSupplier(\Acme\AppBundle\Entity\Supplier $suppliers)
{
    $this->suppliers[] = $suppliers;

    return $this;
}

/**
 * Remove suppliers
 *
 * @param \Acme\AppBundle\Entity\Supplier $suppliers
 */
public function removeSupplier(\Acme\AppBundle\Entity\Supplier $suppliers)
{
    $this->suppliers->removeElement($suppliers);
}

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

class Supplier{
    ...

    /**
     * @ORM\ManyToMany(targetEntity="PurchaseOrder", inversedBy="suppliers")
     * @ORM\JoinTable(name="suppliers_purchaseOrders")
     */
    private $purchaseOrders;
}

New add remove set methods:

 /**
     * Add supplier
     *
     * @param \Acme\AppBundle\Entity\Supplier $supplier
     * @return PurchaseOrder
     */
public function addSupplier(\Acme\AppBundle\Entity\Supplier $supplier)
{
    $this->suppliers->add($supplier);

    return $this;
}

/**
 * Remove supplier
 *
 * @param \Acme\AppBundle\Entity\Supplier $supplier
 */
public function removeSupplier(\Acme\AppBundle\Entity\Supplier $supplier)
{
    $this->suppliers->removeElement($supplier);
}

public function setSuppliers($supplier)
{
     if ( is_array($supplier) ) {
        $this->suppliers = $supplier ;
    } else {
        $this->suppliers->clear() ;
        $this->suppliers->add($supplier) ;
    }
}

回答1:

Problems:

  1. Grammatically incorrect method name and its argument:

    public function addSupplier(\Acme\AppBundle\Entity\Supplier $suppliers)
    

    method says addSupplier (singular) but you are accepting supplierS (plural)

  2. You need refactoring on this method to be:

    public function addSupplier(Supplier $supplier)
    {
        $this->suppliers->add($supplier) ;
    }
    

    also:

    public function removeSupplier(Supplier $supplier)
    {
        $this->suppliers->removeElement($supplier) ;
    }
    

    Getter and setter method will work if you do it like my answer on: set multiple='false' in a form in a many to many relation symfony2

Symfony will find add...() and remove...() methods by itself. So if relation is "suppliers", it will find addSupplier. Or if relation is "categories", it will find addCategory() and removeCategory().



回答2:

Shouldn't your setSupliers method take an ArrayCollection as the first argument?

public function setSuppliers(ArrayCollection $suppliers )
{
    $this->suppliers = $suppliers;

    return $this;
}

As you initalized it as ArrayCollection in your constructor. With your setSuppliers method you will convert your ArrayCollection to one single Supplier Object.

Your form will return one single selected Suplier Object. If this is realy what you want, why don't you use a OnyToMany realtionship? Otherwise you could add the multiple option to your form field. Then multiple Suplier objects can be returned as array.