ZF2:具有嵌套Fieldsets的表单 - Doctrine Hydrator尝试为所有对象添加新实体

时间:2015-04-07 19:37:13

标签: php forms doctrine-orm zend-framework2 fieldset

我有以下设置:

我有一个Product Doctrine对象和一些相关的Doctrine实体。

/**
* Product
*
* @ORM\Table(name="products")
* @ORM\Entity(repositoryClass="\Entities\ProductRepository")
*/

class Product
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="artist", type="string", length=100, precision=0, scale=0, nullable=false, unique=false)
 */
private $artist;

/**
 * @var string
 *
 * @ORM\Column(name="title", type="string", length=100, precision=0, scale=0, nullable=false, unique=false)
 */
private $title;

/**
 * @var string
 *
 * @ORM\Column(name="subtitle", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
 */
private $subtitle;

/**
 * @var string
 *
 * @ORM\Column(name="labelcode", type="string", length=100, precision=0, scale=0, nullable=false, unique=false)
 */
private $labelcode;

/**
 * @var string
 *
 * @ORM\Column(name="description", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
 */
private $description;

/**
 * @var string
 *
 * @ORM\Column(name="hints", type="text", precision=0, scale=0, nullable=true, unique=false)
 */
private $hints;

/**
 * @var string
 *
 * @ORM\Column(name="price", type="decimal", precision=0, scale=2, nullable=false, unique=false)
 */
private $price;

/**
 * @var integer
 *
 * @ORM\Column(name="amount", type="integer", precision=0, scale=0, nullable=true, options={"default" = 0}, unique=false)
 */
private $amount;

/**
 * @var string
 *
 * @ORM\Column(name="image", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
 */
private $image;

/**
 * @var \DateTime
 *
 * @ORM\Column(name="instockdate", type="date", precision=0, scale=0, nullable=true, unique=false)
 */
private $instockdate;

/**
 * @var boolean
 *
 * @ORM\Column(name="ownrelease", type="boolean", precision=0, scale=0, nullable=true, columnDefinition="BOOLEAN DEFAULT FALSE", unique=false)
 */
private $ownrelease;

/**
 * @var \Entities\Label
 *
 * @ORM\ManyToOne(targetEntity="Entities\Label", inversedBy="products")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="label_id", referencedColumnName="id", nullable=false)
 * })
 */
private $label;

/**
 * @var \Entities\Genre
 *
 * @ORM\ManyToOne(targetEntity="Entities\Genre", inversedBy="products"))
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="genre_id", referencedColumnName="id", nullable=false)
 * })
 */
private $genre;

/**
 * @var \Entities\Type
 *
 * @ORM\ManyToOne(targetEntity="Entities\Type")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="type_id", referencedColumnName="id", nullable=false)
 * })
 */
private $type;

/**
 * @var \Entities\Webshop
 *
 * @ORM\ManyToOne(targetEntity="Entities\Webshop", inversedBy="products")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="webshop_id", referencedColumnName="id", nullable=true)
 * })
 */
private $webshop;

/**
 * @var \Entities\Supplier
 *
 * @ORM\ManyToOne(targetEntity="Entities\Supplier")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="supplier_id", referencedColumnName="id", nullable=true)
 * })
 */
private $supplier;

/**  Getters and Setters **/
}

我有CreateProductForm

class CreateProductForm extends Form implements InputFilterProviderInterface
{

protected $objectManager;

public function __construct(ObjectManager $objectManager)
{
    parent::__construct('create-product-form');
    $this->setObjectManager($objectManager);

    $this->setHydrator(new DoctrineHydrator($this->getObjectManager()));

    $this->addElements();
}

public function addElements()
{
    // Add the product fieldset, and set it as the base fieldset
    $productFieldset = new ProductFieldSet($this->getObjectManager());
    $productFieldset->setUseAsBaseFieldset(true);
    $this->add($productFieldset);

    // Add a checkbox allowing creation of another \Entities\Product
    $this->add(array(
        'type' => 'Zend\Form\Element\Checkbox',
        'name' => 'create_another_product',
        'attributes' => array(
            'required' => FALSE,
            'allow_empty' => TRUE,
        ),
        'options' => array(
            'label' => 'Create another',
            'use_hidden_element' => TRUE,
            'checked_value' => TRUE,
            'unchecked_value' => FALSE,
            'label_attributes' => array(
                'class' => 'control-label'
            ),
        ),
    ));

    // … add CSRF and submit elements …
    $this->add(array(
        'type' => 'Zend\Form\Element\Csrf',
        'name' => 'create_product_csrf',
        'options' => array(
            'csrf_options' => array(
                'timeout' => 600
            )
        )
    ));

    $this->add(array(
        'name' => 'submit',
        'type' => 'Submit',
        'attributes' => array(
            'value' => 'Go',
            'id' => 'submitbutton',
            'class' => 'btn btn-primary'
        ),
    ));
}

/**
 * Get objectManager
 * 
 * @return type 
 */
public function getObjectManager()
{
    return $this->objectManager;
}

/**
 * Set objectManager
 * 
 * @param ObjectManager $objectManager
 * @return \Cms\Form\ProductForm
 */
public function setObjectManager(ObjectManager $objectManager)
{
    $this->objectManager = $objectManager;

    return $this;
}

/**
 * Should return an array specification compatible with
 * {@link Zend\InputFilter\Factory::createInputFilter()}.
 *
 * @return array
 */
public function getInputFilterSpecification()
{
    return array(
        'create_another_product' => array(
            'required' => FALSE,
            'allow_empty' => TRUE,
    ));
}
}

我将向您展示两个FieldSet,一个用于产品,一个用于相关实体:

