Symfony 3 : Build forms for entities based on Traits - forms

I'm having a hard time building forms for my entities that are themselves built with traits.
For example my "Article" entity only contains the link to the category and 2 pics, the rest of its properties is in the SeoTrait (title, meta_title, meta_desc, content, etc...), ValidTrait (isValid true/false)... which I want to use for other entities.
It all works fine for doctrine, that generates my schema with all the fields from the Traits in each entity that use them. The problem is for the forms :
I've created the SeoTraitType for the "SEO" properties :
class SeoTraitType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'label' => 'Nom'
))
->add('metaTitle', TextType::class, array(
'label' => 'Meta Title'
))
->add('metaDescription', TextareaType::class, array(
'label' => 'Meta Description'
))
->add('metaKeywords', TextType::class, array(
'label' => 'Keywords'
))
->add('content', TextareaType::class, array(
'label' => 'Content'
))
;
}
}
And then I'm using it in my ArticleType :
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('seo', SeoTraitType::class, array(
'label' => 'Seo',
'mapped' => false
))
->add('isValid', ValidTraitType::class, array(
'label' => 'Valid',
'mapped' => false
))
->add('save', SubmitType::class, array(
'label' => 'form_save',
'translation_domain' => 'back_default'
));
;
}
}
The two problems I have here is that I must set mapped => false for the 2 TraitTypes when I want to embed them in my main entity's form
And then in my form I get article[seo][name] for the SeoTrait's fields, so I can't really use the $form->handleRequest() methods and all... to handle the submission of my form
I was wondering if there is a special way to do this within the provided methods of the form component, or if I just have to handle the request myself and parse the trait arrays myself to build my entity before saving it ? I couldn't really find anything on the internet :(

One way to solve your problem is to transform your class SeoTraitType into a Trait.
like:
trait SeoTraitType
{
public function buildSEOForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'label' => 'Nom'
))
->add('metaTitle', TextType::class, array(
'label' => 'Meta Title'
))
->add('metaDescription', TextareaType::class, array(
'label' => 'Meta Description'
))
->add('metaKeywords', TextType::class, array(
'label' => 'Keywords'
))
->add('content', TextareaType::class, array(
'label' => 'Content'
))
;
}
}
Then:
class ArticleType extends AbstractType
{
use SeoTraitType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->buildSEOForm($builder, $options);
$builder
->add('isValid', ValidTraitType::class, array(
'label' => 'Valid',
'mapped' => false
))
->add('save', SubmitType::class, array(
'label' => 'form_save',
'translation_domain' => 'back_default'
));
;
}
}
You can also do this with static method. Not a big fan of Trait.

Ok so the Trait solution works fine, but I chose to go for this method here :
https://symfony.com/doc/current/form/inherit_data_option.html
Thanks so much guys, I was pretty sure that the solution would be somewhere in the documentation but couldn't find it !

Related

Symfony4 - Can't get error_mapping to work

In my Symfony4 project, I have an entity called 'Customer'. This entity has an extra validation method to do some extra validation for the field 'zip code'. When a zip code is invalid i receive a general error.
/**
#Assert\IsTrue(message = "invalid.zipcode")
*/
public function isZipCodeValid()
{
...
return false;
}
I would like to map this specific error to the 'zip code' field so i can show users which field is causing the issue.
I have tried the following code in the CustomerForm, as mentioned in the Symfony documentation: https://symfony.com/doc/current/reference/forms/types/form.html#error-mapping
....
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Customer::class,
'error_mapping' => [
'isZipCodeValid' => 'zip_code',
],
]);
}
Unfortunately this keeps sending a global error after posting my form. Not sure why.. Does anyone knows how to fix this? Thanks!
buildForm:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array('label' => 'Name', 'attr' => array('size' => '50')))
->add('address_line', TextType::class, array('label' => 'Addressline', 'attr' => array('size' => '50')))
->add('zip_code', TextType::class, array('label' => 'Zipcode', 'attr' => array('size' => '20')))
->add('city', TextType::class, array('label' => 'City', 'attr' => array('size' => '50')));
}

Synfony 2.8 one of the two fields must be filled

