Symfony 3.4嵌入表单集合持久化报错:arrivage_id字段不能为空
解决Symfony 3.4嵌入表单集合持久化时的Integrity Constraint Violation错误
这个错误我在Symfony 3.4处理关联表单时遇到过好几次,核心问题就是中间关联实体的arrivage_id字段没有被正确赋值,触发了数据库的非空约束校验。下面一步步帮你排查解决:
1. 检查实体双向关联配置
首先要确保三个实体之间的关联关系配置正确,尤其是双向关联的绑定逻辑——这是最容易出问题的地方。
Arrivage实体(对应你的Arrival)
确保在addArrivageProduit方法里,主动把当前Arrivage对象绑定到中间实体上:
// src/AppBundle/Entity/Arrivage.php namespace AppBundle\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity */ class Arrivage { // ... 其他字段和getter/setter /** * @ORM\OneToMany(targetEntity="ArrivageProduit", mappedBy="arrivage", cascade={"persist", "remove"}) */ private $arrivageProduits; public function __construct() { $this->arrivageProduits = new ArrayCollection(); } // 关键:添加中间实体时,必须设置双向关联 public function addArrivageProduit(ArrivageProduit $arrivageProduit) { $arrivageProduit->setArrivage($this); // 这行是核心,不能漏! $this->arrivageProduits->add($arrivageProduit); return $this; } public function removeArrivageProduit(ArrivageProduit $arrivageProduit) { $this->arrivageProduits->removeElement($arrivageProduit); } // ... 其他方法 }
中间实体ArrivageProduit
确保arrivage字段的注解配置正确,且非空约束生效:
// src/AppBundle/Entity/ArrivageProduit.php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity */ class ArrivageProduit { // ... 主键字段(比如id) /** * @ORM\ManyToOne(targetEntity="Arrivage", inversedBy="arrivageProduits") * @ORM\JoinColumn(name="arrivage_id", referencedColumnName="id", nullable=false) */ private $arrivage; /** * @ORM\ManyToOne(targetEntity="Produit", inversedBy="arrivageProduits") * @ORM\JoinColumn(name="produit_id", referencedColumnName="id", nullable=false) */ private $produit; // ... 其他字段(比如数量quantite) // 对应的getter和setter public function setArrivage(Arrivage $arrivage): self { $this->arrivage = $arrivage; return $this; } public function getArrivage(): ?Arrivage { return $this->arrivage; } // ... Produit的getter/setter }
2. 检查表单类型配置
在Arrivage的表单类型里,嵌入集合时必须设置by_reference为false——这个选项告诉Symfony不要直接修改集合,而是调用你的addArrivageProduit方法,这样才能触发我们刚才写的双向关联绑定逻辑。
// src/AppBundle/Form/ArrivageType.php namespace AppBundle\Form; use AppBundle\Entity\Arrivage; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\CollectionType; class ArrivageType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder // ... Arrivage的其他字段(比如日期dateArrivage) ->add('arrivageProduits', CollectionType::class, [ 'entry_type' => ArrivageProduitType::class, 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false, // 必须设置为false! 'label' => false, ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => Arrivage::class, ]); } }
中间实体的表单类型(ArrivageProduitType)
这里只需要关联Produit实体即可,不需要添加Arrivage字段——因为我们已经通过addArrivageProduit方法自动绑定了:
// src/AppBundle/Form/ArrivageProduitType.php namespace AppBundle\Form; use AppBundle\Entity\ArrivageProduit; use AppBundle\Entity\Produit; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Bridge\Doctrine\Form\Type\EntityType; class ArrivageProduitType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('produit', EntityType::class, [ 'class' => Produit::class, 'choice_label' => 'nom', // 替换成你的Produit实体里用于显示的字段 'placeholder' => 'Choisir un produit', ]) ->add('quantite'); // 如果你有数量字段的话 } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => ArrivageProduit::class, ]); } }
3. 检查控制器处理逻辑
确保控制器里正确实例化Arrivage对象,绑定表单后直接持久化即可——不需要手动处理中间实体,因为我们配置了cascade={"persist"}:
// src/AppBundle/Controller/ArrivageController.php namespace AppBundle\Controller; use AppBundle\Entity\Arrivage; use AppBundle\Form\ArrivageType; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; class ArrivageController extends Controller { /** * @Route("/arrivage/new", name="arrivage_new") */ public function newAction(Request $request) { $arrivage = new Arrivage(); $form = $this->createForm(ArrivageType::class, $arrivage); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($arrivage); // 只需要持久化Arrivage,中间实体会被级联持久化 $em->flush(); return $this->redirectToRoute('arrivage_show', ['id' => $arrivage->getId()]); } return $this->render('arrivage/new.html.twig', [ 'arrivage' => $arrivage, 'form' => $form->createView(), ]); } }
最后验证
做完以上配置后,重新提交表单应该就能解决arrivage_id cannot be null的错误了。核心就是保证双向关联的绑定逻辑被触发,而by_reference: false和add方法里的setArrivage($this)是实现这个的关键。
内容的提问来源于stack exchange,提问作者Med Karim Garali
相关产品推荐
相关产品推荐

