Symfony 4.2 DateTimeImmutable validator error - php

I'm using Symfony 4.2.11 on PHP 7.2 with this code:
<?php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\LessThan;
class FormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('startDate', DateType::class, [
"required" => true,
'input' => 'datetime_immutable',
/*'constraints' => [
new LessThan([
'propertyPath' => 'parent.all[endDate].data'
]),
]*/
]);
$builder->add('endDate', DateType::class, [
"required" => true,
'input' => 'datetime_immutable',
}
}
$form = $this->formFactory->create(FormType::class);
//$form->submit($formData);
$form->submit([
"startDate" => new \DateTimeImmutable(),
"endDate" => new \DateTimeImmutable()
]);
if (!$form->isValid()) {
echo("Form invalid");
}
However when I run this code it prints out "Form Invalid". How do I fix this code so it accepts all DateTimeImmutable objects?

Related

How to render the input with VichUploaderBundle?

I've a trouble with the bundle VichUploaderBundle with Symfony 4.
I've follow a tutorial to make an upload with VichUploaderBundle to upload multiple files.
My problem is that the input form to upload the file is never render. But I don't see what it's wrong with my Type file.
In my MemberType who can find all fiels, I've
...
->add('member_files', CollectionType::class, [
'label' => 'Add a file',
'entry_type' => MemberFilesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true
])
...
And my MemberFilesType is :
<?php
namespace App\Form;
use App\Entity\MemberFiles;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichImageType;
class MemberFilesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageFile', VichImageType::class, [
'required' => false,
'download_uri' => true,
'image_uri' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => MemberFiles::class,
]);
}
}
Do you have an idea ?
If you are working with a Member with no MemberFiles yet (empty collection) then that is what is expected. There are no collection entries because there are none persisted, try adding a new MemberFiles to the Member before you create the form.
In your controller action:
if(empty($member->getMemberFiles())){
$member->addMemberFile(new MemberFiles());
}
$form = $this->createForm(MemberType::class, $member);

Symfony FromEvents 1 argument must be an instance of

The error here is not explicit, from what I think it is related just to a Type difference between parameters but i have not found the way to resolve this...
The EventListener is receiving 2 parameters but it doesn't provide the information of which of them is the "1" argument with the issue but I think it is the 2nd due to the error says an instance of UserBundle\Form\FormEvent on singular and the first argument is a generic event from the FormEvents class which is on plural.
Error Shown:
Type error: Argument 1 passed to
UserBundle\Form\UserType::UserBundle\Form{closure}() must be an
instance of UserBundle\Form\FormEvent, instance of
Symfony\Component\Form\FormEvent given
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvents;
class UserType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username',HiddenType::class)
->add('firstname',TextType::class)
->add('lastname',TextType::class)
->add('email', RepeatedType::class,
array('type' => EmailType::class, 'invalid_message' => 'The email fields must match.','options'
=> array('attr' => array('class' => 'email-field')),'required' => true, 'first_options'
=> array('label' => 'Email'),'second_options' => array('label' => 'Confirm email'),))
->add('password', RepeatedType::class,
array('type' => PasswordType::class, 'invalid_message' => 'The password fields must match.','options'
=> array('attr' => array('class' => 'password-field')),'required' => true, 'first_options'
=> array('label' => 'Password'),'second_options' => array('label' => 'Confirm password'),))
->add('save',SubmitType::class)
->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if ($data['firstname'] !== null && $data['lastname'] !== null){
$username = $data['firstname']." ".$data['lastname'];
$data['username'] = $username;
$event->setData($data);
}
});
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'UserBundle\Entity\User'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'user';
}
}
I have followed the steps given by symfony documentation
You're missing the FormEvent class import.
use Symfony\Component\Form\FormEvent;
Since you did not import it, it used the current namespace for the class (UserBundle\Form\FormEvent). But this class doesn't exist.

Symfony 3 FormTypeTest

