Validate a non-model attribute - symfony

Env: Symfony2.2 + Propel 1.6
I try to validate a 'customer' form which is linked to the model (Customer) to manage a "create account" (form 1) and a "login account" (form 2). The user is required to check the "I accept the terms of agreement" checkbox and this field is not linked to the model. I'm using a global "validation.yml" file to manage validation rules.
I don't know how to validate the checkbox is checked with the validation.yml file.
I've try several technics:
1/ Put a rule in the validation.yml and add getter/setter in the model:
validation.yml:
MyProject\Model\Customer:
properties:
email:
- NotBlank:
groups: [login, create]
message: Champ obligatoire.
- Email:
groups: [login, create]
message: La valeur saisie doit être un email.
cgv:
- Symfony\Component\Validator\Constraints\True:
groups: [login, create]
message: Vous devez accepter les CGV.
constraints:
- Propel\PropelBundle\Validator\Constraints\UniqueObject:
groups: [create]
fields: email
message: Cet email est déjà inscrit.
- Callback:
groups: [login]
methods:
- [MyProject\Model\CustomerQuery, isCustomerEmail]
MyProject\Model\Customer:
class Customer extends BaseCustomer {
private $cgv;
(...)
public function setCgv($cgv) {
$this->cgv = (Boolean) $cgv;
}
public function getCgv() {
return $this->cgv;
}
}
Result: the rule "True" is ok even if the checkbox isn't checked. If I add a "NotBlank" rule, the validation failed in both check/uncheck cases.
2/ Try to add specific validation rules in the "CustomerType" object (like explained in this article)
/*
*
*
* #author
*/
class CustomerLoginType extends AbstractType {
/**
*
* #param \Symfony\Component\Form\FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', 'text', array('required' => true));
$builder->add('fill', 'checkbox', array('mapped' => false, 'required' => false, 'data' => true));
$builder->add('cgv', 'checkbox', array('data' => true, 'mapped' => false, 'required' => true, 'validation_groups' => array('login'), 'constraints' => new True(array('message' => 'Vous devez accepter les Conditions Générales de Vente.'))));
}
/**
*
* #return string
*/
public function getName()
{
return 'customer_login';
}
/**
*
* #param \MyProject\FrontBundle\Form\Type\OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyProject\Model\Customer',
'validation_groups' => array('login')
));
}
}
Result: Theses rules are not take into account if a "validation.yml" file exists, it could work if I remove the "Customer" entry in the file, but I would prefer to keep it if possible.
3/ As I can't find a "yml" solution, I've finally add a "manual" validation inside the controller like this:
$form_request = $this->getRequest()->get('customer_login');
if (!isset($form_request['cgv'])) {
$form_customer_login->get('cgv')->addError(new \Symfony\Component\Form\FormError('Vous devez accepter les Conditions Générales de Vente.'));
}
Any idea of how I could add my "accept terms" checkbox rule inside the validation.yml file?

Related

Symfony form validation does not work on edit

I have a form with 3 fields as below : storeClient , type and line that belong to Fashion entity.
I basically have the same problem mentioned here :
Symfony Form Validation not working in Edit
But I was surprised when I edited the form and chose the placeholder as option for Client and I filled the line and type fields , I got that for the client field, it DOES display my validation message "Please choose an option" .which is good .
However for the remaining two fields, if line or type are edited in such a way to choose the placeholder option, the application crashed and gives the argumet exception error.
I did a dump; die; inside the controller (you can see it commented below) . And I got the $fashion object like this when I chose the placeholder for all the fields aka for the client,type and line fields :
Fashion object :
storeClient: null
line: Proxies ...\RefLine
___isinitilized___ :true
and all the info of the line that was set initiallly in this object when I first enterede its edit page.
type: Proxies ...\RefType
___isinitilized___ :true
and all the info of the type that was set initiallly in this object when I first enterede its edit page.
so my question is why the validations work on edit for the client field and does not work for line and type ? I do not think that it is related to the fact that it is a choicettype whereas the other two are entitytype. Moreover, I didn't put a "?" in the setter of client. So i don't see why it works for this field and WHY it gave a Null value when I printed the object and it didn't print the initial client value that was already set in the object when I first landed on the edit page although the two other fields hold the values that were already stored in the object initially.
FashionType.php
->add('storeClient', ChoiceType::class,
[
'label' => 'Store Client',
'choices' => $choicesClient,
'choice_value' => function ($value) {
if ($value instanceof Client) {
return $value->getId();
} else {
return $value;
}
},
'placeholder' => 'Choose ..',
'choice_label' => 'diplayLabel',
'attr' => ['class' => "chosen"],
'required' => true,
]
)
->add('type',
EntityType::class,
[
'label' => 'Clothes Type',
'class' => RefType::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('refType')
->orderBy('refType.id', 'ASC');
},
'attr' => ['class' => "chosen"],
'placeholder' => 'Choose..',
'required' => true,
'choice_label' => 'label',
])
->add('line',
EntityType::class,
[
'label' => 'cotation.creation_form.ligne_de_cotation',
'class' => RefLine::class,
'choice_value' => function ($value) {
if ($value instanceof RefLine) {
return $value->getId();
} else {
return $value;
}
},
'query_builder' => function (EntityRepository $er) {
return $er->getShoppingLines();
},
'attr' => ['class' => "chosen"],
'placeholder' => 'Choose..',
'required' => true,
'choice_label' => 'getLabel',
])
IN my controller, this function is called upon submitting the form :
public function validerAction(Request $request, $idFashion)
{
$em = $this->getDoctrine()->getManager();
/** #var Fashion $fashion */
$fashion = ($idFashion === null) ? new Fashion() : $em->getRepository(
'App:Fashion'
)->find($idFashion);
$form = $this->createForm(FashionType::class, $fashion);
// try {
$form->handleRequest($request);
//} catch(\InvalidArgumentException) {
//dump($fashion);die;
// }
if ($form->isSubmitted() && $form->isValid()) {..}
Here are my setters:
/**
* Set line
*
* #param Refline $line
*
* #return Fashion
*/
public function setLine(RefLine $line)
{
$this->line = $line;
return $this;
}
/**
* Set type
*
* #param RefType $type
*
* #return Fashion
*/
public function setType(RefType $type)
{
$this->type = $type;
return $this;
}
/**
* Set storeClient
*
* #param Client $storeClient
* #return Fashion
*/
public function setStoreClient($storeClient)
{
$this->storeClient = $storeClient;
return $this;
}
THe three fields were declared like this :
/**
* #ORM\ManyToOne(targetEntity="App\Entity\RefLine")
* #ORM\JoinColumn(name="line_id", referencedColumnName="id", nullable=false)
*/
private $line;
In EntityType::class field type is by default nullable. If you want to add validation on that then you have to write this
/**
*
* #Assert\NotBlank(message="Please enter Line", groups="groupName")
*/
private $line;
For more details you can read https://symfony.com/doc/current/validation.html
if you are using group name then you should declare in Form
$resolver->setDefaults([
// ...
'validation_groups' => ['Default', 'groupName'],
]);

Symfony - ManyToMany - replaces instead of adding

I apologize in advance if my question seems silly to you, I'm a beginner, I've searched but I can't find the answers.
We are in a factory. In this factory, each worker can have several posts, and each posts can contain several workers. So we are in a ManyToMany relationship. The problem is that when I add a worker to a post, he doesn't add to the worker already present in this post, he replaces him! As if a post could only contain one worker.
Can someone tell me what I'm doing wrong or send me precisely the documentation related to this type of problem?
Thanks.
Here is the related code.
(Poste = Post, Operateur = Worker)
in the Post Entity :
/**
* #ORM\ManyToMany(targetEntity=Operateur::class, inversedBy="postes")
* #ORM\JoinTable(name="poste_operateur")
*/
private $operateurs;
/**
* #return Collection|Operateur[]
*/
public function getOperateurs(): Collection
{
return $this->operateurs;
}
public function addOperateur(Operateur $operateur): self
{
if (!$this->operateurs->contains($operateur)) {
$this->operateurs[] = $operateur;
$operateur->addPoste($this);
}
return $this;
}
public function removeOperateur(Operateur $operateur): self
{
$this->operateurs->removeElement($operateur);
$operateur->removePoste($this);
return $this;
}
In the Operateur (worker) entity :
/**
* #ORM\ManyToMany(targetEntity=Poste::class, mappedBy="operateurs")
*/
private $postes;
/**
* #return Collection|Poste[]
*/
public function getPostes(): Collection
{
return $this->postes;
}
public function addPoste(Poste $poste): self
{
if (!$this->postes->contains($poste)) {
$this->postes[] = $poste;
$poste->addOperateur($this);
}
return $this;
}
public function removePoste(Poste $poste): self
{
if ($this->postes->removeElement($poste)) {
$poste->removeOperateur($this);
}
return $this;
}
In the PosteController, method to add an operateur to a post :
/**
* #Route("/{id}/new", name="poste_ope", methods={"GET", "POST"})
*/
public function addOpe(Request $request, Poste $poste): Response
{
$form = $this->createForm(PosteType2::class, $poste);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash(
'success',
"L'opérateur a bien été ajouté au poste {$poste->getNom()} !"
);
return $this->redirectToRoute('operateur_index');
}
return $this->render('poste/addope.html.twig', [
'poste' => $poste,
'form' => $form->createView(),
]);
}
The form in PostType2 :
class PosteType2 extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('operateurs', EntityType::class, [
'class' => Operateur::class,
'label' => 'ajouter un opérateur à ce poste',
'choice_label' => 'nom',
'multiple' => true,
'expanded' => true,
])
->add('save', SubmitType::class, [
'label' => 'Enregistrer',
'attr' => [
'class' => 'btn btn-primary'
]
]);
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Poste::class,
]);
}
}
The problem was in the PosteController, here is the correction :
add an addPost
Here is the documentation who helped me : https://symfony.com/doc/current/doctrine/associations.html#saving-related-entities

