Use Expression constrainst on non object - symfony

I have a form like it:
class FeatureDynamicSequenceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('upstream', IntegerType::class, [
'data' => 0,
'constraints' => [
new LessThan([
'value' => 1000,
]),
],
])
->add('downstream', IntegerType::class, [
'data' => 0,
'constraints' => [
new LessThan([
'value' => 1000,
]),
],
])
->add('showUtr', CheckboxType::class,[
'data' => true,
'label' => 'Show UTR',
'required' => false,
])
->add('showIntron', CheckboxType::class,[
'data' => true,
'required' => false,
])
;
}
}
In this form, I would like add a Constrainst that check:
If showUtr or ShowIntron are not checked, then upstream and downstreal can't be > to 0.
Then I want something like it:
->add('upstream', IntegerType::class, [
'data' => 0,
'constraints' => [
new LessThan([
'value' => 1000,
]),
new Expression([
'expression' => 'value > 0 && (this.showUtr || this.showIntron)',
'message' => 'You cannot set upstream if you do not display UTRs and introns.',
]),
],
])
But I can't use it, because it's not an object, value give me the value of the upstream field (it's ok), but I can't access to the showUtr or showIntron value...
EDIT: try with Callback closure
->add('upstream', IntegerType::class, [
'data' => 0,
'constraints' => [
new LessThan([
'value' => 1000,
]),
new Callback([
'callback' => function($data, ExecutionContextInterface $executionContectInterface) {
dump($data);
$executionContectInterface->addViolation('You cannot set upstream if you do not display UTRs and introns.');
},
])
],
])
I have the same problem, $data just contain the field value.
I don't really want to create an Entity, because I don't persist it... And I can't believe there is not a solution to check it whithout creating an Entity.

I answered in a previous question here

I solved it by using:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'constraints' => [
new Callback([
'callback' => function($data, ExecutionContextInterface $executionContectInterface) {
if ($data['upstream'] > 0 && (!$data['showUtr'] || !$data['showIntron'])) {
$executionContectInterface->buildViolation('You cannot set upstream if you do not display UTRs and introns.')
->atPath('[upstream]')
->addViolation()
;
}
},
]),
],
]);
}
The full code is:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('upstream', IntegerType::class, [
'data' => 0,
'constraints' => [
new LessThan([
'value' => 1000,
]),
],
])
->add('downstream', IntegerType::class, [
'data' => 0,
'constraints' => [
new LessThan([
'value' => 1000,
]),
],
])
->add('showUtr', CheckboxType::class, [
'data' => true,
'label' => 'Show UTR',
'required' => false,
])
->add('showIntron', CheckboxType::class, [
'data' => true,
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'constraints' => [
new Callback([
'callback' => function($data, ExecutionContextInterface $executionContectInterface) {
if ($data['upstream'] > 0 && (!$data['showUtr'] || !$data['showIntron'])) {
$executionContectInterface->buildViolation('You cannot set upstream if you do not display UTRs and introns.')
->atPath('[upstream]')
->addViolation()
;
}
},
]),
],
]);
}

Related

Symfony 5 - display an required input field after a specify dropdown select

I would like to build a form with Symfony 5 that should display a required input field for a certain dropdown selection.
After selecting "sonstiges" I need a required input field.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('salutation', ChoiceType::class, [
'label' => 'salutation',
'required' => true,
'constraints' => [
new NotBlank()
],
'placeholder' => 'Bitte wählen',
'label_attr' => [
'class' => 'visually-hidden'
],
'choices' => [
'Herr' => 'herr',
'Frau' => 'frau',
'Diverse' => 'diverse',
],
])
->add('firstname', TextType::class, [
'label' => false,
'required' => true,
'constraints' => [
new NotBlank()
],
'attr' => [
'placeholder' => "firstname",
],
])
->add('afterWork', ChoiceType::class, [
'label' => 'afterWork',
'required' => true,
'constraints' => [
new NotBlank()
],
'placeholder' => 'Bitte wählen',
'label_attr' => [
'class' => 'visually-hidden'
],
'choices' => [
'Studium' => 'studium',
'weitere Schule' => 'weiterSchule',
'Ausbildung' => 'ausbildung',
'Sonstiges' => 'sonstiges',
],
])
}
I added a $builder->addEventListener, unfortunately it didn't give me the result I need, I also added the input field as not required, hidden it and displayed it with a javascript. Unfortunately, it will not be validated because it is not required in the $builder.
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
if (!is_null($data) && $data['afterWork'] == "sonstiges") {
$form->add('afterWorkText', TextType::class, [
'label' => "afterWorkText",
'required' => false,
'constraints' => array(
new NotBlank(),
),
'attr' => [
'placeholder' => "afterWorkText"
],
'label_attr' => [
'class' => 'visually-hidden'
],
]);
}
}
Is there a way to insert this field with "display:none" attribute and activate it with a javascript? In addition, the field should then be set to required.
Or can someone help me to find the right solution here?

how i can add constrainte greatherthan or equal today to field dateType when user update data?

I have formType class that i use for creation and modification of data, i need to use constrainte GreaterThanOrEqual than today if user create a new object or modifie date field and not activate the constrainte if user not modifie field dateType how i can do that .
there is my form type:
$builder
->add('title', TextType::class, [
'label' => "title",
'constraints' => [
new NotBlank(),
]
])
->add('percentage', PercentageType::class, [
'required' => false,
'label' => "percentage",
'constraints' => [
new Range(min: 0, max: 100),
new NotBlank()
],
])
->add('startAt', DateType::class, [
'label' => 'startAt',
'widget' => 'single_text',
'input' => 'datetime_immutable',
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual(['value' => 'today'])
],
])

