Unable to render form in Symfony 6.2 - php

I've just updated to Symfony 6.2 (6.2.6 to be exact) and now, not sure why, unable to render form.
This is what debugger says:
Object of class Symfony\Component\Form\FormView could not be converted to string
In Symfony 6.2, according to the documentation, it should be possible to pass only FormInterface into render method in Controller. However, in both cases (meaning even using the createView()) method, it's unable to render the form itself. Any ideas where the problem might be?
Controller method:
#[Route(path: '/register', name: 'security-register')]
public function register(Request $request, MailUtil $util): Response
{
$form = $this->createForm(RegistrationForm::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$mail = new Mail();
$mail->setRecpient($form->get('email')->getData());
$mail->setTemplate("TestMail");
$util->sendMail($mail);
}
return $this->render("security/register.html.twig", ['form' => $form->createView()]);
}
Form class:
class RegistrationForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('email', EmailType::class, [
'required' => true
])
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'register.error.password',
'required' => true,
'first_options' => ['label' => 'Password'],
'second_options' => ['label' => 'Repeat password']
])
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => true,
'csrf_field_name' => '_csrf',
'csrf_token_id' => 'user_register'
]);
}
}
Twig:
{% block container %}
<div class="row">
<div class="col s6 offset-s3">
<div class="card-panel blue-grey darken-1" data-darkmode="card-panel blue-grey darken-3">
{% if not app.user %}
<h4 class="white-text center-align ">
Register
</h4>
{{ form_start(form) }}
{{ form.email }}
{{ form_end(form) }}
<div class="white-text center-align">
You are logged in as {{ app.user.userIdentifier }}<br><br><a class="waves-effect waves-light btn" href="{{ path('app_logout') }}">Logout</a>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
Tried to use new, Symfony 6.2 approach as stated in documentation: https://symfony.com/doc/current/forms.html#rendering-forms
And then tried to use the old one with createView() method.
Expected result should be rendered form, however both methods are throwing the same stacktrace.

In twig {{ form.email }} is wrong. This is probably why the error occur.
Switch to {{ form_row(form.email) }} for example

Or you can use widget and label separately.
{{ form_label(form.email) }}
{{ form_widget(form.email) }}

Related

Symfony - How to show a collection of form inputs with a loop in a twig template?

I'm a beginner with PHP and Symfony.
I'm trying to show each values of ring (which is entity) from database and trying to add checkbox at the end of the each ring.
My twig template only shows the checkbox in the first ring.
How do I show all the rings?
This is my controller:
public function testPage(Request $request) {
$ring = $this->ringRepository->customFindAll();
$jewel = $this->ringJewelRepository->customFindAll();
$custom = $this->customRingRepository->customFindAll();
$form = $this->createFormBuilder($ring)
->add('active', CheckboxType::class, [
'label'=>'Selection',
'required'=>false
])
->getForm();
$form->handleRequest($request);
return [
'rings'=>$ring,
'jewels'=>$jewel,
'custom' => $custom,
'form'=>$form->createView()
];
}
This is my Twig template:
{% for ring in rings %}
<div>
<div class="text-center">
<p>Ring Name: {{ ring.ring_name }}</p>
<p>Ring Type: {{ ring.ring_type }}</p>
{{ form_start(form) }}
{{ form_widget(form.active) }}
{{ form_end(form) }}
</div>
<div class="text-center">
Update Ring
</div>
</div>
{% endfor %}
Considering your use-case I think you might be looking for an EntityType, with the 'multiple' option set to true. The syntax would be as below. Make sure to import your Ring class as well.
->add('active', EntityType::class, [
'class' => Ring::class,
'multiple' => true,
'label' => 'Selection',
'required' => false,
'query_builder' => static fn (RingRepository $ringRepository) => $ringRepository->customFindAll(),
]);
You can read more about the EntityType in the symfony docs.

Align multiple form inputs horizontally

I have a form with a collectionType field, this field has 3 inputs and I want to align all 3 of them horizontally. Is it possible to do that in Symfony?
Also: Is it possible to choose the label for each element inside the collectionType?
Here's my form:
class WorkerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Firstname')
->add('Lastname')
->add('tasks', CollectionType::class, [
'label' => 'Tasks',
'entry_type' => TasksType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => false,
'delete_empty' => true,
'attr' => [
'class' => 'collection',
],
])
;
$builder->add('save', SubmitType::class, [
'label' => 'Valider',
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => worker::class,
]);
}
}
Here's my tasks form:
class TasksType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('label')
->add('start')
->add('end')
;
}
and here's my _form.html.twig:
{% block extra_js %}
<script src="{{ asset('jquery.collection.js') }}"></script>
{% endblock %}
{% block body %}
<div class="row">
{%
form_theme form
'jquery.collection.html.twig'
'TaksTemplate.html.twig'
%}
{{ form_start(form) }}
<div class="my-custom-class-for-errors">
{{ form_errors(form) }}
</div>
<div class="col">
{{ form_row(form.Firstname, {'label': 'firstname'}) }}
</div>
<div class="col" >
{{ form_row(form.Lastname, {'label': 'Lastname'}) }}
</div>
<div class="col">
{{ form_row(form.tasks, {'label': 'tasks'}) }}
</div>
</div>
{{ form_end(form) }} </div>
{% endblock %}
{% block script %}
<script type="text/javascript">
$('.collection').collection({
'drag_drop_options': {
'placeholder': null
}
});
</script>
{% endblock %}
Also: is is possible to choose the label for each element inside the collectionType ?
No. you can't have multiple label in symfony collectionType.
I want to align all 3 of them horizontally.
Ask this again with HTML CSS tags

