Я работаю с Symfony 3.4 литра, и у меня проблема с постоянством Встроенные формы сбора.
Эта проблема требует Symfony Эксперт. Описание кажется длинным, но это 80% кода, автоматически сгенерированного командами Symofny CLI по умолчанию.
Я эксперт по сокращению, поэтому вы обнаружите, что мое описание простое и педагогическое,
Мои 3 объекта описаны с помощью этого простого Класс диаграммы UML
Код на французском языке, поэтому Arrival - это Arrivage, product - это Produit, а промежуточный объект ArrivalElement - это ElementArrivage.
/**
* Arrivage
*
* @ORM\Table(name = "arrivage")
* @ORM\Entity(repositoryClass = "AppBundle\Repository\ArrivageRepository")
*/
class Arrivage
{
/**
* @var int
*
* @ORM\Column(name = "id", type = "integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy = "AUTO")
*/
private $id;
/**
* @var \DateTime
*
* @ORM\Column(name = "dateCreation", type = "datetime")
*/
private $dateCreation;
/**
* @var Arrivage
* @ORM\OneToMany(targetEntity = "ElementArrivage", mappedBy = "arrivage", cascade = {"persist", "remove"}, orphanRemoval=TRUE)
*/
private $elementArrivages;
public function __construct() {
$this->dateCreation = new \DateTime();
$this->elementArrivages = new ArrayCollection();
}
// bin/console doctrine:generate:entities AppBundle:Arrivage => OK
/**
* ElementArrivage
*
* @ORM\Table(name = "element_arrivage")
* @ORM\Entity(repositoryClass = "AppBundle\Repository\ElementArrivageRepository")
*/
class ElementArrivage
{
/**
* @var int
*
* @ORM\Column(name = "id", type = "integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy = "AUTO")
*/
private $id;
/**
* @var Arrivage
*
* @ORM\ManyToOne(targetEntity = "Arrivage", inversedBy = "elementArrivages")
* @ORM\JoinColumn(name = "arrivage_id", referencedColumnName = "id", nullable=FALSE)
*/
private $arrivage;
/**
* @var Produit
*
* @ORM\ManyToOne(targetEntity = "Produit", inversedBy = "elementArrivages")
* @ORM\JoinColumn(name = "produit_id", referencedColumnName = "id", nullable=FALSE)
*/
private $produit;
/**
* @var int
*
* @ORM\Column(name = "quantite", type = "integer")
*/
private $quantite;
/**
* @var string
*
* @ORM\Column(name = "prix_achat", type = "decimal", precision=10, scale=3)
*/
private $prixAchat;
// bin/console doctrine:generate:entities => OK
/**
* Produit
*
* @ORM\Table(name = "produit")
* @ORM\Entity(repositoryClass = "AppBundle\Repository\ProduitRepository")
*/
class Produit
{
/**
* @var int
*
* @ORM\Column(name = "id", type = "integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy = "AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name = "name", type = "string", length=255, unique=true)
*/
private $name;
/**
* @var Arrivage
* @ORM\OneToMany(targetEntity = "ElementArrivage", mappedBy = "produit", cascade = {"persist", "remove"}, orphanRemoval=TRUE)
*/
private $elementArrivages;
// bin/console doctrine:generate:entities => OK
class ArrivageType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('elementArrivages', CollectionType::class, array(
'entry_type' => ElementArrivageType::class,
'allow_add' => true,
'allow_delete' => true,
'label'=> false,
'entry_options' => array(
'label' => false
)
))
;
}/**
class ElementArrivageType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('quantite')
->add('prixAchat')
->add('produit', EntityType::class, array(
// looks for choices from this entity
'class' => 'AppBundle:Produit',
// uses the User.username property as the visible option string
'choice_label' => 'name',
// used to render a select box, check boxes or radios
// 'multiple' => true,
// 'expanded' => true,
));
;
}/*
Итак, моя основная сущность - это прибытие, я должен создать прибытие, и он должен сохранить остальное на каскаде, поэтому мне нужно создать контроллер только для объекта прибытия.
он все еще чистый после генерации грязи доктриной
Я использовал технику документации (прототип), чтобы создать новый elementArrivage
Док: https://symfony.com/doc/3.4/form/form_collections.html#allowing-new-tags-with-the-prototype
Демо javascript: http://jsfiddle.net/847Kf/4/
С этой конфигурацией При попытке создать новое прибытие у меня возникает первая проблема с постоянством:
An exception occurred while executing 'INSERT INTO element_arrivage (quantite, prix_achat, prixVente, arrivage_id, produit_id) VALUES (?, ?, ?, ?, ?)' with params [1, 1, 1, null, 11]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'arrivage_id' cannot be null
Я решил эту проблему, добавив строку в Entity Arrivage в методе:
public function addElementArrivage(\AppBundle\Entity\ElementArrivage $elementArrivage)
{
$elementArrivage->setArrivage($this); // Added Liiiiiiiiiiiiiiiiiiine
$this->elementArrivages[] = $elementArrivage;
return $this;
}
Первый вопрос: правильное ли это решение ???
После решения первой проблемы я получаю вторую проблему:
An exception occurred while executing 'INSERT INTO element_arrivage (quantite, prix_achat, prixVente, arrivage_id, produit_id) VALUES (?, ?, ?, ?, ?)' with params [1, 1, 1, null, 11]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'arrivage_id' cannot be null
Второй вопрос: я понятия не имею о решении, но когда я попытался изменить контроллер с помощью этой строки, проблема исчезла, но это не решение, потому что в этом случае ElementArrivage будет зафиксировано на двух:
/**
* Creates a new arrivage entity.
*
* @Route("/new", name = "arrivage_new")
* @Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$arrivage = new Arrivage();
$elementArrivage = new ElementArrivage(); //Added Liiiiines begin
$elementArrivage2 = new ElementArrivage();
$arrivage->addElementArrivage($elementArrivage);
$arrivage->addElementArrivage($elementArrivage2); // Added Liiiines end
$form = $this->createForm('AppBundle\Form\ArrivageType', $arrivage);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($arrivage);
$em->flush();
return $this->redirectToRoute('arrivage_show', array('id' => $arrivage->getId()));
}
return $this->render('arrivage/new.html.twig', array(
'arrivage' => $arrivage,
'form' => $form->createView(),
));
}




Что касается вашего вопроса о том, является ли добавление родителя к дочернему вручную для распознавания двунаправленной ассоциации правильным способом. Да, это.
По поводу вашего второго вопроса. Похоже, что Doctrine не может сказать, что ей необходимо сохранить поступления, прежде чем она сохранит элементы прибытия, что приводит к проблеме с нулевым идентификатором, с которой вы столкнулись. Вы можете решить эту проблему, указав data_class ваших форм, как показано ниже, чтобы убедиться, что ваши формы возвращают правильные элементы:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => ElementArrivage::class,
));
}
Даже в этом случае Doctrine может не управлять вашими объектами, поскольку вы создавали их вручную. Поскольку Symfony Form пытается использовать геттеры и сеттеры вашего класса, когда у вашего класса Arrivage есть такой сеттер, он должен распознавать сущности и также сохранять их:
public function setElementArrivages($elements)
{
foreach($elements as $element) {
$this->addElementArrivage($element); // the method from your example
}
}
Возможно, вам также придется продолжать работу без указания объекта, поэтому просто $em->persist(); вместо $em->persist($arrivage); в вашем контроллере;
Если все это не работает, попробуйте использовать xdebug, чтобы увидеть, находятся ли элементы в единице работы (uow) диспетчера сущностей. Если нет, вы всегда можете сохранить их вручную:
if ($form->isSubmitted() && $form->isValid()) {
$elements = $arrivage->getElementArrivages();
foreach ($elements as $element) {
$em->persist($element);
}
$em->persist($arrivage);
$em->flush();
}
Когда вам приходится вручную управлять Doctrine таким образом, это обычно является признаком того, что вы делаете что-то, выходящее за рамки того, что она хочет делать и где она работает хорошо. Возможно, вы захотите реорганизовать сопоставление домена / таблицы.
Спасибо @dhrumann за ваше сотрудничество, которое было очень полезным для меня. Теперь я знаю, что doctrine: generate: crud предоставляет только первичный каркас контроллера, а не контроллер, идеально адаптированный к моей ситуации.
Итак, для data_class, который был правильно упомянут в моем коде (автоматически генерируется doctrine: generate: crud)
Что касается вашего последнего раздела кода, я думаю, что это не логика, потому что вы пытаетесь сохранить (ElementsArrivage), который содержит arrivage_id, перед сохранением (Arrivage), который является родителем самого себя. Таким образом, arrivage_id будет нулевым, и я думаю, это вызовет проблему. Вы подтверждаете?
Решение, которое я адаптирую: (см. // Добавленный блок)
public function newAction(Request $request){
$arrivage = new Arrivage();
$form = $this->createForm('AppBundle\Form\ArrivageType', $arrivage);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//Added Block Begiiiiiiin
$elementArrivages = $arrivage->getElementArrivages();
foreach( $elementArrivages as $elementArrivage){
$elementArrivage->setArrivage($arrivage);
}
//Added Block Ennnnnd
$em = $this->getDoctrine()->getManager();
$em->persist($arrivage);
$em->flush();