Symfony Security component register form ChoiceType field insert into bdd as an array(for the roles a user as)

When i register with my form(1) the roles the user select isn't entered in the bdd instead it enter just [] i couldn't get it enter either ROLE_USER or ROLE_ADVERTISER or both in array in the bdd, roles is a longtext in,
Here is my Form builder:
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->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,
'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('roles', ChoiceType::class, [
'required' => true,
'multiple' => false,
'expanded' => false,
'choices' => [
'ROLE_USER' => 'ROLE_USER',
'ROLE_ADVERTISER' => 'ROLE_ADVERTISER',
],
]);
$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,
]);
}
}

Symfony manually $form->submit(); with multidimensional array

Im struggling since 10 hours with Symfony 5.1.7 and $form->submit();
My target is a JSON API that converts data to a similiar array. I already debugged and found following part.
Can someone please help me what I am doing wrong here?
To test it, i have created a manually PHP array to submit it.
My Code in Controller
$form = $this->createForm(AddCommentFormType::class);
$test = [
'content' => 'Test',
'media' => [
[
'path' => '1.png',
],
[
'path' => '2.png',
],
],
'_token' => '3bF4qkiUPjKNuGnbY-ySdO6B2sCLzKcS4ar7auX3Dek',
];
$form->submit($test);
AddCommentFormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content', TextareaType::class, [
'constraints' => [
new NotBlank(),
new Length([
'max' => 10000,
]),
],
])
->add('media', CollectionType::class, [
'entry_type' => MediaFormType::class,
'constraints' => [
new Count([
'min' => 1,
'max' => 5,
]),
],
])
->add('_token', HiddenType::class, [
'mapped' => false,
'constraints' => [
new NotBlank(),
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
MediaFormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('path', TextType::class, [
'constraints' => [
new NotBlank(),
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Media::class,
]);
}
Validator Result
children[media].data
This collection should contain 1 element or more.
[]
children[media]
This form should not contain extra fields.
[▼
[▼
"path" => "1.png"
]
[▼
"path" => "2.png"
]
]
your form has no default data, since you create it with
$form = $this->createForm(AddCommentFormType::class);
createForm can take an additional parameter for default data. This alone is not necessarily a problem, the default is an array of the form (or something very similar, maybe empty strings instead of null)
[
'content' => null,
'media' => [],
'_token' => null,
]
However, the CollectionType will not allow adding or removing elements by default. Setting it's options allow_add (and optionally allow_remove, if you ever set default values) will change that.
So the minimal change would be:
->add('media', CollectionType::class, [
'allow_add' => true, // <-- this is new
'entry_type' => MediaFormType::class,
'constraints' => [
new Count([
'min' => 1,
'max' => 5,
]),
],
])
If your type is AddCommentFormType the form expects by default the data to be in add_comment_form keys like:
$test = [
‘add_comment_form’ => [
'content' => 'Test',
'media' => [
[
'path' => '1.png',
],
[
'path' => '2.png',
],
],
'_token' => '3bF4qkiUPjKNuGnbY-ySdO6B2sCLzKcS4ar7auX3Dek',
]
];

How to set min value for field in form?

I have form with integer field - price.
$builder->add('list',
CollectionType::class,
[
'required' => false,
'allow_add' => true,
'error_bubbling' => true,
])
->add('price',
IntegerType::class,
[
'required' => false,
'error_bubbling' => true,
]);
How to set, for example, for validation i need min value for price 0 or greater?
I tried this one, but it's not work:
'constraints' => [
new GreaterThanOrEqual(50)
],
Thanks for all help.
controller Action
public function getProductAction(Request $request)
{
$variables = $request->get('list');
$price = $request->get('price');
$form = $this->createForm(ProductForm::class, null, ['csrf_protection' => false, 'allow_extra_fields' => true]);
$form->submit(['variables' => $variables, 'prices' => $price]);
if(!$form->isValid()) {
$errors = '';
foreach ($form->getErrors() as $error) {
$errors = $error->getMessage();
}
return new JsonResponse([
'errors' => $errors
],Response::HTTP_BAD_REQUEST);
} else {
$product = $this->getDoctrine()
->getRepository(Product::class)
->findByListAndPrice($list, $price);
if (!$product) {
return new JsonResponse([
'errors' => 'Product not found.'
],Response::HTTP_BAD_REQUEST);
}
return new JsonResponse($product);
}
}
Form not validate, and don't show errors, $form->isValid() === true
According to https://github.com/symfony/symfony/issues/3533 you can use min and max for the IntegerType even though the documentation might not mention this.
$builder->add('list',
CollectionType::class,
[
'required' => false,
'allow_add' => true,
'error_bubbling' => true,
])
->add('price',
IntegerType::class,
[
'required' => false,
'error_bubbling' => true,
/*'min' => 50*/
'attr' => [
'min' => 50
]
]);
EDIT: According to the documentation the 'min' property has to be inside 'attr' tag. This will add the min inside the input in the HTML.
You can use RangeType instead of IntegerType.
use Symfony\Component\Form\Extension\Core\Type\RangeType;
// ...
$builder->add('list',
CollectionType::class,
[
'required' => false,
'allow_add' => true,
'error_bubbling' => true,
])
->add('price',
RangeType::class,
[
'required' => false,
'error_bubbling' => true,
'min' => 0,
'max' => 50
]);

Resources