How to keep form data when browsing paginated output?

I have a symfony form that contains a long list of items with checkboxes. I have successfully made knp paginator work and now the form is split into pages. However, when I move between pages I lose the checked/unchecked status of the checkboxes. I am sure that this is probably quite easy but I cannot seem to work it out. I am not very experienced with php yet.
Here is my controller:
/**
* #Route("/addquestions/{quizid}", name="addquestions")
*/
public function add(Request $request, PaginatorInterface $paginator, $quizid)
{
$repository = $this->getDoctrine()->getRepository(Quiz::class);
$quiz = $repository->find($quizid);
$repository = $this->getDoctrine()->getRepository(Question::class);
$questions = $repository->findAll();
// Paginate the results of the query
$pagination = $paginator->paginate($questions, $request->query->getInt('page', 1), 3);
$form = $this->createForm(AddQuizQuestionType::class, $quiz, ["pagination" => $pagination]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$questions = $form->get('question')->getData();
foreach ($questions as $question){
$quizquestion = new QuizQuestion();
$quizquestion->setQuestion($question);
$quiz->addQuizQuestion($quizquestion);
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($quiz);
$entityManager->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('editquiz/addquestions.html.twig', [
'form' => $form->createView(),
'pagination' => $pagination,
]);
}
And my form - it uses the paginated query to display the items with a check box.
class AddQuizQuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('question', EntityType::class, [
'expanded' => true,
'multiple' => true,
'class' => Question::class,
'mapped' => false,
'choices' => $options['pagination'],
])
->add('submit', SubmitType::class, [
'label' => 'Submit',
])
->setMethod('GET')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Quiz::class,
'pagination' => null,
]);
}
}
and my template - it renders the checkboxes with images from the database.
{% extends 'base.html.twig' %}
{% block title %}CreatequizController{% endblock %}
{% block body %}
<h1>Create Quiz</h1>
{{ form_start(form) }}
{% for question in form.question %}
<h5>Question {{ question.vars.value }}</h5>
<div class="border mb-5">
<img src="{{ asset(question.vars.label) }}" alt=""/>
<div class="pl-3">
{{ form_widget(question) }}
</div>
</div>
{% endfor %}
<div class="navigation">{{ knp_pagination_render(pagination) }}</div>
{{ form_end(form) }}
{% endblock %}
I would like it so that when I jump between pages the checkboxes that I have selected remain checked.
Thank you in advance,
Martyn

Access nested form fields in view (Embed a Collection of Forms)