EntityType - Can not use query_builder

I have a problem with my FormType in Symfony. I have a field that allows me to check one or more checkboxs whose values represent objects from another entity. (In this case, types of holidays).
Except that I do not want to display them all, so I use a query_builder as such:
->add('typesConges', EntityType::class, [
'class' => TypeConge::class,
'choice_label' => 'nom',
'expanded' => true,
'multiple' => true,
'query_builder' => function (TypeCongeRepository $repoTypes) {
return $repoTypes->getTypesNotNull();
}
])
But it raised this error :
The name "Heures supp" contains illegal characters. Names should start
with a letter, digit or underscore and only contain letters, digits,
numbers, underscores ("_"), hyphens ("-") and colons (":").
However, if I remove the query_builder, I have all my TypeConge ( the "Heures supp" aswell).
GestionSoldes.php
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
class GestionSoldes
{
/**
* Types de congés
*
* #var Collection|TypeConge[]
*/
private $typesConges;
/**
* All types
*
* #var boolean
*/
private $allTypes;
public function __construct()
{
$this->typesConges = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection|TypeConge[]
*/
public function getTypesConges(): Collection
{
return $this->typesConges;
}
public function addTypesConge(TypeConge $typesConge): self
{
if (!$this->typesConges->contains($typesConge)) {
$this->typesConges[] = $typesConge;
}
return $this;
}
public function removeTypesConge(TypeConge $typesConge): self
{
if ($this->typesConges->contains($typesConge)) {
$this->typesConges->removeElement($typesConge);
}
return $this;
}
public function getAllTypes(): ?bool
{
return $this->allTypes;
}
public function setAllTypes(bool $allTypes): self
{
$this->allTypes = $allTypes;
return $this;
}
}
Form:
<?php
namespace App\Form;
use App\Entity\TypeConge;
use App\Entity\GestionSoldes;
use App\Repository\TypeCongeRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
class GestionSoldesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('typesConges', EntityType::class, [
'class' => TypeConge::class,
'choice_label' => 'nom',
'expanded' => true,
'multiple' => true,
'query_builder' => function (TypeCongeRepository $repoTypes) {
return $repoTypes->getTypesNotNull();
}
])
->add('allTypes', CheckboxType::class, [
'required' => false,
'label' => 'Tous les types de congés',
'label_attr' => [
'class' => 'custom-control-label',
'for' => 'allTypes'
],
'attr' => [
'class' => 'custom-control-input',
'id' => 'allTypes'
]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => GestionSoldes::class,
]);
}
}
My repo function:
/**
* Retourne les types de congés qui ont un solde initial différent de null ( pour form EntityType )
*
* #return void
*/
public function getTypesNotNull()
{
return $this->createQueryBuilder('t')
->where('t.soldeInitial is not null')
->orderBy('t.nom', 'ASC');
}
As it shows no errors when you remove te query-builer, The problem must come from the query-builder.
As the only moment you use the property nom in the query-builder is in
->orderBy('t.nom', 'ASC');
The problem is here.
Make sure that your ORM (doctrine or else) is ok with string containing spaces.
to verify my point try to remove the orderBy from your query

