简体   繁体   中英

sonata_type_collection field only works with existing parent objects

In a Symfony2 application using the Sonata Admin bundle, I have two entities:

  • CorporateAttributes
  • CorporateAttributesApi

Related in Doctrine like so:

CorporateAttributes ←one-to-many→ CorporateAttributesApi

My Sonata Admin class for CorporateAttributes contains the following:

in AppBundle/Admin/CorporateAttributesAdmin.php

// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper) {
    $formMapper
        ->add('apis', 'sonata_type_collection',
            ['required' => false, 'label' => 'API Clients'],
            ['edit'=>'inline','inline'=>'table']
        )
    ;
}

This adds a "Add new" button to the CorporateAttributes form where I can add and edit CorporateAttributesApi's related to the CorporateAttributes object for which the user is editing.

However, this only works for an existing CorporateAttributes object.

If I'm trying to add a new CorporateAttributes, clicking the "Add New" button gives the following error in the console:

Failed to load resource: the server responded with a status of 500 (Internal Server Error)
http://localhost/app_dev.php/admin/core/append-form-field-element?code=sonata.admin.corporateattributes&elementId=s55fc29157eeee_apis&uniqid=s55fc29157eeee

I suspect it has something to do with the fact that CorporateAttributesApi needs a CorporateAttributes id that it references, but I'm not sure how to make it play nice.

Here is the other relevant code:

in AppBundle/Admin/CorporateAttributesApiAdmin.php:

// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper) {
    $formMapper
        ->add('corporate_attributes', null, ['required' => true])
        ->add('group_name', 'choice', [
            'choices' => ['a', 'b', 'c'],
            'required' => false,
        ])
    ;
}

And the entities with doctrine2 annotations:

in AppBundle/Entity/CorporateAttributes.php:

namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
 * CorporateAttributes
 *
 *
 * @ORM\Entity
 * @ORM\Table("drupal_wiredb_corporate_attributes")
 */
class CorporateAttributes
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /** 
     * @ORM\OneToMany(targetEntity="CorporateAttributesApi", mappedBy="corporate_attributes", cascade={"persist"}, orphanRemoval=true))
     */
    protected $apis;

    public function getId() {
        return $this->id;
    }

    /**
     * Add apis
     *
     * @param \AppBundle\Entity\CorporateAttributesApi $apis
     * @return CorporateAttributes
     */
    public function addApi(\AppBundle\Entity\CorporateAttributesApi $api)
    {
        $this->apis[] = $api;
        $api->setCorporateAttributes($this);

        return $this;
    }

    /**
     * Remove apis
     *
     * @param \AppBundle\Entity\CorporateAttributesApi $apis
     */
    public function removeApi(\AppBundle\Entity\CorporateAttributesApi $api)
    {
        $this->apis->removeElement($api);
        $api->setCorporateAttributes(null);
    }

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

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->apis = new \Doctrine\Common\Collections\ArrayCollection();
    }
}

in AppBundle/Entities/CorporateAttributesApi.php:

namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
 * CorporateAttributesApi
 *
 * 
 * @ORM\Entity
 * @ORM\Table("drupal_wiredb_corporate_attributes_api")
 */
class CorporateAttributesApi
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="CorporateAttributes", inversedBy="apis")
     * @ORM\JoinColumn(name="attribute_id", referencedColumnName="id")
     */
    protected $corporate_attributes;

    /**
     * @ORM\Id
     * @ORM\Column(name="group_name", type="string", length=128, options={"default":""})
     */
    protected $group_name = '';

    public function __toString() {
        if (empty($this->corporate_attributes) && empty($this->api_user)) {
            return 'New Corporate Attributes - API User Join';
        }
        else {
            return (string)$this->corporate_attributes . ' | ' . (string)$this->api_user . ' | ' . $this->group_name;
        }
    }

    /**
     * Set group_name
     *
     * @param string $groupName
     * @return CorporateAttributesApi
     */
    public function setGroupName($groupName)
    {
        $this->group_name = $groupName;

        return $this;
    }

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

    /**
     * Set corporate_attributes
     *
     * @param \AppBundle\Entity\CorporateAttributes $corporateAttributes
     * @return CorporateAttributesApi
     */
    public function setCorporateAttributes(\AppBundle\Entity\CorporateAttributes $corporateAttributes)
    {
        $this->corporate_attributes = $corporateAttributes;

        return $this;
    }

    /**
     * Get corporate_attributes
     *
     * @return \AppBundle\Entity\CorporateAttributes 
     */
    public function getCorporateAttributes()
    {
        return $this->corporate_attributes;
    }
}

I would try to modify your AppBundle/Admin/CorporateAttributesApiAdmin.php file in following way:

// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper) {
    $formMapper
        ->add('corporate_attributes', null, ['required' => true])
        ->add('group_name', 'choice', [
            'choices' => ['a', 'b', 'c'],
            'required' => false,
        ])
    ;

    // If this is sonata_type_collection inside CorporateAttributes form,
    // then we don't need 'corporate_attributes' field as it would be the parent entity
    if ($this->getRoot() instanceof \AppBundle\Admin\CorporateAttributesAdmin) {
        $formMapper->remove('corporate_attributes');
    }
}

public function getNewInstance()
{
    $object = parent::getNewInstance();

    // Here we specify the 'corporate_attributes'
    if ($this->getRoot()->getSubject() instanceof \AppBundle\Entity\CorporateAttributes) {
        $object->setCorporateAttributes($this->getRoot()->getSubject());
    }

    return $object;
}

You also may want to try modify AppBundle/Admin/CorporateAttributesAdmin.php to set by_reference=false in form field definition:

// Fields to be shown on create/edit forms
//  AppBundle/Admin/CorporateAttributesAdmin.php
protected function configureFormFields(FormMapper $formMapper) {
    $formMapper
        ->add('apis', 'sonata_type_collection',
            ['required' => false, 'label' => 'API Clients', 'by_reference' => false],
            ['edit'=>'inline','inline'=>'table']
        )
    ;
}

Here is documentation for by_reference option: http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference

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