Symfony 3: проблема сохранения встраиваемой коллекции форм: нарушение ограничения целостности: 1048 Столбец 'Arrivage_id' не может быть пустым - PullRequest
0 голосов
/ 06 мая 2018

Я работаю с Symfony 3.4 lts , и у меня возникает проблема с Встроенными формами сбора персистентность.

В этом выпуске требуется Symfony Expert .. Описание где-то кажется длинным, но 80% кода генерируется автоматически с помощью команд Symofny CLI по умолчанию.

Я эксперт по сокращению, так что вы обнаружите, что мое описание простое и педагогическое,

Мои 3 сущности описаны с помощью этого простого UML Diagram Class

enter image description here

Код на французском языке, поэтому Arrival - это Arrivage, продукт - Produit, а промежуточным объектом ArrivalElement - ElementArrivage.

Сущность: Arrivage.php (Прибытие на английском языке)

/**
 * 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

Entity: ElementArrivage (промежуточный объект, созданный для отношения «Многие ко многим» с дополнительным полем)

/**
 * 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.php:

/**
 * 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

Форма: ArrivageType.php:

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

    }/**

Форма: ElementArrivage.php:

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

Итак, моя основная сущность - это прибытие, я должен создать прибытие, а остальное должно остаться на Каскаде, так что мне нужно создать контроллер только для сущности прибытия

ArrivageController:

все еще чисто после генерации грубого учения

Вид:

Я использовал технику документации (прототип), чтобы позволить создать новый 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 будет исправлено на два:

ArrivageController:

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

Ответы [ 2 ]

0 голосов
/ 06 мая 2018

Спасибо @dhrumann за ваше сотрудничество, которое принесло мне большую пользу. Теперь я знаю, что доктрина: generate: crud предоставляет только основной каркас контроллера, а не контроллер, идеально приспособленный к моей ситуации.

Итак, для data_class, который был правильно упомянут в моем коде (автоматически генерируется доктриной: generate: crud)

Что касается вашего последнего раздела кода, я думаю, что это не логично, потому что вы пытаетесь сохранить (ElementsArrivage), который содержит Arrivage_id, перед сохранением (Arrivage), который сам является родителем. Таким образом, Arrivage_id будет нулевым, и это будет причиной проблемы, я думаю. Вы подтверждаете?

Решение, которое я адаптирую: (см. // Добавленный блок)

ArrivageController:

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();
0 голосов
/ 06 мая 2018

Относительно вашего вопроса о том, является ли ручное добавление родителя к ребенку для распознавания двунаправленной ассоциации правильным способом. Да, это так.

Что касается вашего второго вопроса. Кажется, что 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();
}

Когда вам приходится вручную контролировать Доктрину, как это, обычно это признак того, что вы делаете что-то, что находится за пределами того, что он хочет делать, и где он работает хорошо. Возможно, вы захотите изучить реорганизацию сопоставления вашего домена / таблицы.

...