symfony easyadmin form field type entity with filter list

I use symfony 3.4 and easycorp/easyadmin-bundle 1.17
This is my class "Quotation", the "artisan" field is an "under registration" of the "Person" entity (person.type = 1) :
class Quotation
{
/** others fields... */
/**
* 1 => 'artisan', 2 => 'customer'
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="artisanQuotations", cascade= { "persist" })
* #ORM\JoinColumn(name="artisan_id", referencedColumnName="id")
*/
private $artisan;
/** getters and setters ... */
I have a problem with a form field using a custom field type
form:
fields:
...
- { property: 'artisan', label: '', type: 'AppBundle\Form\Field\ArtisanType' }
I created this form field type to be able to filter the list thanks to the query_builder :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('artisan', EntityType::class, array(
'class' => 'AppBundle:Person',
'label' => false,
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('person')
->where('person.type = 1');
},
'attr' => array('data-widget' => 'select2'),
'multiple' => false,
'expanded'=> false
));
}
my form is displayed perfectly well but when i submit this form i have an error :
Expected argument of type "AppBundle\Entity\Person", "array" given
Thank you in advance for your help
Instead of using the buildForm method you should use configureOptions here. That way your form is not extended by another subform which results in the array.
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ArtisanType extends AbstractType
{
/**
* #param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => 'AppBundle:Person',
'label' => false,
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('person')
->where('person.type = 1');
},
'attr' => array('data-widget' => 'select2'),
'multiple' => false,
'expanded'=> false,
]);
}
/**
* #return string|null
*/
public function getParent()
{
return EntityType::class;
}
}

