Symfony多对多表单报错:关联字段类型不匹配求助
解决多商品订单表单的关联错误&实现方案
这个错误一眼就能看出是中间表Bestelregel的实体关联映射搞反了——你把$bestelordernummer字段错误关联到了Artikel实体,但它实际上应该关联到Bestelling订单实体。咱们一步步拆解解决:
1. 修正Bestelregel实体的核心关联
先把Bestelregel的ORM映射改对,它作为中间表,需要同时关联Bestelling和Artikel,并且用这两个关联字段作为复合主键:
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Table(name="bestelregel") * @ORM\Entity */ class Bestelregel { /** * @ORM\Id * @ORM\ManyToOne(targetEntity="Bestelling", inversedBy="bestelregels") * @ORM\JoinColumn(name="bestelordernummer", referencedColumnName="bestelordernummer") */ private $bestelordernummer; // 关联到Bestelling实体 /** * @ORM\Id * @ORM\ManyToOne(targetEntity="Artikel", inversedBy="bestelregels") * @ORM\JoinColumn(name="artikelnummer", referencedColumnName="artikelnummer") */ private $artikelnummer; // 关联到Artikel实体 /** * @ORM\Column(type="integer") */ private $aantal; // 商品数量 // 构造函数、Getter和Setter public function __construct() {} public function getBestelordernummer(): ?Bestelling { return $this->bestelordernummer; } public function setBestelordernummer(?Bestelling $bestelling): self { $this->bestelordernummer = $bestelling; return $this; } public function getArtikelnummer(): ?Artikel { return $this->artikelnummer; } public function setArtikelnummer(?Artikel $artikel): self { $this->artikelnummer = $artikel; return $this; } public function getAantal(): ?int { return $this->aantal; } public function setAantal(int $aantal): self { $this->aantal = $aantal; return $this; } }
2. 完善Bestelling实体的关联集合
Bestelling实体需要包含一个bestelregels集合,用来关联对应的订单行,并且添加添加/删除订单行的方法:
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; /** * @ORM\Table(name="bestelling") * @ORM\Entity */ class Bestelling { // 这里补充你的Bestelling原有字段,比如主键bestelordernummer、订单日期datum、关联客户klant等 /** * @ORM\Column(name="bestelordernummer", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $bestelordernummer; /** * @ORM\OneToMany(targetEntity="Bestelregel", mappedBy="bestelordernummer", cascade={"persist", "remove"}) */ private $bestelregels; public function __construct() { $this->bestelregels = new ArrayCollection(); } // 补充其他字段的Getter/Setter... public function getBestelregels(): \Doctrine\Common\Collections\Collection { return $this->bestelregels; } public function addBestelregel(Bestelregel $bestelregel): self { if (!$this->bestelregels->contains($bestelregel)) { $this->bestelregels[] = $bestelregel; $bestelregel->setBestelordernummer($this); // 自动关联订单 } return $this; } public function removeBestelregel(Bestelregel $bestelregel): self { if ($this->bestelregels->removeElement($bestelregel)) { if ($bestelregel->getBestelordernummer() === $this) { $bestelregel->setBestelordernummer(null); } } return $this; } }
3. 创建支持多商品的表单类型
3.1 单个订单行的表单(BestelregelType)
用来选择商品和填写数量:
<?php namespace AppBundle\Form; use AppBundle\Entity\Artikel; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\IntegerType; class BestelregelType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('artikelnummer', EntityType::class, [ 'class' => Artikel::class, 'choice_label' => function(Artikel $artikel) { return $artikel->getArtikelnummer() . ' - ' . $artikel->getOmschrijving(); }, 'label' => 'Artikel' ]) ->add('aantal', IntegerType::class, [ 'label' => 'Aantal', 'attr' => ['min' => 1] ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => \AppBundle\Entity\Bestelregel::class, ]); } }
3.2 主订单表单(BestellingType)
嵌入订单行集合,支持动态添加/删除:
<?php namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\CollectionType; class BestellingType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder // 这里添加Bestelling的其他字段,比如客户选择、订单日期等 // ->add('klant', EntityType::class, ['class' => Klant::class, 'choice_label' => 'naam']) ->add('bestelregels', CollectionType::class, [ 'entry_type' => BestelregelType::class, 'allow_add' => true, // 允许添加新行 'allow_delete' => true, // 允许删除行 'prototype' => true, // 启用模板生成新行 'by_reference' => false, // 必须设为false,才能触发addBestelregel方法 'label' => 'Bestelregels' ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => \AppBundle\Entity\Bestelling::class, ]); } }
4. 控制器处理表单提交
编写控制器逻辑,处理表单的创建、验证和持久化:
<?php namespace AppBundle\Controller; use AppBundle\Entity\Bestelling; use AppBundle\Form\BestellingType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; class BestellingController extends AbstractController { /** * @Route("/bestelling/nieuw", name="bestelling_nieuw") */ public function nieuwAction(Request $request) { $bestelling = new Bestelling(); // 可选:预先添加一个空订单行,方便用户直接输入 // $bestelling->addBestelregel(new \AppBundle\Entity\Bestelregel()); $form = $this->createForm(BestellingType::class, $bestelling); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $entityManager = $this->getDoctrine()->getManager(); $entityManager->persist($bestelling); $entityManager->flush(); $this->addFlash('success', 'Bestelling is succesvol aangemaakt!'); return $this->redirectToRoute('bestelling_overzicht'); // 替换成你的订单列表路由 } return $this->render('bestelling/nieuw.html.twig', [ 'form' => $form->createView(), ]); } }
5. Twig模板支持动态添加商品行
在模板里添加JS,实现点击按钮动态添加/删除订单行:
{% extends 'base.html.twig' %} {% block body %} <h1>Nieuwe Bestelling</h1> {{ form_start(form) }} {{ form_widget(form) }} <div id="bestelregels-container" class="mt-3"> {% for bestelregel in form.bestelregels %} <div class="bestelregel-item p-2 border mb-2"> {{ form_widget(bestelregel) }} <button type="button" class="remove-btn btn btn-danger ms-2">Verwijder</button> </div> {% endfor %} </div> <button type="button" id="add-bestelregel" class="btn btn-primary mt-2">Voeg Artikel Toe</button> <button type="submit" class="btn btn-success mt-2 ms-2">Opslaan</button> {{ form_end(form) }} <script> // 获取Symfony生成的订单行模板 var prototype = '{{ form_widget(form.bestelregels.vars.prototype)|escape('js') }}'; var container = document.getElementById('bestelregels-container'); var addButton = document.getElementById('add-bestelregel'); var rowCount = {{ form.bestelregels|length }}; // 添加新行的事件 addButton.addEventListener('click', function() { // 替换模板里的__name__占位符为当前行数 var newRow = prototype.replace(/__name__/g, rowCount); container.insertAdjacentHTML('beforeend', `<div class="bestelregel-item p-2 border mb-2">${newRow}<button type="button" class="remove-btn btn btn-danger ms-2">Verwijder</button></div>`); // 给新添加的删除按钮绑定事件 bindRemoveButtons(); rowCount++; }); // 初始化删除按钮事件 bindRemoveButtons(); function bindRemoveButtons() { document.querySelectorAll('.remove-btn').forEach(btn => { btn.addEventListener('click', function() { this.parentElement.remove(); }); }); } </script> {% endblock %}
最后验证
完成以上修改后,先执行Doctrine的 schema 更新命令:
php bin/console doctrine:schema:update --force
然后访问订单创建页面,就能正常添加多个商品到订单里了,之前的类型不匹配错误也会消失。
内容的提问来源于stack exchange,提问作者Mr J.
相关产品推荐
相关产品推荐