I have this form and I want to checked if one of the two fields (numberPlate or expirationDate) is filled.
This is my buildForm:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, array(
'choices_as_values' => true,
'required' => true,
'label' => 'Tipo Veicolo',
'empty_data' => '',
'empty_value' => '',
'attr' => array('class'=> 'form-control select2'),
'choices' => array('Auto' => 'Auto', 'Moto' => 'Moto', 'Camper' => 'Camper' ,'Barca' => 'Barca')
))
->add('numberPlate', TextType::class, array(
'label' => 'Targa',
'required' => false,
'attr' => array(
'class'=> 'form-control',
'minlength' => 5,
'maxlength' => 7
)
))
->add('expirationDate', DateTimeType::class, array(
'label' => 'Scadenza',
'widget' => 'single_text',
'input' => 'datetime',
'format' => 'dd/MM/yyyy',
'attr' => array('class'=> 'form-control')
))
;
}
You can ensure one of the fields are not empty, by adding a callback constraint to your entity.
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class YourModel
{
/**
* #Assert\Callback
*/
public function validate(ExecutionContextInterface $context)
{
if (!$this->numberPlate && !$this->expirationDate) {
$context->buildViolation('Targa or Scadenza is required')
//optionally display the error at the numberPlate field, omit to display at the top of the form errors
->atPath('numberPlate')
->addViolation()
;
}
}
}
Then update your Scadenza field as not required.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//...
->add('expirationDate', DateTimeType::class, array(
'label' => 'Scadenza',
'required' => false,
'widget' => 'single_text',
'input' => 'datetime',
'format' => 'dd/MM/yyyy',
'attr' => array('class'=> 'form-control')
))
;
}
When the form is submitted it will execute the YourModel::validate method, and if the numberPlate and expirationDate are empty, it will fail $form->isValid().
Be sure to clear your cache after making the changes, to refresh the annotations.
NOTE: This will apply to any/all forms this entity model is used in,
to separate the validation you will need to implement validation groups

Symfony embed form ManyToOne relation

I'm trying to improve a personal application but I'm getting into trouble with an embed form. In fact, I have an advertType form in which I want to add a field in order to choose the skill and the level attached to the advert.
In that way, I have an advert entity, a skill entity, an advertSkill entity which is referencing advert and skill entities thanks to a OneToMany relation. The level attribute is from the advertSkill entity.
I don't know how to proceed to add a skill field in my advert form so as to the advert will be correctly stored with the corresponding skill and level.
There is no attribute in my advert entity which is referencing skill.
Below, a sample of my advertType class :
$builder
->add('date', DateTimeType::class, array(
'view_timezone' => 'Europe/Paris',
'with_seconds' => true
))
->add('title', TextType::class)
->add('content', TextareaType::class)
->add('author', TextType::class)
->add('email', TextType::class)
->add('image', ImageType::class)
->add('categories', EntityType::class, array(
'class' => 'OCPlatformBundle:Category',
'choice_label' => 'name',
'multiple' => true
))
->add('save', SubmitType::class);
Thanks for help, I can give more details about my code but it's just ugly when I put some code with the .
I tried to go ahead reading some advices on the same problem as me on a french forum but I have got a mistake yet because I don't suceed in displaying my skills name and my levels name.
My AdvertType form class :
enter code here $builder
->add('date', DateTimeType::class, array(
'view_timezone' => 'Europe/Paris', // On affiche l'horaire avec le fuseau horaire de Paris
'with_seconds' => true // On ajoute les secondes àl'affichage de l'horaire
))
->add('title', TextType::class)
->add('content', TextareaType::class)
->add('author', TextType::class)
->add('email', TextType::class)
->add('image', ImageType::class)
->add('categories', EntityType::class, array(
'class' => 'OCPlatformBundle:Category',
'choice_label' => 'name',
'multiple' => true
))
->add('advertSkill', CollectionType::class, array(
'entry_type' => AdvertSkillType::class
))
->add('save', SubmitType::class);
My new AdvertType form class :
enter code here public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('advertSkill', EntityType::class, array(
'class' => 'OCPlatformBundle:Skill',
'choice_label' => 'name'
))
->add('level', EntityType::class, array(
'class' => 'OCPlatformBundle:AdvertSkill',
'choices' => array('Débutant', 'Intermédiaire', 'Expert')
));
}
Moreover, I added a OneToMany attribute "advertSkill" in the two classes referenced by the advertSkill class.
I don't understand how to make it works !

How can I get the id and the name of the pupils in the table "eleve" in a FormType?

You can see here my FormType.
And I would like to let appear a drop down menu with the pupils name as a value and the id as the key.
Something like this :
->add('eleve', 'choice', array(
'choices' => array(id of the pupil => name of the pupil),
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('eleve', 'entity', array(
'class' => 'WCSCantineBundle:Eleve'
))
->add('status', 'choice', array(
'choices' => array('0' => 'Non-Inscrit'),
'label' => false
))
->add('date', 'text', array(
'label' => false
))
->add('Ajouter', 'submit');
}
Thank you for your help.
->add('eleve', 'entity', array(
'class' => 'WCSCantineBundle:Eleve'
'choice_label' => 'pupilname',
))
here pupil name would be the name of the field in your Eleve entity
this would create a drop down menu with all the Eleve records loaded from the database, on submission the id of the selection would be submitted through the form
Read more here

How Can I get autofocus on the first element (buildform) symfony

In my TopicType class, I used :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
->add('content', 'ckeditor', array(
'label' => 'Contenu',
'config_name' => 'my_custom_config',
'config' => array('language' => 'fr'),))
->add('save', 'submit')
;
}
How can I get autofocus on my first field "title", when i display the form?
$builder->add('title', 'text', array(
'attr' => array(
'autofocus' => true
)
);
The realy crossbrowser way is to type
$builder->add('title', 'text', array(
'attr' => array(
'autofocus' => null
)
);
This code produces just autofocus attribute without = sign and any value.