Following the Symfony 3.0 manual on testing Form Types, I followed the example for testing forms with dependencies as follows;
<?php
namespace Tests\AppBundle\Form;
use AppBundle\Form\Type\Database\OfficeAssignType;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;
class OfficeTypeTest extends TypeTestCase
{
private $entityManager;
protected function setUp()
{
// mock any dependencies
$this->entityManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager');
parent::setUp();
}
protected function getExtensions()
{
// create a type instance with the mocked dependencies
$office = new OfficeType($this->entityManager);
return array(
// register the type instances with the PreloadedExtension
new PreloadedExtension(array($office), array()),
);
}
public function testSubmitValid()
{
$formData = array(
'name' => 'new test office',
'address1' => 'Test new office address',
'city' => 'Test office city',
'phone' => 'testoffice#stevepop.com',
'country' => '235',
'currrency' => '1',
);
// Instead of creating a new instance, the one created in
// getExtensions() will be used
$form = $this->factory->create(OfficeType::class);
// submit data to form
//$form->submit($formData);
//$this->assertTrue($form->isSynchronized());
}
}
When I run the tests, I get the following error
Argument 1 passed to Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must be an instance of Doctrine\Common\Persistence\ManagerRegistry, none given, called in /www/stevepop.com/symfony/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php on line 85 and defined
The OfficeType is below;
namespace AppBundle\Form\Type\Database;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class OfficeType extends AbstractType {
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', TextType::class, array(
'required' => true,
));
$builder->add('address1', TextType::class, array(
'required' => true,
));
$builder->add('country', EntityType::class, array(
'class' => 'AppBundle:Country',
'attr' => array(
'class' => 'input-sm',
),
'required' => true,
'placeholder' => "Non selected",
));
$builder->add('currency', EntityType::class, array(
'class' => 'AppBundle:Currency',
'attr' => array(
'class' => 'input-sm',
),
'required' => true,
'placeholder' => "Non selected",
));
}
I am not sure what this means to be honest and will appreciate help from anyone who has done FormType unit tests in Symfony 2.8/3.0.
Thank you.
Try to adding this to your tests:
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
use Symfony\Component\Form\Extension\Core\CoreExtension;
class OfficeType extends AbstractType {
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
public function setUp()
{
$this->em = DoctrineTestHelper::createTestEntityManager();
parent::setUp();
}
protected function getExtensions()
{
$manager = $this->createMock('Doctrine\Common\Persistence\ManagerRegistry');
$manager->expects($this->any())
->method('getManager')
->will($this->returnValue($this->em));
$manager->expects($this->any())
->method('getManagerForClass')
->will($this->returnValue($this->em));
return array(
new CoreExtension(),
new DoctrineOrmExtension($manager),
);
}
// your code...
}

silex form validation with dependencies

I've built a form with the silex framework that contains some checkboxes and a textfield. The user has to check at least one checkbox or write something in the textfield. Now I don't know how to validate such a dependency or better where to put the validation logic. I could add constraints to the single fields but how can I implement the dependency that either the checkboxes are validated or the textfield?
This is my validation code in the controller class.
public function validateAction(Request $request, Application $app)
{
$form = $app['form.factory']->create(new ApplicationForm());
$form->bind($request);
if ($form->isValid()) {
return $app->json(array(
'success' => true,
));
}
}
The ApplicationForm class looks like this (simplified):
class ApplicationForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('crafts', 'choice', array(
'choices' => array(
array('drywall' => 'Drywall'),
array('painter' => 'Painter'),
array('plasterer' => 'Plasterer'),
array('carpenter' => 'Carpenter'),
array('electrician' => 'Electrician'),
array('plumber' => 'Plumber'),
array('tiler' => 'Tiler'),
array('bricklayer' => 'Bricklayer'),
),
'multiple' => true,
'expanded' => true,
'required' => false,
))
->add('craftsOther', 'text', array(
'attr' => array('class' => 'textinput', 'placeholder' => 'Other', 'maxlength' => 256),
'constraints' => array(
new Assert\Length(array('max' => 256, 'maxMessage' => $this->_errorMessages['crafts_other_max'])),
),
'required' => false,
));
}
}
Any ideas how to do this in an elegant way?
You can add constraints to the form itself, not just individual fields. You can pass an array of constraints in the options:
Here is a working example that checks both fields are not equal when submitted.
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Silex\Application;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Constraints as Assert;
$app = new Application();
$app['debug'] = true;
$app->register(new \Silex\Provider\FormServiceProvider());
$app->register(new Silex\Provider\TranslationServiceProvider(), array('translator.domains' => array(),));
$app->register(new Silex\Provider\ValidatorServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider());
class ExampleFormType extends AbstractType
{
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options)
{
$builder
->add('apple')
->add('banana')
->add('submit', SubmitType::class);
}
public function configureOptions(\Symfony\Component\OptionsResolver\OptionsResolver $resolver)
{
$resolver->setDefaults([
'constraints' => [
new Assert\Callback(function (array $data, ExecutionContextInterface $context) {
if ($data['apple'] === $data['banana']) {
$context
->buildViolation('Apple and Banana cannot be the same')
->atPath('apple')
->addViolation();
}
}),
],
]);
}
}
$app->match('/', function (Request $request, Application $app) {
$form = $app['form.factory']->create(ExampleFormType::class)->handleRequest($request);
if ($form->isValid()) {
echo "Valid Submission";
}
return $app['twig']->createTemplate('{{ form(form) }}')->render([
'form' => $form->createView(),
]);
})->method('GET|POST');
$app->run();

