Symfony 渲染一个集合表单类型原型
Posted
技术标签:
【中文标题】Symfony 渲染一个集合表单类型原型【英文标题】:Symfony Render a Collection Form Type Prototype 【发布时间】:2021-10-08 14:51:48 【问题描述】:我尝试使用嵌入集合表单呈现我创建的集合类型原型。 我阅读了 Symonfy、Github 上的文档......但没有成功理解使用块呈现表单的方式。 在这里,我有一个基于 User 实体的表单 (RegistrationFormType),它具有 Adress (AdressFormType) 的嵌入集合表单。 我成功地添加了按钮并在表单上生成了新地址,但我希望我的原型具有与我的第一个地址相同的布局(从数据库中检索)。 你能帮我理解我做错了什么吗?
这是我的 RegistrationFormType
<?php
namespace App\Form;
use App\Entity\User;
//use Doctrine\DBAL\Types\TextType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\CallbackTransformer;
class RegistrationFormType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
$builder
->add('roles',ChoiceType::class,[
'choices'=>[
'Specialist'=>'Role_Specialist',
'Parent'=>'Role_Parent',
'Center'=>'Role_Center',],
'label'=>"Je m'inscris en tant que"])
->add('name')
->add('firstname',TextType::class, [
'label' => 'Firstname',
'row_attr' => [
'id' => 'firstname'
],])
->add('email')
->add('phone',TelType::class)
->add('NISS',TextType::class, [
'label' => 'NISS',
'row_attr' => [
'id' => 'NISS'
]
,
'required'=>'false',
])
->add('BCE',TextType::class, [
'label' => 'BCE',
'row_attr' => [
'id' => 'BCE'
],
'required'=>'false'])
->add('agreeTerms', CheckboxType::class, [
'mapped' => false,
'constraints' => [
new IsTrue([
'message' => 'You should agree to our terms.',
]),
],
])
->add('plainPassword', PasswordType::class, [
// instead of being set onto the object directly,
// this is read and encoded in the controller
'mapped' => false,
'attr' => ['autocomplete' => 'new-password'],
'constraints' => [
new NotBlank([
'message' => 'Please enter a password',
]),
new Length([
'min' => 6,
'minMessage' => 'Your password should be at least limit characters',
// max length allowed by Symfony for security reasons
'max' => 4096,
]),
],
])
//imbrication de adress dans le formulaire user afin de retrouver toutes les adresses qui lui sont référées
->add('adress',CollectionType::class,[
'entry_type' => AdressType::class,
'entry_options' => ['label' => false],
'allow_add' => true,
'block_name' => 'adress'
])
;
// Data transformer
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesArray)
// transform the array to a string
return count($rolesArray)? $rolesArray[0]: null;
,
function ($rolesString)
// transform the string back to an array
return [$rolesString];
));
public function configureOptions(OptionsResolver $resolver)
$resolver->setDefaults([
'data_class' => User::class,
]);
地址类型
<?php
namespace App\Form;
use App\Entity\Adress;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AdressType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
$builder
->add('type',ChoiceType::class,[
'choices' =>[
'Adresse Principale' => 'Principale',
'Adresse Secondaire' => 'Secondaire'
]
])
->add('housenumber',TextType::class,[
'label'=>'N°'
])
->add('additional_info',TextType::class,[
'label'=>'Apt/Etage/...',
'required'=>false
])
->add('street', TextType::class, [
'label' => 'Rue',
'row_attr' => [
'id' => 'Adress2'
],])
->add('postalcode')
->add('city')
->add('region')
->add('country')
->add('latitude', HiddenType::class)
->add('longitude', HiddenType::class)
// ->add('users')
;
public function configureOptions(OptionsResolver $resolver)
$resolver->setDefaults([
'data_class' => Adress::class,
]);
树枝模板
% extends 'base.html.twig' %
% block title %Register% endblock %
% block body %
% for flashError in app.flashes('verify_email_error') %
<div class="alert alert-danger" role="alert"> flashError </div>
% endfor %
<div class="container">
form_start(registrationForm)
<div class="form-group"> form_row(registrationForm.roles) </div>
<div class="form-row">
<div class="form-group col-md-6"> form_row(registrationForm.name) </div>
<div class="form-group col-md-6"> form_row(registrationForm.firstname) </div>
</div>
<div class="form-row">
<div class="form-group col-md-6"> form_row(registrationForm.email) </div>
<div class="form-group col-md-6"> form_row(registrationForm.plainPassword,
label: 'Password'
) </div>
</div>
<div class="form-row">
<div class="form-group col-md-4"> form_row(registrationForm.phone) </div>
<div class="form-group col-md-4"> form_row(registrationForm.NISS, label: 'NISS') </div>
<div class="form-group col-md-4"> form_row(registrationForm.BCE, label: 'BCE') </div>
</div>
# @TODO formatter pour qu'on differencie bien les adresses entre elles par block. Cf block en dessous#
<h3>Adresses</h3>
% block _registration_form_adress_entry_widget %
<div class="adress" data-prototype=" form_widget(registrationForm.adress.vars.prototype)|e('html_attr') ">
% for adress in registrationForm.adress %
<div class="form-group col-md-4"> form_row(adress.type) </div>
<div class="form-row">
<div class="form-group col-md-4"> form_row(adress.street) </div>
<div class="form-group col-md-4"> form_row(adress.housenumber) </div>
<div class="form-group col-md-4"> form_row(adress.additional_info) </div>
</div>
<div class="form-row">
<div class="form-group col-md-3"> form_row(adress.postalcode) </div>
<div class="form-group col-md-3"> form_row(adress.city) </div>
<div class="form-group col-md-3"> form_row(adress.region) </div>
<div class="form-group col-md-3"> form_row(adress.country) </div>
</div>
% endfor %
</div>
<button type="button" class="add_item_link" data-collection-holder-class="adress">Add an adress</button>
form_end(registrationForm)
% endblock %
</div>
<script>
const addFormToCollection = (e) =>
const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);
const item = document.createElement('li');
item.innerHTML = collectionHolder
.dataset
.prototype
.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index++;
;
document
.querySelectorAll('.add_item_link')
.forEach(btn => btn.addEventListener("click", addFormToCollection));
</script>
% endblock %
#% block registration_form_adress_entry_row %#
# <div class="form-row">#
# <div class="form-group col-md-4"> form_row(adress.street) </div>#
# <div class="form-group col-md-4"> form_row(adress.housenumber) </div>#
# <div class="form-group col-md-4"> form_row(adress.additional_info) </div>#
# </div>#
# <div class="form-row">#
# <div class="form-group col-md-3"> form_row(adress.postalcode) </div>#
# <div class="form-group col-md-3"> form_row(adress.city) </div>#
# <div class="form-group col-md-3"> form_row(adress.region) </div>#
# <div class="form-group col-md-3"> form_row(adress.country) </div>#
# </div>#
#% endblock %#
代码渲染
谢谢你:)
【问题讨论】:
【参考方案1】:您可以定义一个宏来呈现表单的现有子字段和原型字符串。
% import _self as formMacros %
% macro address(item) %
... your code formatting the address subform ...
% endmacro %
所以树枝中的这一行:
<div class="adress" data-prototype=" form_widget(registrationForm.adress.vars.prototype)|e('html_attr') ">
会
<div class="adress" data-prototype=" formMacros.address(registrationForm.adress.vars.prototype)|e('html_attr') ">
现有字段也是如此。
【讨论】:
以上是关于Symfony 渲染一个集合表单类型原型的主要内容,如果未能解决你的问题,请参考以下文章