ZF2: Форма с вложенными наборами полей — Doctrine Hydrator пытается добавить новые сущности для всех объектов

У меня есть следующие настройки:

у меня есть 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,
));
}
}

Я покажу вам два FieldSets, один для продукта и один для связанной сущности:

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 пытается добавить все связанные сущности в качестве новой сущности и не находит существующую сущность для связи на основе введенного мной имени.

Кто-нибудь может мне помочь?

— редактировать

Так что на самом деле у меня нет удостоверения личности связанных сущностей. Код контроллера выглядит так:

Привет, значит, идентификатор отсутствует, потому что я использую имя для идентификации связанного объекта. Мой контроллер выглядит так:

// 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
));
}

Должен ли я получить соответствующие идентификаторы в контроллере или есть более тонкий способ сделать это?
В официальном примере https://github.com/doctrine/DoctrineModule/blob/master/docs/hydrator.md#a-complete-example-using-zendform никакие идентификаторы не получены в коде контроллера.

0

Решение

Я не могу полностью выяснить, какие поля / данные вы передаете из огромного количества кода, который вы разместили здесь, но чтобы сделать 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 не будет собирать никакие объекты, используя поля идентификатора. Поэтому, если вы передадите какие-либо поля, кроме идентификатора, это предполагает, что вы хотите создать новую ассоциацию с новой сущностью, и будет использовать данные, которые вы передаете, для гидратации новой связанной сущности.

Если передача полей идентификатора — это то, что вы делаете, и это не работает, то, пожалуйста, объясните подробно, какие данные вы точно передаете …

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]