I try to build a huge form in symfony 3 with the use of the CollectionType. I have to define multiple sub-forms, some multiple, some single.
This is my FormType for that:
public function buildRegistrationForm(FormBuilderInterface $builder, array $options) {
$builder->add('userRegistration', CollectionType::class, [
'entry_type' => UserRegistrationType::class,
'entry_options' => ['label' => true],
]);
$builder->add('meters', CollectionType::class, [
'entry_type' => MeterType::class,
'entry_options' => ['label' => true],
'allow_add' => true,
]);
...
}
Now I try to access the CollectionType fields in the view. The code for this is:
{{ form_label(registrationForm.email, null, {'label_attr': {'class': 'form-label'}}) }}
{{ form_widget(registrationForm.email, {'attr': {'class': 'form-control'}}) }}
but I get the error:
Neither the property "email" nor one of the methods "email()", "getemail()"/"isemail()"/"hasemail()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView".
I know that Symfony tries to get the email field directly out of the main form (registrationForm), but I don't know how to access the subform. In the documentation (http://symfony.com/doc/current/form/form_collections.html) it is described that I can simply access the sub form by using registrationForm.userRegistration.email. But this gives me the error:
Neither the property "userRegistration" nor one of the methods ...
How can I access the subfields in the view?
First step is to understand why we want to use collectionType?
In case if you have One-To-Many or Many-To-Many relationship you should use CollectionType.
Example:
/**
* Class Team
*
* #ORM\Entity
* #ORM\Table(name="example_project_team")
*/
class Team
{
// ...
/**
* Unidirectional Many-To-Many
*
* Many Teams has many users accounts.
*
* #var ArrayCollection $users
*
* #ORM\ManyToMany(
* targetEntity="AppBundle\Entity\User",
* cascade={"persist", "remove"},
* orphanRemoval=true
* )
*
* #ORM\JoinTable(name="teams_to_users",
* joinColumns={#ORM\JoinColumn(name="team_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
protected $users;
// ...
}
In this example we have some entity called team. Each team has many users (this is just example related to you). I guess, you have already created User entity.
Imagine that for you have UserRegistrationType for your user entity.
/**
* Class UserRegistrationType
*/
class UserRegistrationType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType', [
'label' => false,
'translation_domain' => 'messages'
])
->add('email', 'Symfony\Component\Form\Extension\Core\Type\TextType', [
'label' => false,
'translation_domain' => 'messages'
])
// ... the other fields
;
}
/**
* #return string
*/
public function getName()
{
return 'app_user_registration_type';
}
/**
* #return null|string
*/
public function getBlockPrefix()
{
return 'app_user_registration';
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
'csrf_protection' => true,
'validation' => true,
));
}
}
Please pay attention !!!
We used here 'data_class' => User::class,
As you can see we use User object in userRegistrationType. It means, that we can use this form for every object, which has field of type User or field with type CollectionType (your case!) but collection of Users!
Our Team entity has field users.
Now, as we have already created the userRegistrationType we can add it to TeamFormType.
public function teamRegistrationFormType(FormBuilderInterface $builder, array $options) {
$builder->add('users', CollectionType::class, [
'entry_type' => UserRegistrationType::class,
'entry_options' => [
'label' => false,
],
'label' => false,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
]);
// ...
}
Finally. Your form in twig:
{# you can add this form in your twig file #}
<div class="box">
{% block team_form %}
{{ form_start(team_form, { 'attr': {'class': 'form-horizontal', 'role' : 'form'}}) }}
<div class="box-body">
<div class="form-group">
{% block users_collection %}
<label for="" class="col-sm-2 control-label">
{{ 'admin.label.phones'|trans }}
</label>
<div class="col-sm-10 users" data-prototype="{{ form_widget(team_form.users.vars.prototype)|e('html_attr') }}">
{% for user in users %}
<div>
{{ form_widget(user) }}
</div>
{% endfor %}
</div>
<span class="help-block">
{{ form_errors(users) }}
</span>
{% endblock users_collection %}
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-danger">
{{ 'admin.button.submit'|trans }}
</button>
</div>
</div>
{{ form_end(team_form) }}
</div>
{% endblock team_form %}
</div>
Here we have strange widget {{ form_widget(user) }}. I guess, you want to edit the style of this widget. You can do it with Symfony built-in form styles. Create file (if you haven't created it yet) path_to_your_project/src/AppBundle/Resources/views/Form/fields.html.twig and add to it the style of your userRegistrationType. Notice, that name of this widget is created from name of you form block prefix and string "_widget" (public function getBlockPrefix())
{% file fields.html.twig %}
{% trans_default_domain 'messages' %}
{% block app_user_registration_widget %}
{% spaceless %}
<div class="form-group" {{ block('widget_container_attributes') }}>
<div class="col-sm-2">
{{ form_widget(form.username, {'attr' : {'class' : 'form-control' }}) }}
</div>
<div class="col-sm-4">
{{ form_widget(form.email, {'attr' : {'class' : 'form-control' }}) }}
</div>
</div>
{% endspaceless %}
{% endblock %}
And of course you will have to set the add and delete buttons. For this purposes I suggest you use official documentation: How to Embed a Collection of Forms or you can use bundle (but not official): ninsuo/symfony-collection

Customize forms Symfony

I'm trying to customize my forms. I have been using
http://symfony.com/doc/current/form/form_customization.html
as reference for theme/form customization, but I'm not quite grasping the concept.
What I currently have
RegistrationForm.php
class UserRegistrationForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, ['type' => PasswordType::class])
->add('IHaveReadAndAcceptTheTermsOfService', CheckboxType::class, array(
'mapped' => false,
'constraints' => new IsTrue(),));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'validation_groups' => ['Default', 'Registration']
]);
}
Register.html.twig
{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Register!</h1>
{{ form_start(form, {'attr':{'class':'boxsize'}}) }}
{{ form_row(form.username) }}
{{ form_row(form.email)}}
{{ form_row(form.plainPassword.first, {
'label': 'Password'
}) }}
{{ form_row(form.plainPassword.second, {
'label': 'Repeat Password'
}) }}
Terms of service
{{ form_row(form.IHaveReadAndAcceptTheTermsOfService) }}
cancel
<button type="submit" class="btn btn-primary" formnovalidate>
Register
</button>
<br></br>
<p>Privacy Policy
{{ form_end(form) }}
</div>
</div>
</div>
{% endblock %}
In CSS
.boxsize {
width: 200px;
}
What I am trying to do.
Make this boxsize the default size when I create a form. As it stands I would have to use class:boxsize for each form I create.

Categories