I'm new to Symfony. I want to do collection of forms.
I'm getging something like:
class SkillType extends AbstractType
public function getName()
{
return 'skills_cfg';
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array(
'label' = 'name'
))
->add('name_short', 'text', array(
'label' => 'short name'
));
}
}
And I get a Form where I want to use collection on that form is before:
class AbsType extends AbstractType
{
private $object;
public function __construct($object)
{
$this->object = $object;
}
public function getName()
{
return '';
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('row', 'collection', array(
'type' => $this->object,
'allow_add' => true,
'prototype' => true,
))
->add('addMore', 'button', array(
'attr' => array(
'value' => 'add more'
),
))
->add('submit', 'submit', array(
'attr' => array(
'value'=>'save',
),
'label' => 'save'
));
}
}
And in my controller i getting forms like that:
class InstallController extends Controller
{
public function indexAction($configPage, Request $request)
{
$skillForm= $this->createForm(new AbsType(new SkillType()));
$SkillConfig=new SkillsCfg();
if($request->isMethod('POST')) {
$skillForm->handleRequest($request);
if ($skillForm->isValid()) {
$em=$this->getDoctrine()->getManager();
$em->persist($SkillConfig);
$em->flush();
}
}
return array(
'form' => $skillForm->createView(),
'page' => $configPage
);
}
The entity and view looks like that:
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=30)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="name_short", type="string", length=10)
*/
private $nameShort;
Of course i get getters and setters.
View:
var emailCount = '{{ form.row|length }}';
jQuery(document).ready(function() {
jQuery('#addMore').click(function(e) {
e.preventDefault();
var emailList = jQuery('#fields-list');
// grab the prototype template
var newWidget = emailList.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, emailCount);
emailCount++;
// create a new list element and add it to the list
var newLi = jQuery('<li></li>').html(newWidget);
newLi.appendTo(emailList);
});
})
</script>
{% endblock javascripts %}
{% block body %}
{{ form_start(form) }}
<ul id="fields-list"
data-prototype="{{ form_widget(form.row.vars.prototype)|e }}">
{% for newRow in form.row %}
<li>
{{ form_errors(newRow) }}
{{ form_widget(newRow) }}
</li>
{% endfor %}
</ul>
{{ form_end(form) }}
{% endblock body %}
And I get an error:
An exception occurred while executing 'INSERT INTO skills_cfg (name,
name_short) VALUES (?, ?)' with params [null, null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name'
cannot be null
Why does it happen? And how do I solve it?
Change the indexAction like this:
public function indexAction($configPage, Request $request)
{
$em=$this->getDoctrine()->getManager();
$skillForm= $this->createForm(new AbsType(new SkillsCfg()));
$skillForm->handleRequest($request);
if ($skillForm->isValid())
{
foreach($form->get('row')->getData() as $row)
{
$em->persist($row);
}
$em->flush();
}
return array(
'form' => $skillForm->createView(),
'page' => $configPage
);
}
If its still not working start debugging. Try to get the data from row and check if this is an array.
Related
I am trying to use custom form rendering for category fields which is child form.
When i use it in normal everything works, but i have to use custom form theme to render it differently
{{ form_widget(form.category, {
attr: {
class: 'category',
},
}) }}
My category form type:
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', EntityType::class, [
'class' => Category::class,
'choice_label' => function (Category $choice, $key, $id) {
return new TranslatableMessage($choice->getName());
},
'label' => false,
'placeholder' => null,
'expanded' => true,
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Category::class,
]);
}
}
In my main twig with form i have
{% form_theme form.category 'form/custome_theme_categories.html.twig' %}
I created twig view in form/custome_theme_categories.html.twig where i can put
{{ dump(foo) }}
and i don't event get the error that is't wrong or any kind of HTML. It doesn't meter what i put in my new view: i get empty HTML in result.
Is custom field theme done otherwise?
I'm trying to create a registration form and add the form to the template for documentation
Controller:
class RegController extends AbstractController
{
#[Route('/register', name: 'app_register')]
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
{
$user = new Admin();
$form = $this->createForm(RegistrationFormType::class, $user);
return $this->render('registration/reg.html.twig', [
'form' => $form->createView()
]);
}
}
Form type:
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class)
->add('plainPassword', PasswordType::class, [
'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' => 4096,
]),
],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Admin::class,
]);
}
}
Template:
{% extends 'base.html.twig' %}
{% block title %}{{ 'Register' | trans }}!{% endblock %}
{% block body %}
<main class="form-signin admin-register" style="max-width: 300px">
{{ form(form) }}
</main>
{% endblock %}
Error:
Twig\Environment::getTemplateClass(): Argument #1 ($name) must be of type string, null given
Question:
It seems to have done everything according to the documentation, why can the following error occur?
My controller acts as if I never click on the submit button.
My controller:
public function listAction(RegionsService $service)
{
$regions = $service->getRegionList();
$editForm = $this->createForm('App\Form\RegionListType');
if ($editForm->isSubmitted())
{
dump('submitted');
die();
}
if ($editForm->isSubmitted() && $editForm->isValid()) {
$task = $editForm->getData();
dump($task);
die();
...
}
return $this->render('parameter/region.list.html.twig', [
'form' => $editForm->createView(),
'regions' => $regions
]);
...
My form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('regionsset', TextType::class, array(
'required' => false))
->add('save', SubmitType::class, array(
'attr' => array('class' => 'save')));
}
My view :
{{ form_start(form, {'action' : path('app_region_list')} ) }}
{{ form_widget(form.regionsset, {'attr': {'class': 'foo'}}) }}
{{ form_widget(form.save, { 'label': 'Save' }) }}
{{ form_end(form) }}
When I click on the submit button, the controller never goes into the first test if ($editForm->isSubmitted())
What did I miss ?
You forgot to handle the request in your form. After creating the form, ($editForm), you have to handle the request as follows:
$editForm->handleRequest($request);
After that, the method isSubmitted() will return true.
Try this code. It will resolve the problem.
use Symfony\Component\HttpFoundation\Request;
.
.
.
public function listAction(Request $request, RegionsService $service )
{
$regions = $service->getRegionList();
$editForm = $this->createForm('App\Form\RegionListType');
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$task = $editForm->getData();
dump($task);
die();
...
}
return $this->render('parameter/region.list.html.twig', [
'form' => $editForm->createView(),
'regions' => $regions
]);
...
like in How to handle Symfony form collection with 500+ items i have got a big symfony form collection. In the mentioned thread someone suggests to use pagination for the collection.
How can i paginate such a form collection with the knpPaginatorBundle?
The form type is like this
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('myType', 'collection', array(
'type' => new MyType(),
))
;
}
My controller generates a form with the collection
$form = $this->createForm(new MyFormCollectionType());
and then i'm trying to use the paginator
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$form['myType'],
$request->get('page', 1),
10
);
I think i'm using the wrong target for paginate($target), whats the correct target in a symfony form collection?
Example on Symfony3 :
Catalog Controller
// I want to paginate products (my collection)
$products = $catalog->getProducts(); // ArrayCollection
// Pagination
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($products, $request->query->getInt('page', 1), 10);
// build the form with catalog data
$form = $this->createForm(CatalogProductType::class, $catalog, array("pagination" => $pagination));
// [...]
// show page
return $this->render('catalogs/products.html.twig', array(
'catalog' => $catalog,
'form' => $form->createView(),
'pagination' => $pagination
));
Catalog Form (CatalogProductType)
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('products', CollectionType::class, array(
'entry_type' => ProductType::class,
'data' => $options['pagination'],
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'label' => false,
'by_reference' => false
))
->add('save', SubmitType::class, array(
'attr' => array('class' => 'button-link save'),
'label' => 'Validate'
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Catalog',
'pagination' => null // Don't forget this !
));
}
My view (products.html.twig)
<div class="navigation">{{ knp_pagination_render(pagination) }}</div>
{% if pagination.getTotalItemCount > 0 %}
{% for widget in form.products.children %}
{# Refers to a Twig macro #}
{{ _self.prototype_product(widget) }}
{% endfor %}
{% else %}
<div class="error">The is no product in this catalog</div>
{% endif %}
My products collection is now paginated.
Solution:
Query all entities of the collection, paginate these and set the data attribute of the collection to the paginated value.
Controller:
$allEntities = $em->getRepository('MyBundle:Entity')->findAll();
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$allEntities,
$request->get('page', 1),
10
);
$form = $this->createForm(new MyFormCollectionType($pagination));
Form type:
private $data;
public function __construct($data) {
$this->data = $data;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('myType', 'collection', array(
'type' => new MyType(),
'data' => $this->data,
))
;
}
I've added an EventSubscriber to a form class so that a single template can be used for both add and edit of an entity. Before adding the subscriber, a form would not throw an error for either add or edit. After adding the subscriber, the following error occurs on editing when the conditions for adding the dateAdded field are met:
Expected argument of type "\DateTime", "array" given
Otherwise, the EventSubscriber appears to perform as expected.
Subscriber
class AddV2FieldsSubscriber implements EventSubscriberInterface {
private $factory;
public function __construct(FormFactoryInterface $factory) {
$this->factory = $factory;
}
public static function getSubscribedEvents() {
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(DataEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
// check if the client object is v1
if (!$data->getId() || $data->getDateAdded()) {
$date = new \DateTime();
$form->add($this->factory->createNamed('dateAdded', 'date', $date, array(
'widget' => 'single_text',
'format' => 'MM/dd/yyyy',
'pattern' => '{{ year }}-{{ month }}-{{ day }}',
'years' => range(Date('Y'), Date('Y') - 5),
'required' => false,
'data' => date_create(),))
);
$form->add($this->factory->createNamed('dob', 'dob_age')
);
$form->add($this->factory->createNamed('sex', 'choice', array(
'choices' => array('Male' => 'Male', 'Female' => 'Female'),
'empty_value' => "Select a gender",
'required' => false))
);
}
}
}
The dateAdded field in its Entity:
/**
* #var \DateTime $dateAdded
*
* #ORM\Column(name="date_added", type="date", nullable=true)
*/
private $dateAdded;
Template snippet
{% if form.dateAdded is defined %}
{% include 'ManaClientBundle:Client:v2.html.twig' %}
{% endif %}
Again, the answer is to pay attention to the third parameter of the createNamed() function. In this case, I had to modify
$form->add($this->factory->createNamed('sex', 'choice', array(...
to read
$form->add($this->factory->createNamed('sex', 'choice', null, array(...