symfony2 Entity Object vs. integer crashes

I have defined a entity like :
/**
* #ORM\ManyToOne(targetEntity="Pr\UserBundle\Entity\Client")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
private $client_id;
.....
public function setClientId($clientId = null)
{
$this->client_id = $clientId;
return $this;
}
There are two controllers with which I can create a new db entry. the first one is "admin-only" where the admin can create a db entry with a client id of his choice:
->add('client_id', 'entity', array(
'data_class' => null,
'attr' => array(
'class' => 'selectstyle'),
'class' => 'PrUserBundle:Client',
'property' => 'name',
'required' => true,
'label' => 'staff.location',
'empty_value' => 'admin.customer_name',
'empty_data' => null
)
)
......
// Handling the form
$em = $this->getDoctrine()->getManager();
$saniType->setName($form->get('name')->getData());
$saniType->setClientId($form->get('client_id')->getData());
$saniType->setCreated(new \DateTime(date('Y-m-d H:m:s')));
$saniType->setCreatedBy($user->getUsername());
$em->persist($saniType);
$em->flush();
The second one is for the client itself, where he's not able to set a different client id. Therefor I just removed the form field "client_id" and replace it by the users->getClientId():
$saniType->setSpecialClientId($form->get('client_id')->getData());
When I add an entry as admin, it work fine. If I try to add one as "client", it crashes with following error message
Warning: spl_object_hash() expects parameter 1 to be object, integer given in /var/www/symfony/webprojekt/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 1389
I'm a newby in symfony, so I haven't got the strength to figure out what happens. I only knew that in the first case, admin will submit a object (entity). In the second (client) case, I set an integer as I get one by
$user = $this->container->get('security.context')->getToken()->getUser();
Is there a solution for it so that I can handle entity object AND given integers like it comes when the client add an entry?
You should change you relation defnition to:
/**
* #ORM\ManyToOne(targetEntity="Pr\UserBundle\Entity\Client")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
private $client;
.....
public function setClient($client = null)
{
$this->client = $client;
return $this;
}
Then in form:
->add('client', 'entity', array(
'data_class' => null,
'attr' => array('class' => 'selectstyle'),
'class' => 'PrUserBundle:Client',
'property' => 'name',
'required' => true,
'label' => 'staff.location',
'empty_value' => 'admin.customer_name',
'empty_data' => null
)
)
Handling the form:
form = $this->createForm(new SaniType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
// perform some action...
return $this->redirect($this->generateUrl('some_success'));
}
More about handling form: http://symfony.com/doc/current/book/forms.html#handling-form-submissions
Also worth nothing:
for auto update properties like createdBy / updatedBy i would recommend you to use Doctrine Extension: https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/blameable.md
You don't have to know the client id. You have to set the Client himself.
/**
* #ORM\ManyToOne(targetEntity="Pr\UserBundle\Entity\Client")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
private $client;
.....
public function setClient(\Pr\UserBundle\Entity\Client $client = null)
{
$this->client = $client;
return $this;
}
I found a Solution:
Symfony2: spl_object_hash() expects parameter 1 to be object, string given in Doctrine
It's a workaround but it's ok for the moment.

Resources