Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList and Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList have been rewritten in Symfony2.8 (details in the post below).
After that, a validation error occurred in Symfony3 and the save of the author selected in the form failed.
I attributed it to the swapping of the Choice Type keys and values.
So, before raising Symfony, I specified chouces_as_values as Type and verified whether it works.
But it didn't work and the result was similar. In other words, can we think that this is not a replacement of key values?
It may be that the rewriting of ChoiceList has failed.
Also, I would like advice on where to debug so that you can understand what is the cause.
Symfony: Unable to reverse value for property path [...]: The choice [...] does not exist or is not unique
https://symfony.com/doc/2.8/reference/forms/types/choice.html#choices-as-values
ArticleType
$ChoiceList = new StaffChoiceLoader($this->staffService, $options['login_staff']);
//$ChoiceList = new StaffChoiceList($this->staffService, $options['login_staff']);
$builder->add("author", "entity", array(
"required" => true,
"class" => "AppBundle:Staff",
"choice_loader" => $ChoiceList,
//"choice_list" => $ChoiceList,
"empty_value" => "Please select",
//Add
"choices_as_values" => true,
));
ArticleEntity
/**
* author
*
* #ORM\ManyToOne(targetEntity="Staff")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=true)
*/
protected $author;
Related
I have a request that takes several input. I want the 'salary' input to be validated only if the salary_type is equal to "exact". Else, I don't want to have any message about it.
However, now, even if salary_type is equal to "range", I still get an error that "salary must be an integer".
How can I make sure there isn't any error unless the field is required?
Thanks!
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreJobOfferRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required|string|min:3',
'description' => 'required|string|min:10',
'city' => 'required|string|min:3',
'salary_type' => 'required|in:range,exact',
'min_salary' => 'required_if:salary_type,range|integer|min:0',
'max_salary' => 'required_if:salary_type,range|integer|min:0',
'salary' => 'required_if:salary_type,exact|integer|min:0',
'fieldsOfWork' => 'required|exists:field_of_works,id',
'job_type.*' => 'required|exists:job_types,id',
"trainings.*" => 'required|exists:trainings,id',
"availability.*" => 'required|exists:trainings,id'
];
}
}
required and required_if do not stop the rest of the validation rules from being executed. It just expects the value not to be null, an empty string, and empty Countable object or an uploaded file with no path. If the field is set, the required rule will just be skipped and the next rule is applied.
So, the required_if rule for salary will skip if salary_type is not 'exact', and continue validating if it's an integer and min 0. This behavior is confusing to a lot of new Laravel developers.
There is an exclude_if rule that does what you probably expect:
'salary' => 'exclude_if:salary_type,exact|integer|min:0',
This will exclude the whole field under validation from the request data that will be returned by validate() and $request->validated() methods if the condition is not met. So if salary_type is not 'exact', the salary field will be gone but the validation will pass.
If salary_type is 'exact', however, it will be validated for being a min 0 integer value.
Please note that the exclude_if rule was added in Laravel 6, so this doesn't work for previous versions of Laravel. See https://laravel.com/docs/8.x/validation#conditionally-adding-rules for details on conditional validation.
I have a form containing a FileType field. I've set the multiple option to true so the user can upload multiple files at the same time.
$builder->add('myFile', FileType::class, [
'label' => 'upload file',
'multiple' => true,
])
Here is the corresponding property in the Entity connected to this form:
/**
* #Assert\NotBlank()
* #Assert\File(mimeTypes = {"application/pdf", "application/x-pdf", "image/jpeg", "image/png"})
* #ORM\Column(type="array")
*/
private $myFile;
When I submit the form I get the error:
UnexpectedTypeException in FileValidator.php line 168:
Expected argument of type "string", "array" given
I added curly braces in front of File assert so it looks like this:
* #Assert\File{}(mimeTypes = {"application/pdf", "application/x-pdf", "image/jpeg", "image/png"})
Now it doesn't complain when submitting the form. but the file type validation is also not checked.
Any idea how to make the file type working for multiple selected files?
Since you're validating array of File, you need to apply All validator, which will apply inner validators on each element of the array.
Try with something like:
/**
* #Assert\All({
* #Assert\NotBlank(),
* #Assert\File(mimeTypes = {"application/pdf", "application/x-pdf", "image/jpeg", "image/png"})
* })
* #ORM\Column(type="array")
*/
private $myFile;
Update
I found in the Symfony docs the answer and an example tutorial to my case: http://symfony.com/doc/current/reference/forms/types/collection.html#basic-usage
How can I assign multiple email fields to a form in Symfony 3.1?
In my entity I have:
/**
* #var array
* #ORM\Column(name="notification_emails", type="array", nullable=true)
*/
private $notificationEmails;
/**
* #return array
*/
public function getNotificationEmails()
{
return $this->notificationEmails;
}
/**
* #param array $notificationEmails
*/
public function setNotificationEmails($notificationEmails)
{
$this->notificationEmails = $notificationEmails;
}
In my form I have:
$builder->add(
'notificationEmails',
CollectionType::class,
array(
'entry_type' => EmailType::class,
'label' => 'Add more emails separated by comma',
'attr' => array(
'multiple' => 'multiple',
),
)
);
But this doesn't work :(
You need Collection type field, only if you are providing multiple email fields for each one email; with some help of javascript and having Add new email button.
If users are going to add emails separated by comma, you don't need a Collection Type field. Keep it simple Text type field with help text as you have done.
Now, in setter [setNotificationEmails], you should split the csv email string to array and feed to ORM. ORM does the rest in order to save in the database.
You should do the vice-versa in getter [getNotificationEmails] (converting array to string). So your form can represent comma separated emails.
In case above doesn't work, as I doubt, the form might not read data from getter. In that case, you can always use a Transformer. It's so useful.
I'm pretty deep into a complex Symfony2 project, with many entities and join tables in forms etc. but I'm having a strange issue with the "multiple" attribute within the form builder.
Basically I have a form where a user can add an illness to the CRM, and each illness can be attached to a specific store (depending on the language used). There is a list of stores that can be chosen by the user within this form, and the values are stored in a join table. However I only want one store to be chosen (i.e. a select drop down) rather than a multiple select list but using the false value for the multiple attribute throws an error which I will outline later.
Firstly, here is the buildForm() code in my Type.php file:
$builder->add('name' , 'text');
$builder->add('description' , 'textarea');
$builder->add('store', 'entity',
array(
'class' => 'AppBundle:Store',
'empty_value' => 'Choose store',
'property' => 'name',
'multiple' => false,
));
$builder->add('save', 'submit', array(
'attr' => array(
'class' => 'btn btn-primary'
),
));
And the entry for the store field in my Entity:
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Store", inversedBy="illness", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="store_id", referencedColumnName="id")
* })
*/
public $store;
However, if I used the false declaration for the multiple attribute in the form Type, when the form is submitted I receive the following error:
Warning: spl_object_hash() expects parameter 1 to be object, string given
because it looks like it's passing the text value of the select box, rather than the relevant Entity. When set as a multiple select box (i.e. set to true) then it works fine and persists as it should.
My controller code:
$addIllness = new Illness();
$form = $this->createForm(new IllnessType($em), $addIllness);
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($addIllness);
$em->flush();
return $this->redirect($this->generateUrl('app_illness_table'));
}
Having it as a multiple select box is not the end of the world, though I'd rather have it as a drop down select so the user cannot select more than one store - rather than me having to add an error message or note to tell them otherwise.
If anyone has an ideas as to why this may be happening, or has encountered it before please let me know, I would be very grateful!
Thank you
Michael
In Symfony 2.6, I am using an entity form type that is unmapped:
$form
->add(
'myEntity', // Form field name
'entity',
[
'mapped' => false, // Not mapped
'class' => 'MyVendor\MyBundle\Entity\MyEntity',
'choices' => $MyEntityCollection, // list of MyEntity
'property' => 'name',
'empty_value' => 'Please select MyEntity',
'empty_data' => null,
'attr' => [
'label' => 'My label'
]
]
);
This allows user to properly select an item of MyEntity or leave it blank. According to that, I am adding a EventSubscriber to modify the preSubmitted data if any value is selected, and leave it as it is if no choice has been made.
Here is the eventSubscriber:
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SUBMIT => 'preSubmitData'
];
}
/**
* #param FormEvent $event
*/
public function preSubmitData(FormEvent $event)
{
if( null === ($entity = $event->getForm()->get( 'myEntity' )->getData() ) ){
return;
}
// Set value if field has been defined
$event
->getForm()
->setData( $entity )
;
}
If user selects a choice other than blank, when I debug the preSubmitData function:
$event->getForm()->get('entity')->getData() gives null
$event->getData() gives an array having as 'entity' key the selected entity ID (just the scalar value)
My questions are:
Shouldn't $event->getForm()->get('entity')->getData() have the selected entity?
Why is $event->getForm()->get('entity')->getData() giving null if $event->getData() has at least the entity ID in it?
Is there any way to get the entity here (as it happens with the mapped entities) without having to call the entity manager and querying the entity via its ID?
Thanks in advance!
Edit
For the big picture, in my global form (other fields not described here) I have 2 depending fields:
A select A (not described here) with some options from a tree. This option does exist in the global form entity as a property.
A second B select named myEntity (described here). It doesn't exist as the global form entity as a property, thus the mapped = false. If any choice is made here, then the first select (A)'s option is overridden by this one. Else the first choice remains as the entity property value.
Hope is clearer now.
Ok, it was giving null because we are on the preSubmit event, and here data sent is not yet mapped within an entity.
Changing the event to submit gives the mapped entity as needed.