Symfony2 form validator groups without entities

I'm using Symfony2 form component to build and validate forms. Now I need to setup validator groups based on a single field value, and unfortunately it seems that every example out there is based on entities - which im not using for several reasons.
Example:
If task is empty, all constraint validators should be removed, but if not, it should use the default set of validators (or a validator group).
In other words, what I'm trying to achieve is making subforms optional, but still be validated if a key field is populated.
Can someone possible give me an example how to configure it?
<?php
namespace CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints as Assert;
use CoreBundle\Form\Type\ItemGroupOption;
class ItemGroup extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text', array(
'label' => 'Titel',
'attr' => array('class' => 'span10 option_rename'),
'required' => false
));
$builder->add('max_selections', 'integer', array(
'label' => 'Max tilvalg',
'constraints' => array(new Assert\Type('int', array('groups' => array('TitleProvided')))),
'attr' => array('data-default' => 0)
));
$builder->add('allow_multiple', 'choice', array(
'label' => 'Tillad flere valg',
'constraints' => array(new Assert\Choice(array(0,1))),
'choices' => array(0 => 'Nej', 1 => 'Ja')
));
$builder->add('enable_price', 'choice', array(
'label' => 'Benyt pris',
'constraints' => array(new Assert\Choice(array(0,1))),
'choices' => array(0 => 'Nej', 1 => 'Ja'),
'attr' => array('class' => 'option_price')
));
$builder->add('required', 'choice', array(
'label' => 'Valg påkrævet',
'constraints' => array(new Assert\Choice(array(0,1))),
'choices' => array(0 => 'Nej', 1 => 'Ja')
));
$builder->add('options', 'collection', array(
'type' => new ItemGroupOption(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false
)
);
$builder->add('sort', 'hidden');
}
public function getName()
{
return 'item_group';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
global $app;
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) use ($app) {
// Get submitted data
$data = $form->getData();
if (count($app['validator']->validateValue($data['title'], new Assert\NotBlank())) == 0) {
return array('TitleProvided');
} else {
return false;
}
},
));
}
}
If you are using 2.1 you may want to have a look at "Groups based on submitted data".
Update
Example using the demo contact page /demo/contact in the default AcmeDemoBundle provided with Symfony Standard Edition :
The form type with conditional validation groups :
namespace Acme\DemoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints as Assert;
class ContactType extends AbstractType
{
// Inject the validator so we can use it in the closure
/**
* #var Validator
*/
private $validator;
/**
* #param Validator $validator
*/
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', 'email');
$builder->add('message', 'textarea', array(
// Added a constraint that will be applied if an email is provided
'constraints' => new Assert\NotBlank(array('groups' => array('EmailProvided'))),
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// This is needed as the closure doesn't have access to $this
$validator = $this->validator;
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) use ($validator) {
// Get submitted data
$data = $form->getData();
$email = $data['email'];
// If email field is filled it will not be blank
// Then we add a validation group so we can also check message field
if (count($validator->validateValue($email, new Assert\NotBlank())) == 0) {
return array('EmailProvided');
}
},
));
}
public function getName()
{
return 'contact';
}
}
Don't forget to inject the validator service in the form type :
<?php
namespace Acme\DemoBundle\Controller;
//...
class DemoController extends Controller
{
// ...
public function contactAction()
{
$form = $this->get('form.factory')->create(new ContactType($this->get('validator')));
// ...
}
}
As you can see validation of the message field will be triggered only if the email field is filled.
Use a diff tool to catch the differences.

Categories