我有一个实体Product。我创建了一个包含以下字段的表单ProductType:
<<ul>我想创建一个集合,允许用户一次创建和提交多个产品。因此,我创建了一个新的表单,它没有实体Products类型。这个表单包含一个字段:
- 产品它是一个CollectionType类,条目类型为ProductType。
在我的模板中,我使用了一个原型,Javascript可以完美地创建它。但是,在提交时,我的所有条目都不会在DB中持久化。我花了几个小时终于找到了一些鼓舞人心的东西,但仍然不起作用:Symfony:从Controller
中的CollectionType访问未映射的表单字段你看到我的错误(在控制器我猜)吗?
ProductController
//[...]
class ProductController extends AbstractController
{
/**
* @Route("/product", name="product")
*/
public function index(): Response
{
$request = new Request();
$formProduct = $this->createForm('AppFormProductsType');
$product = new Product();
$formProduct->handleRequest($request);
if ($formProduct->isSubmitted() && $formProduct->isValid()) {
foreach ($formProduct->get('products') as $formChild)
{
$product->setName($formChild->get('name')->getData()); // That's it!
$product->setPrice($formChild->get('price')->getData());
$product->setReference($formChild->get('reference')->getData());
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($product);
$entityManager->flush();
}
return $this->redirectToRoute('task_success');
}
return $this->render('product/index.html.twig', [
'formProduct' => $formProduct->createView(),
]);
}
}
ProductType
//[...]
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('price')
->add('reference')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Product::class,
]);
}
}
ProductsType
//[...]
class ProductsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('products', CollectionType::class, [
'entry_type' => ProductType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}
产品模板(嫩枝)
{% extends 'base.html.twig' %}
{% block title %}Hello ProductController!{% endblock %}
{% block body %}
{{ form_start(formProduct) }}
{# store the prototype on the data-prototype attribute #}
<ul id="products-fields-list"
data-prototype="{{ form_widget(formProduct.products.vars.prototype)|e }}"
data-widget-tags="{{ '<li></li>'|e }}"
data-widget-counter="{{ formProduct.products|length }}">
{% for products in formProduct.products %}
<li>
{{ form_row(products) }}
</li>
{% endfor %}
</ul>
<input type="submit" value="Submit">
{{ form_end(formProduct) }}
<button type="button"
class="add-another-collection-widget"
data-list-selector="#products-fields-list">Add another email</button>
<script>
// add-collection-widget.js
jQuery(document).ready(function () {
jQuery('.add-another-collection-widget').click(function (e) {
var list = jQuery(jQuery(this).attr('data-list-selector'));
// Try to find the counter of the list or use the length of the list
var counter = list.data('widget-counter') || list.children().length;
// grab the prototype template
var newWidget = list.attr('data-prototype');
// replace the "__name__" used in the id and name of the prototype
// with a number that's unique to your emails
// end name attribute looks like name="contact[emails][2]"
newWidget = newWidget.replace(/__name__/g, counter);
// Increase the counter
counter++;
// And store it, the length cannot be used if deleting widgets is allowed
list.data('widget-counter', counter);
// create a new list element and add it to the list
var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
newElem.appendTo(list);
});
});
</script>
{% endblock %}
希望你能看到我错过的东西。谢谢你:)
你的控制器方法有几个问题。主要的问题是你对$request = new Request();
的使用。这是在创建一个新的Request
,而不是客户端发送的请求,因此在该空请求中没有提交表单数据。请求应该作为一个方法参数注入,这样自动连接将从来自客户端的当前http请求构建Request
。
https://symfony.com/doc/current/forms.html处理表单
在进行该更正之后,您的代码应该得到一个与表单中最后一个的值保持一致的Product
,因为您只创建一个新的Product
并在每次循环中为其设置新值。请参阅下面代码示例中的注释:
use SymfonyComponentHttpFoundationRequest;
class ProductController extends AbstractController
{
/**
* @Route("/product", name="product")
*/
public function index(Request $request): Response
{
// line below would create empty Request
// $request = new Request();
$formProduct = $this->createForm('AppFormProductsType');
// line below would create only ONE Product that would be overwritten in each iteration
// $product = new Product();
$formProduct->handleRequest($request);
if ($formProduct->isSubmitted() && $formProduct->isValid()) {
// you only need to fetch the entity manager once
$entityManager = $this->getDoctrine()->getManager();
foreach ($formProduct->get('products') as $formChild)
{
// create a new Product foreach $formChild
$product = new Product();
$product->setName($formChild->get('name')->getData());
$product->setPrice($formChild->get('price')->getData());
$product->setReference($formChild->get('reference')->getData());
// you only need to fetch the entity manager once
// $entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($product);
$entityManager->flush();
}
return $this->redirectToRoute('task_success');
}
return $this->render('product/index.html.twig', [
'formProduct' => $formProduct->createView(),
]);
}
}