Skip to content

[Form] Add constraints_from_* options #50459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from

Conversation

tasiot
Copy link

@tasiot tasiot commented May 28, 2023

Apply constraints to field from an entity not mapped by the form data_class.

Q A
Branch? 6.4
Bug fix? no
New feature? yes
Deprecations? no
Tickets no
License MIT
Doc PR symfony/symfony-docs#...

Use-cases:

  • DTO form like user registration with many entities concerned (user / address)

Current usage:
You set constraints in your entities then create a DTO but you must duplicate your constraints in your DTO object to valide your fields.

New usage:
You set constraints in your entities then create a DTO and link your DTO form fields to your entities properties.

Usage:

Add constraints_from_entity option on any form field (child of FormType::class).
If the entity related property name is different that the field name, you must provide constraints_from_property to declare the property name.

Works on (un)mapped field and (un)set data_class form.

<?php
// App/Form/UserRegistrationType.php

namespace App\Form;

use App\Entity\User;
use App\Entity\Address;
use App\Form\Model\UserRegistration;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserRegistrationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('username', TextType::class, [
                'label' => 'Your username',
                'constraints_from_entity' => User::class,
                // Not required if property name is same as the field name
                // 'constraints_from_property' => 'username' // Constraints from User::username
            ])
            ->add('zipCode', TextType::class, [
                'label' => 'Your zip/postal code',
                'constraints_from_entity' => Address::class,
                'constraints_from_property' => 'postalCode' // Constraints from Address::postalCode
            ]);
    }

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

Notes:
It's my first pull request, I hope I done all required things ;)

@carsonbot
Copy link

Hey!

I see that this is your first PR. That is great! Welcome!

Symfony has a contribution guide which I suggest you to read.

In short:

  • Always add tests
  • Keep backward compatibility (see https://symfony.com/bc).
  • Bug fixes must be submitted against the lowest maintained branch where they apply (see https://symfony.com/releases)
  • Features and deprecations must be submitted against the 6.4 branch.

Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change.

When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor!
If this PR is merged in a lower version branch, it will be merged up to all maintained branches within a few days.

I am going to sit back now and wait for the reviews.

Cheers!

Carsonbot

@carsonbot
Copy link

Hey!

Thanks for your PR. You are targeting branch "6.3" but it seems your PR description refers to branch "6.3 for features".
Could you update the PR description or change target branch? This helps core maintainers a lot.

Cheers!

Carsonbot

@tasiot
Copy link
Author

tasiot commented May 28, 2023

I think the target branch is 6.4 but she's behind the 6.3 so I didn't know which branch I must to target

@tasiot tasiot force-pushed the feature/constraints-from branch 2 times, most recently from a16660d to 017d72d Compare May 28, 2023 23:41
@tasiot
Copy link
Author

tasiot commented May 28, 2023

I don't understand why the high-deps and low-deps unit tests fail…

Can anyone help me?

@nicolas-grekas
Copy link
Member

6.4 is the right target, we merge 6.3 into 6.4 regularly so it will catch up eventually.

@nicolas-grekas nicolas-grekas modified the milestones: 6.3, 6.4 May 29, 2023
@nicolas-grekas nicolas-grekas changed the base branch from 6.3 to 6.4 May 29, 2023 11:12
@nicolas-grekas nicolas-grekas changed the base branch from 6.4 to 6.3 May 29, 2023 11:12
@tasiot tasiot force-pushed the feature/constraints-from branch from 017d72d to 55cb5fe Compare June 7, 2023 09:52
@tasiot tasiot marked this pull request as draft June 7, 2023 10:02
@tasiot tasiot changed the base branch from 6.3 to 6.4 June 7, 2023 10:02
@tasiot tasiot marked this pull request as ready for review June 7, 2023 10:03
@carsonbot
Copy link

Hey!

I see that this is your first PR. That is great! Welcome!

Symfony has a contribution guide which I suggest you to read.

In short:

  • Always add tests
  • Keep backward compatibility (see https://symfony.com/bc).
  • Bug fixes must be submitted against the lowest maintained branch where they apply (see https://symfony.com/releases)
  • Features and deprecations must be submitted against the 6.4 branch.

Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change.

When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor!
If this PR is merged in a lower version branch, it will be merged up to all maintained branches within a few days.

I am going to sit back now and wait for the reviews.

Cheers!

Carsonbot

@@ -7,6 +7,7 @@ CHANGELOG
* Don't render seconds for HTML5 date pickers unless "with_seconds" is explicitly set
* Add a `placeholder_attr` option to `ChoiceType`
* Deprecate not configuring the "widget" option of date/time form types, it will default to "single_text" in v7
* Add `constraints_from_entity` and `constraints_from_property` option to `FormType`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for a new 6.4 section

Apply constraints to field from an entity not mapped by the form data_class.

Fix fabbot review

Fix changelog to 6.4
@tasiot tasiot force-pushed the feature/constraints-from branch from 55cb5fe to fbe76e9 Compare June 7, 2023 10:18
@tasiot
Copy link
Author

tasiot commented Jun 7, 2023

The job low-deps fails because it cannot find the trait Symfony\Component\Form\Tests\VersionAwareTest in Symfony\Component\Form\Test\FormPerformanceTestCase.
Can anyone help me? I don't understand how to pass this test because I don't think I have any relation with these files…

@tasiot
Copy link
Author

tasiot commented Jun 8, 2023

Maybe the term "DTO" is not right, a form model will be more appropriate? An object who contains some properties to create some entities…

Sometimes, you can have many forms… A form with 'data_class' => YourEntity::class and another one with 'data_class' => YourDTO::class.

When you make a form to create/edit an entity, it's recommanded to set constraints in this entity instead of each form field, isn't it?
Why duplicate these constraints in your second form (with DTO object)?

With the option constraints_from_entity another form (DTO) or an unmapped field can also apply these constraints without needs to duplicate the entity field constraints in each form field.

Consider these entities (simplified):

Topic
name (string)
category (TopicCategory)
TopicCategory
name (string)
TopicMessage
topic (Topic)
message (text)

You can easily imagine a form to create the first message which asks all theses entity (a category, a name and a message) (=a DTO with these properties) and another form to create the next messages which only asks a message (=TopicMessage Entity).
Instead of duplicate your entities constraints to your DTO form, you only need to add option constraints_from_entity as below:

// With a DTO (data_class => App\Form\Model\FirstTopicMessage::class)
$builder
->add('category', EntityType::class, [
    'class' => TopicCategory::class
])
->add('name', TextType::class, [
    'constraints_from_entity' => Topic::class
])
-> add('message', TextareaType::class, [
    'constraints_from_entity' => TopicMessage::class
])

OR

// With unmapped fields (data_class => App\Entity\TopicMessage::class)
$builder
->add('category', EntityType::class, [
    'class' => TopicCategory::class,
    'mapped' => false,
])
->add('name', TextType::class, [
    'constraints_from_entity' => Topic::class,
    'mapped' => false,
])
-> add('message', TextareaType::class)

@nicolas-grekas nicolas-grekas modified the milestones: 6.4, 7.1 Nov 15, 2023
@xabbuh xabbuh modified the milestones: 7.1, 7.2 May 15, 2024
@fabpot fabpot modified the milestones: 7.2, 7.3 Nov 20, 2024
@fabpot fabpot modified the milestones: 7.3, 7.4 May 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants