I have a problem with symfony validation.
After adding NotBlank or Length to the entity validation displays a standard message for missing values(default message NotBlank). Regardless of whether the field has been filled or not.
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\OneToOne;
use Symfony\Component\Validator\Constraints as Assert;
class Employee
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=30)
* #Assert\NotBlank()
* #Assert\Length(min="3")
*/
private $name;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add("name", TextType::class, ['label'=>"Imie"])
->add("submit", SubmitType::class, ["label"=>"Licytuj"]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOprions(OptionsResolver $resolver)
{
$resolver
->setDefaults
(
[
"data_class"=>Employee::class,
'attr'=>array('novalidate'=>'novalidate')
]
);
}
You have a naming issue. Change method name to override configureOptions method.
configureOprions => configureOptions
I couldn't find any issue with the code you posted. Most probably the issue will be on your controller code. Most probably you are loading a validated form into the view in the form load itself. For the better understanding please add your controller and view you are using. Sorry for writing this in the answer section.
Related
In a publishing form, a user can decide to add one or more media, they just need to copy/paste <iframe> tags into text fields.
I would like to find a solution to make sure that the user only enters valid <iframe> tags in this form.
I'm trying to use an #Assert\Regex in my entity, as below, but this doesn't work because no matter what data is entered, it is validated.
I've used the PHP Live Regex online tool to help me write this regex. On this tool, my regex seems to work well.
What's wrong with this regex?
Is using an #Assert\Regex in this case a good practice ?
namespace App\Entity;
use App\Repository\MediaRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass=MediaRepository::class)
*/
class Media
{
// ...
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotNull()
* #Assert\NotBlank()
* #Assert\Regex(
* pattern="/<iframe[^>]*>\s*<\/iframe>/",
* message="Please enter a valid iframe tag"
* )
*/
private $iframe;
Strangely, the regular expression constraint works when initialized from the FormBuilder, like this :
class MediaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('iframe', TextType::class, [
'constraints' => new Regex([
'pattern' => "/^<iframe[^>]*>\s*<\/iframe>/",
'message' => "Please enter a valid iFrame tag",
])
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Media::class,
]);
}
}
I'm trying to make a website with Symfony 4 and Doctrine. I'm a complete beginner (both with Symfony and PHP in general), so I apologise if my question is trivial.
I want to create a database with doctrine, which means that I have to create classes in src/Entity. But I also want to add forms to the site, and they also require classes in src/Entity. I'd like to separate these classes in two subfolders: src/Entity/database and src/Entity/forms. I tried to edit config/packages/doctrine.yaml as follows:
doctrine:
#...
orm:
#...
mappings:
App:
#...
dir: '%kernel.project_dir%/src/Entity/database'
prefix: 'App\Entity\database'
But I when I use bin/console make:entity Entity it creates the file in src/Entity and gives the following error:
[ERROR] Only annotation mapping is supported by make:entity, but the
<info>App\Entity\Entity</info> class uses a different format. If you
would like this command to generate the properties & getter/setter
methods, add your mapping configuration, and then re-run this command
with the <info>--regenerate</info> flag.
When I run bin/console make:entity Entity --regenerate it says:
[ERROR] No entities were found in the "Entity" namespace.
I also tried bin/console make:entity database/Entity, but it fails with:
[ERROR] "App\Entity\Database/Entity" is not valid as a PHP class name (it must start with a letter or underscore,
followed by any number of letters, numbers, or underscores)
If I do the same with a backslash (database\Entity) it creates a DatabaseEntity.php file in the wrong directory and gives the same error as the first one.
Be very careful, because with such approach you might mess your architecture up. This question is a bit opinionated, but I'm gonna tell you how we make it with entities and forms.
First, my strong belief, Entities and Forms should be separated. Therefore, we contain Entites in src/Entity and Forms in src/Form. The connection between them is a FormType, we contain those in src/FormType.
Here's an example User entity contained in src/Entity/User.php:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #UniqueEntity("username")
*
* #ORM\Entity()
* #ORM\Table(name="users")
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #var int
*/
private $id;
/**
* #Assert\NotBlank()
* #Assert\Email
* #Assert\Length(max="255")
*
* #ORM\Column(type="string", length=255, unique=true)
*
* #var string
*/
private $username;
/**
* #ORM\Column(type="string", length=64)
*
* #var string
*/
private $password;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* #param null|string $username
*
* #return User
*/
public function setUsername(?string $username): User
{
$this->username = (string) $username;
return $this;
}
/**
* #return string
*/
public function getPassword(): string
{
return $this->password;
}
/**
* #param null|string $password
*
* #return User
*/
public function setPassword(?string $password): User
{
$this->password = (string) $password;
return $this;
}
}
Now, we need a user to be able to register. For this we create a FormType and a Form. Take a look at src/FormType/User.php:
namespace App\FormType;
use App\Entity;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type as NativeType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
class User extends AbstractType
{
public function getParent(): string
{
return BaseType::class;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// This maps `Entity\User::username` to the respective field
$builder->add(
'username',
NativeType\EmailType::class,
['label' => 'username']
);
// This maps `Entity\User::password` to the respective field
$builder->add(
'password',
NativeType\RepeatedType::class,
[
'constraints' => [new NotBlank()],
'invalid_message' => 'nonMatchingPasswords',
'first_options' => ['label' => 'password'],
'second_options' => ['label' => 'password again'],
'type' => NativeType\PasswordType::class,
]
);
}
// This tells Symfony to resolve the form to the `Entity\User` class
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => Entity\User::class]);
}
}
And now the Form itself, it's src/Form/UserRegistration.php:
namespace App\Form;
use App\FormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type as NativeType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints;
class UserRegistration extends AbstractType
{
public function getParent()
{
// Note this!
return FormType\User::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'fields' => ['username', 'password'],
'translation_domain' => 'forms',
]
);
}
}
And a final stroke on this. In src/Controller/Registration.php we do this:
$form = $this->createForm(
Form\UserRegistration::class,
$user = new Entity\User()
);
The rest (how to handle forms etc.) you know. If you don't, read Symfony docs, they cover it perfectly.
I have cut out / edited some sensitive or non-essential things from this example. For instance, we do not bind password to password, we ask for plain password and then encrypt it. I have not tested the above, so it might not be stable. But for a demonstration on how your architecture should be done it's a good example, IMO.
Symfony version : 2.8.5
Context: I have an entity Restaurant which has a OneToOne relationship with an entity Coordinates which have several relations with other entities related to Coordinates informations. I my backend I create a form related to Restaurant entity with a custom nested form related to Coordinates.
Nota : I use EasyAdminBundle to generate my backend.
Entities relations scheme :
Restaurant
1 ________ 1 `Coordinates`
* ________ 1 `CoordinatesCountry`
1 ________ 1 `CoordinatesFR`
* ________ 1 `CoordinatesFRLane`
* ________ 1 `CoordinatesFRCity`
Backend view :
At this point I try the following scenario :
I create a new Restaurant, so I fill the fields related form for the first time. I let the Coordinates nested form blank (empty fields). So after form submission, validation messages are displayed (see image below).
I edit the previous form and this time I fill the fields of the Coordinates nested form. After form submission, a new Coordinates entity is hydrated and a relationship is created between Restaurant and Coordinates.
Once again I edit the previous form and this time I clear all the fields of the Coordinates nested form. The validation is not triggered and I get the following error :
Expected argument of type "FBN\GuideBundle\Entity\CoordinatesFRCity",
"NULL" given
I precise that in CoordinatesFRType (see code below), to trigger the validations message the first time I had to use the option empty_data with a closure (like described in the official doc) to instatiate a new CoordinatesFR instance in case of empty datas (all fields blank). But here, in this article (written by the creator of the Symfony form component), it is explained (see empty_data and datta mappers paragraphs) that the empty_data is only called at object creation. So I think this the reason why my validation does not work anymore in case of edition.
Question : why the validation is not effective anymore when editing my form and clearing all embedded form ?
The code (only what is necessary) :
Restaurant entity
use Symfony\Component\Validator\Constraints as Assert;
class Restaurant
{
/**
* #ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\Coordinates", inversedBy="restaurant", cascade={"persist"})
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* #Assert\Valid()
*/
private $coordinates;
}
Coordinates entity
use Symfony\Component\Validator\Constraints as Assert;
class Coordinates
{
/**
* #ORM\ManyToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesCountry")
* #ORM\JoinColumn(nullable=false)
*/
private $coordinatesCountry;
/**
* #ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesFR", inversedBy="coordinates", cascade={"persist"})
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* #Assert\Valid()
*/
private $coordinatesFR;
/**
* #ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\Restaurant", mappedBy="coordinates")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $restaurant;
}
CoordinatesFR entity
use Symfony\Component\Validator\Constraints as Assert;
class CoordinatesFR extends CoordinatesISO
{
/**
* #ORM\ManyToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesFRLane")
* #ORM\JoinColumn(nullable=true)
* #Assert\NotBlank()
*/
private $coordinatesFRLane;
/**
* #ORM\ManyToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesFRCity")
* #ORM\JoinColumn(nullable=false)
* #Assert\NotBlank()
*/
private $coordinatesFRCity;
/**
* #ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\Coordinates", mappedBy="coordinatesFR")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $coordinates;
}
Easy Admin config (equivalent to RestaurantType)
easy_admin:
entities:
Restaurant:
class : FBN\GuideBundle\Entity\Restaurant
form:
fields:
- { property: 'coordinates', type: 'FBN\GuideBundle\Form\CoordinatesType' }
CoordinatesType
class CoordinatesType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('CoordinatesCountry', EntityType::class, array(
'class' => 'FBNGuideBundle:CoordinatesCountry',
'property' => 'country',
))
->add('coordinatesFR', CoordinatesFRType::class)
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FBN\GuideBundle\Entity\Coordinates',
));
}
}
CoordinatesFRType
class CoordinatesFRType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('laneNum', TextType::class)
->add('coordinatesFRLane', EntityType::class, array(
'class' => 'FBNGuideBundle:CoordinatesFRLane',
'property' => 'lane',
'placeholder' => 'label.form.empty_value',
))
->add('laneName', TextType::class)
->add('miscellaneous', TextType::class)
->add('locality', TextType::class)
->add('metro', TextType::class)
->add('coordinatesFRCity', EntityType::class, array(
'class' => 'FBNGuideBundle:CoordinatesFRCity',
'property' => 'display',
'query_builder' => function (CoordinatesFRCityRepository $repo) {
return $repo->getAscendingSortedCitiesQueryBuilder();
},
'placeholder' => 'label.form.empty_value',
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FBN\GuideBundle\Entity\CoordinatesFR',
// Ensures that validation error messages will be correctly displayed next to each field
// of the corresponding nested form (i.e if submission and CoordinatesFR nested form with all fields empty)
'empty_data' => function (FormInterface $form) {
return new CoordFR();
},
));
}
}
Because of the problems I had with symfony version 2.7 (404 page error right away after installing a project) I started using Symfony version 3.0. After some minor problems I figured out that "app/console" is replaced by "bin/console". So I'm working now on a new project and I have already build a new bundle with 1 entity called
Codeit/RestaurantBundle && CodeitRestaurantBundle:Reserveren
Format is annotation, and the entity has an id and 1 field called "naam" (string, 255). I updated the schema's, I generate the entities of Codeit and after that was succesfully done I generated a crud with write actions. The format was again annotation and the prefix is /reserveren.
So if I visit the page web/reserveren I am getting a show page of my entity. Unfortunately if I try to add a new entry I am getting the following error:
Expected argument of type "string", "Codeit\RestaurantBundle\Form\ReserverenType" given
My Bundle/Form/ReserverenType.php
<?php
namespace Codeit\RestaurantBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ReserverenType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('naam')
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Codeit\RestaurantBundle\Entity\Reserveren'
));
}
}
My entity code
<?php
namespace Codeit\RestaurantBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Reserveren
*
* #ORM\Table(name="reserveren")
* #ORM\Entity(repositoryClass="Codeit\RestaurantBundle\Repository\ReserverenRepository")
*/
class Reserveren
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Naam", type="string", length=255)
*/
private $naam;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set naam
*
* #param string $naam
*
* #return Reserveren
*/
public function setNaam($naam)
{
$this->naam = $naam;
return $this;
}
/**
* Get naam
*
* #return string
*/
public function getNaam()
{
return $this->naam;
}
}
Forms have changed quite a bit in 3.0. You might be better off sticking with 2.8 for now.
You did not show it but I suspect, based on the error message, that your controller code looks like:
$form = $this->createForm(new ReservernType(), $item);
That is the 2.x way of doing things. For 3.x use:
$form = $this->createForm(ReservernType::class, $item);
http://symfony.com/doc/current/book/forms.html#creating-form-classes
try with:
$builder
->add('naam', TextType::class);
// If you use PHP 5.3 or 5.4 you must use
// ->add('naam','Symfony\Component\Form\Extension\Core\Type\TextType')
instead of this
$builder
->add('naam');
And add the use statement:
use Symfony\Component\Form\Extension\Core\Type\TextType;
Motivation: from the upgrade guide:
Type names were deprecated and will be removed in Symfony 3.0. Instead
of referencing types by name, you should reference them by their
fully-qualified class name (FQCN) instead. With PHP 5.5 or later, you
can use the "class" constant for that:
Hope this help
Try like this:
/.../
use Symfony\Component\Form\Extension\Core\Type\TextType;
/.../
class ReserverenType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('naam', TextType::class);
}
}
add this to your entity's class replace name with an attribute that suits you.
public function __toString() {
return $this->name;
}
I have a form, used for a course subscription, in which I have 2 entity fields, activite and etudiant. I would like NOT to validate this form IF the trainer i already booked (information that i can find in the DB through the entity activite).
How (where...) can i add some logic instructions to control the validation of this form?
Is anybody has an idea? A lead? It would be so simple WITHOUT Symfony (for me)!...
Thanks
class InscriptionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('activiteId', 'entity',
array('label'=>'Activité',
'attr'=>array('class'=>'form-control'),
'class'=>'AssoFranceRussie\MainBundle\Entity\Activite',
'property'=>'nomEtNiveauEtJour',
))
->add('etudiantId', 'entity',array('label'=>'Etudiant',
'attr'=>array('class'=>'form-control'),
'class'=>'AssoFranceRussie\MainBundle\Entity\Etudiant',
'property'=>'NomEtPrenom',
))
;
}
You can write your own constraints and validators by extending the Symfony validation classes.
You need to extend Symfony\Component\Validator\Constraint to define the constraint and Symfony\Component\Validator\ConstraintValidator to define the validation code.
There are probably other ways to do this as well, but this gives you complete control of the validation.
You could do what you want in this way:
1- You need a variable to store the EntityManager in your InscriptionType class:
protected $em;
public function __construct($em) {
$this->em = $em;
}
2- Pass the entity manager to the FormType class from your controller as below:
new InscriptionType( $this->getDoctrine()->getManager() );
3- Add the logic what you want in the setDefaultOptions function to specify the validation_groups what you want for the form:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$p = $this->em->getRepository('YourBundle:YourEntity')->find(1);
if($p){
$resolver->setDefaults(array(
'data_class' => 'YourBundle\Entity\YourEntity',
'validation_groups' => array('myValidation1'),
'translation_domain'=>'custom'
));
}
else{
$resolver->setDefaults(array(
'data_class' => 'YourBundle\Entity\YourEntity',
'validation_groups' => array('myValidation2'),
'translation_domain'=>'custom'
));
}
}
4- In your entity, you need to specify the validation groups for the fields of the entity:
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #Assert\NotNull(groups={"myValidation1"})
*/
private $name;
/**
* #var date
*
* #ORM\Column(name="start", type="date")
* #Assert\NotNull(groups={"myValidation1", "myValidation2"})
* #Assert\Date()
*/
private $start;
In this case, the field 'start' would be validated in both cases, but the first only with the myValidation1 is the group to be validated.
In this way, you could control which fields you want to validate.