smoove
smoove

Reputation: 4000

Link in form label using route

In my registration form i have a checkbox "I accept the terms", and want to link the word "terms" to my terms page.

Is there a way to add a link to a form label, using a route? (preferably without injecting the container in the form)

Upvotes: 9

Views: 10690

Answers (7)

Leevi Graham
Leevi Graham

Reputation: 684

My current Symfony v7 Registration Form.

Terms and conditions are added via the help attribute with help_html set to true.

Router is injected in the controller as a Form is a service.

<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;

class RegistrationFormType extends AbstractType
{
    public function __construct(
        private readonly RouterInterface $router
    )
    {

    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, [
                'label' => 'Name',
                'attr' => [
                    'autofocus' => true,
                    'autocomplete' => 'name',
                ],
                'constraints' => [
                    new NotBlank([
                        'message' => 'Please enter a password',
                    ]),
                ],

            ])
            ->add('email', EmailType::class, [
                'label' => 'Email',
                'attr' => [
                    'autocomplete' => 'email',
                ],

            ])
            ->add('plainPassword', PasswordType::class, [
                // instead of being set onto the object directly,
                // this is read and encoded in the controller
                'mapped' => false,
                'label' => 'Password',
                'help' => 'Your password should be at least 6 characters long.',
                '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,
                    ]),
                ],
            ])
            ->add('agreeToTerms', CheckboxType::class, [
                'mapped' => false,
                'label' => 'Agree to our terms and conditions',
                'help' => 'View <a href="' . $this->router->generate('app_terms') . '">terms and conditions</a>.',
                'help_html' => true,
                'constraints' => [
                    new IsTrue([
                        'message' => 'You should agree to our terms and conditions.',
                    ]),
                ],
            ])
            ->add('submit', SubmitType::class, [
                'label' => 'Continue',
                'attr' => [
                    'data-variant' => 'primary',
                    'style' => 'width: 100%;'
                ]
            ]);
    }

    public function getBlockPrefix(): string
    {
        return '';
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

Upvotes: 0

Faraona
Faraona

Reputation: 1700

In Symfony 5.1 there are new form improvements.

HTML contents are allowed in form labels!

HTML contents are escaped by default in form labels for security reasons. The new label_html boolean option allows a form field to include HTML contents in their labels, which is useful to display icons inside buttons, links and some formatting in checkbox/radiobutton labels, etc.


// src/Form/Type/TaskType.php
namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            // ...
            ->add('save', SubmitType::class, [
                'label' => ' Save',
                'label_html' => true,
            ])
        ;
    }
}

In your case you can set form label directly from template and pass the route there.

{{ form_widget(form.acceptTermsAndConditions, {
    label: '<a href="' ~ path("route") ~ '">' ~ "I accept ..."|trans ~ '</a>',
    label_html: true
    })
}}

Upvotes: 8

Maerlyn
Maerlyn

Reputation: 34105

The best way is to overwrite the twig block used to render that specific label.

First, check the form fragment naming section of the docs. Then create a new block in your form template with the the appropriate name. Don't forget to tell twig to use it:

{% form_theme form _self %}

For the next step check the default form_label block.

You'll probably only need a portion of it, something like this (I'm leaving the default block name here):

{% block form_label %}
{% spaceless %}
    <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
        <a href="{{ path("route_for_terms") }}">{{ label|trans({}, translation_domain) }}</a>
    </label>
{% endspaceless %}
{% endblock %}

Upvotes: 4

fdor
fdor

Reputation: 167

As an option, you can do so:

->add('approve', CheckboxType::class, [
    'label' => 'Text part without link',
    'help' => 'And <a href="/x.pdf">download it</a>',
    'help_html' => true,
])

Upvotes: 3

peshis
peshis

Reputation: 27

A very simple way to do it would be

{{ form_widget(form.terms, { 'label': 'I accept the <a href="'~path('route_to_terms')~'">terms and conditions</a>' }) }}

You can also do this if you want to use translation

In your translation file for example messages.en.yml add

terms:
    url: 'I accept the <a href="%url%">terms and conditions</a>'

And in your view add

{{ form_widget(form.terms, { 'label': 'terms.url'|trans({'%url%': path('route_to_terms')}) }) }}

Upvotes: -1

mansim
mansim

Reputation: 727

My solution was another:

form:

$builder
->add(
    'agree_to_rules',
    'checkbox',
    [
        'required' => true,
        'label' => 'i_agree_to'
    ]
);

And html:

<span style="display:inline-block">
    {{ form_widget(form.agree_to_rules) }}
</span>
<span style="display:inline-block">
        <a href="#">rules</a>
</span>

And looks the same :)

Upvotes: 1

totas
totas

Reputation: 10800

As the solution above somehow didn't work for me I solved it using the solution suggested here: https://gist.github.com/marijn/4137467

OK, so here i how I did it:

    {% set terms_link %}<a title="{% trans %}Read the General Terms and Conditions{% endtrans %}" href="{{ path('get_general_terms_and_conditions') }}">{% trans %}General Terms and Conditions{% endtrans %}</a>{% endset %}
{% set general_terms_and_conditions %}{{ 'I have read and accept the %general_terms_and_conditions%.'|trans({ '%general_terms_and_conditions%': terms_link })|raw }}{% endset %}
<div>
{{ form_errors(form.acceptGeneralTermsAndConditions) }}

{{ form_widget(form.acceptGeneralTermsAndConditions) }}
<label for="{{ form.acceptGeneralTermsAndConditions.vars.id }}">{{ general_terms_and_conditions|raw }}</label>
</div>

Upvotes: 6

Related Questions