class ProductFieldSet extends Fieldset implements InputFilterProviderInterface
{

/**
 * @var ObjectManager
 *
 */
protected $objectManager;

/**
 * @var InputFilter
 *
 */
protected $inputFilter;

public function __construct(ObjectManager $objectManager)
{
    parent::__construct('product');

    $this->setObjectManager($objectManager);


    $this->setHydrator(new DoctrineEntity($this->getObjectManager()))->setObject(new Product());

    $this->addElements();
}

public function addElements()
{
    $this->add(array(
        'name' => 'id',
        'type' => 'Hidden',
    ));

    $this->addLabelCode();
    $this->addArtist();
    $this->addTitle();
    $this->addDescription();
    $this->addPrice();
    $this->addHints();

    $this->add(array(
        'name' => 'subtitle',
        'type' => 'Text',
        'options' => array(
            'label' => 'Subtitle',
        ),
        'attributes' => array(
            'class' => 'form-control',
        ),
    ));


    $this->add(array(
        'name' => 'amount',
        'type' => 'Text',
        'options' => array(
            'label' => 'Amount',
        ),
    ));

    $date = new Date('instockdate');
    $date
            ->setLabel('In Stock Date')
            ->setAttributes(array(
                'min' => '2012-01-01',
                'max' => '2200-01-01',
                'step' => '1', // days; default step interval is 1 day
    ));



    $this->addGenre();
    $this->addLabel();
    $this->addType();
}
// A lot of add methods

 /**
 * Add Type to the Cms\Form\ProductFieldSet
 */
private function addType()
{
    $typeFieldset = new TypeFieldSet($this->getObjectManager());
    $typeFieldset->setName('type');
   //$labelFieldset->setAttribute('required', TRUE);
   //$labelFieldset->setAttribute('class', 'form-control');

    $typeFieldset->setLabel('Type');
   //$labelFieldset->setLabelAttributes(array('class' => 'control-label'));

    $this->add($typeFieldset);
}
}

现在是TypeFieldSet

class TypeFieldSet extends Fieldset implements InputFilterProviderInterface
{

/**
 * @var ObjectManager
 *
 */
protected $objectManager;

/**
 * @var InputFilter
 *
 */
protected $inputFilter;

/**
 * Construct Cms\Form\GenreFieldSet.
 * 
 * @param ObjectManager $objectManager
 */
public function __construct(ObjectManager $objectManager)
{
    parent::__construct('type');

    $this->setObjectManager($objectManager);

    $this->setHydrator(new DoctrineEntity($this->getObjectManager()))->setObject(new Type());

    $this->addElements();
}

/**
 * Method responsible for adding elements to \Cms\Form\Fieldset.
 */
public function addElements()
{
    $this->add(array(
        'name' => 'name',
        'type' => 'Zend\Form\Element\Text',
        'attributes' => array(
            'required' => true,
            'id' => 'name',
            'class' => 'form-control',
            'placeholder' => 'Enter type name'
        ),
        'options' => array(
            'label' => 'Name',
            'label_attributes' => array(
                'class' => 'control-label'
            ),

        ),
    ));
}
// Additional methods
}

不知何故,Doctrine尝试将所有相关实体添加为新实体,而不是根据我输入的名称找到要关联的现有实体。

有人可以帮帮我吗?

- 编辑

所以我确实没有相关实体的身份证明。控制器代码如下所示:

嗨,因此缺少ID,因为我使用该名称来识别相关对象。我的控制器看起来像这样:

// Get your ObjectManager from the ServiceManager
  $objectManager = $this->getServiceLocator()->get('Doctrine\ORM 
  \EntityManager');

    // Create the form and inject the ObjectManager
    $form = new CreateProductForm($objectManager);

    // Create a new, empty entity and bind it to the form
    $product = new Product();
    $form->bind($product);

    if ($this->request->isPost())
    {
        $form->setData($this->request->getPost());

        if ($form->isValid())
        {

            $this->getProductService()->saveProduct($product);

            $this->flashMessenger()->addSuccessMessage('Product ' . $product->getName() . ' saved');

                return $this->redirect()->toRoute('product');
        }
        else
        {
            $this->flashMessenger()->addErrorMessage($form->getMessages());
        }
    }

    return new ViewModel(array(
        'product' => $product,
        'form' => $form
    ));
}

我应该在控制器中获取相关ID还是有更微妙的方法来执行此操作? 在官方示例中https://github.com/doctrine/DoctrineModule/blob/master/docs/hydrator.md#a-complete-example-using-zendform在控制器代码中没有获得任何ID。

1 个答案:

答案 0 :(得分:1)

我无法完全弄清楚你在这里发布的大量代码中传递了哪些字段/数据,但要使DoctrineObject水合器找到并使用数据库中的现有实体,然后使用这些实体进行水合作用您需要传递与关联对象的标识符对应的字段的新对象。

因此,假设您要创建一个包含现有Product的新Genre,这意味着在数据中传递genre_id。所以像这样:

$hydrator = new DoctrineObject($objectManager);

$data = array(
    "genre" => array(
        "genre_id" => 1
    )
);

$product = new Product();

$hydrator->hydrate($data, $product);

在这个非常基本的代码示例中,保湿器将识别来自Genre实体的标识符字段,并在保湿新Product之前使用它来收集它。因此,如果您在数据集中传递标识符字段,它应该可以正常工作。

DoctrineObject不会使用除标识符字段之外的任何实体。因此,如果您传递除标识符字段之外的任何内容,则假定您希望与新实体建立新关联,并且它将使用您传递的数据来为新关联实体提供水合。

如果传递标识符字段是你正在做的而且它不起作用,那么请详细说明你正在传递的数据......