Symfony2 DateTime null accept - symfony

So, I want to be able send a null option to my DOB field.
Here is my form builder:
->add('birthDate', DateType::class, array(
'widget' => 'single_text',
'format' => 'yyyy-MM-dd'))
And here is those field in my entity
/**
* #ORM\Column(
* type="date",
* nullable=true
* )
* #JMS\Groups("single")
*
* #var \DateTime
*/
protected $birthDate;
When I`m trying to send a null I got an error msg
Expected argument of type "DateTime", "NULL" given
any ideas?
CRITICAL - Uncaught PHP Exception Symfony\Component\PropertyAccess\Exception\InvalidArgumentException: "Expected argument of type "DateTime", "NULL" given" at /var/www/server.local/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php line 253
$type = $trace[$i]['args'][0];
$type = is_object($type) ? get_class($type) : gettype($type);
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type));
}
}

In this case, the problem was caused by PHP type hinting.
If you use type hinting (for instance setBirthDate(\DateTime $value)) then PHP forces you that you actually provide a DateTime object. Obviously, null is not such an object. To resolve this problem, it is possible to give $value a default value like this: setBirthDate(\DateTime $value = null).
This is documented behavior and explained in the PHP Documentation (http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration).
Relevant passage:
To specify a type declaration, the type name should be added before the parameter name. The declaration can be made to accept NULL values if the default value of the parameter is set to NULL.

The problem occurs due type-hinted setter as it is mentioned in the comments. There are two solutions:
1. Use 'by_reference' => true on your form:
$builder->add(
'birthDate',
DateType::class,
[
'widget' => 'single_text',
'format' => 'yyyy-MM-dd',
'by_reference' => true,
]
);
2. Let your setter accept null:
public function setBirthDate(\DateTime $value = null)
{
.....
}

Don't pass any values to it. Make the field not required by doing this:
->add(
'birthDate',
DateType::class,
array(
'required' => false,
'widget' => 'single_text',
'format' => 'yyyy-MM-dd'
)
)

I have been using DOB field in my project. Try this.
My ORM file looks like this <field name="dob" type="date" column="dob" nullable="true"/>
->add('dob','birthday',array(
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
'required' => false,
'attr' => array('class' => 'datepicker',
'data-provide' => 'datepicker','data-date-format' => 'dd-mm-yyyy')
))

Related

How to prefill field of type EntityType from PHP

In my form, I have a field of type EntityClass:
$builder
->add(
'user',
EntityType::class,
[
'required' => false,
'label' => 'User',
'placeholder' => 'Please choose',
'choice_label' => 'email',
'choice_value' => 'id',
'class' => 'AppBundle:User',
]
)
;
This field works fine - until I try to pre-fill it from my PHP code. Then it stays empty, and only shows "Please choose".
Pre-filling looks like this:
$user = $this->userRepository->find(...);
$form->get('user')->setData($user);
But it also does not work if I call ->setData($user->getId()), or even ->setData($user->getEmail()).
So how do I prefill a field of type EntityType?
You should not prefill Form, you should prefill Model, if you need it.
$user = $this->userRepository->find(...);
$entity = new YourEntity();
$entity->setUser($user);
$form = $this->createForm(YourEntity::class, $entity);
And it's not about EntityType. It's about any Type in Symfony - there is no way to bind a default value for them. Data is binded on Model.
UPD from comment: It's not true, that Form could be used without Model. It could be used without Doctrine Entity or any other ORM (or not ORM) Entity. But they still operate with data, i.o. with model.
\Symfony\Component\Form\FormFactoryInterface has definition
public function create($type = 'form', $data = null, array $options = array());
So some kind of $data is always present when you're using Form Component.

Symfony2, Sonata, FormMapper, add hidden field to be handled in PrePersist/PreUpdate

I actually did some tricks so i could be able to persist a user if its ID is passed by an url parameter. (Custom action from user list).
/admin/se/api/bundle/create?user=7
I actually could not find how to send the user entity returned by a findByOne(array('id' => $user_id)) so i guess i'll need to pass the $user_id through a hidden field and handle its value in a PrePersist
Otherwise passing the id that way
->add('user', 'hidden', array('data' => $user_id))
will return an error :
This value is not valid.
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[user] = 7
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Compound forms expect an array or NULL on submission.
This is my first attempt that is not working :
$container = $this->getConfigurationPool()->getContainer();
$request = $container->get('request');
$user_id = $request->get('user');
if(!empty($user_id)){
$em = $this->getModelManager()->getEntityManager($this->getClass());
$user = $em->getRepository('ApiBundle:User')->findOneBy(array('id' => $user_id));
if($user){
$formMapper
->with('User', array('description' => '<strong>User : </strong>'.$user->getDisplayName()))
->add('user', 'hidden', array('data' => $user_id))
// this of course doesn't work as explained above. How can i have my own hidden input not related to any property
->end();
}
So how would i do that? Any better solution is welcomed.
Well this is the best trick i found. I wish 'sonata_type_model_hidden' has more options. I guess i could do my own custom field to be able to do that. But i'm not sure how and anyway this solution is fast to implement.
$formMapper
->with('Guide', array('description' => '<strong>Guide : </strong>'.$guide->getDisplayName()))
->add('guide', 'sonata_type_model_autocomplete', array(
'property' => array('firstname', 'lastname', 'username', 'email'),
'data_class' => null, // IMPORTANT
'data' => $guide,
'attr' => array('class' => 'sonata-autocomplete-hidden'), // custom class
'label_attr' => array('class' => 'sonata-autocomplete-hidden'), // custom class
)
)
->end();
To hide the field :
.sonata-autocomplete-hidden{
display:none;
}
If you have any better solutions, you're welcome.

Symfony2 form validation - this value is not valid

I got a form in Symfony but it won't validate and I don't know why.
From UserBundle/Entity/Task.php
/**
*
* #ORM\ManyToMany(targetEntity="Project\TaskBundle\Entity\Task", inversedBy="users")
* #ORM\JoinTable(name="project_users_tasks")
*
*/
protected $tasks;
From UserBundle/Form/CreateUserFormType.php which is my formbuilder:
$builder->add('tasks', 'entity', array(
'class' => 'TaskBundle:Task',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('b')
->andWhere('b.owner = :owner')
->setParameter('owner', $this->securityContext->getToken()->getUser())
->addOrderBy('b.updated', 'ASC');
},
'expanded' => false,
'multiple' => false
));
The post request in my browser:
------WebKitFormBoundary6IBT2Ycy78N9AI7u
Content-Disposition: form-data; name="createUser[tasks][]"
14
The result I get for the form is an error concerning the tasks:
"This value is not valid"
I got no other validation what so ever. So why can't the task 14(which is clean the dishes for example) be assigned to my user? I mean the id of the task should work or not?
Edit:
Symfony doesn't seem to recognize the data, that's why. A print_r of $form->getData();
[tasks:protected] => Doctrine\Common\Collections\ArrayCollection Object
(
[_elements:Doctrine\Common\Collections\ArrayCollection:private] => Array
(
)
)
But how can that be? I can see that my browser is posting the data.
At first glance, there are two brackets too much in your submitted field name: createUser[tasks][] should be createUser[tasks], since the entity field is a collapsed, single-valued entity field.
Also try to debug Symfony's request object. var_dump($request->request->get('createUser')) should return something like
array(
'tasks' => 14,
)
If, on the other hand, you really want to submit one or more tasks, set the "multiple" option on the entity field to true. Then the request data should be something like
array(
'tasks' => array(
0 => 14,
)
)

Symfony2 - Give a default filter in a list of elements of Sonata Admin

I have a list of elements of type Vehicle and I show these elements with Sonata Admin. I allow to filter these elements by the "status" field, but I want that, when the list is showed, only the active vehicles are showed, and if somebody wants to see the inactive vehicles, uses the filter and select the inactive status. I would like to know if somebody Knows the way to apply filters by default for a list of elements using Sonata Admin.
Here is my code:
public function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('status')
;
}
protected function configureDatagridFilters(DatagridMapper $mapper)
{
$mapper
->add('name')
->add('status')
;
}
Is there any option that can be added to the status field in configureDatagridFilters() to achieve this goal? Other options?
Thanks in advance.
You have to override $datagridValues property as following (for status > 0 if it's an integer) :
/**
* Default Datagrid values
*
* #var array
*/
protected $datagridValues = array (
'status' => array ('type' => 2, 'value' => 0), // type 2 : >
'_page' => 1, // Display the first page (default = 1)
'_sort_order' => 'DESC', // Descendant ordering (default = 'ASC')
'_sort_by' => 'id' // name of the ordered field (default = the model id field, if any)
// the '_sort_by' key can be of the form 'mySubModel.mySubSubModel.myField'.
);
source: Configure the default page and ordering in the list view
You can also use this method
public function getFilterParameters()
{
$this->datagridValues = array_merge(
array(
'status' => array (
'type' => 1,
'value' => 0
),
// exemple with date range
'updatedAt' => array(
'type' => 1,
'value' => array(
'start' => array(
'day' => date('j'),
'month' => date('m'),
'year' => date('Y')
),
'end' => array(
'day' => date('j'),
'month' => date('m'),
'year' => date('Y')
)
),
)
),
$this->datagridValues
);
return parent::getFilterParameters();
}
Using both above suggested approaches will break the filters "reset" behaviour since we are always forcing the filter to filter by a default value. To me, i think the best approach is to use the getFilterParameters function (since we can add logic in there instead of statically add the value) and check if the user clicked the "Reset button"
/**
* {#inheritdoc}
*/
public function getFilterParameters()
{
// build the values array
if ($this->hasRequest()) {
$reset = $this->request->query->get('filters') === 'reset';
if (!$reset) {
$this->datagridValues = array_merge(array(
'status' => array (
'type' => 1,
'value' => 0
),
),
$this->datagridValues
);
}
}
return parent::getFilterParameters();
}
Since sonata-admin 4.0, the function getFilterParameters() is tagged as final and the $datagridValues doesn't exist anymore.
So you need to override the configureDefaultFilterValues() function
protected function configureDefaultFilterValues(array &$filterValues): void
{
$filterValues['foo'] = [
'type' => ContainsOperatorType::TYPE_CONTAINS,
'value' => 'bar',
];
}
More details: https://symfony.com/bundles/SonataAdminBundle/current/reference/action_list.html#default-filters
Another approach is to use createQuery and getPersistentParameters to enforce invisible filter. This approach is best to have fully customizable filters. See my articles here:
http://www.theodo.fr/blog/2016/09/sonata-for-symfony-hide-your-filters/

SonataAdminBundle set NULL $formMapper

how to set field NULL if nothing was writen in the input ?
I would like to have a checkbox to NULLIFY the field. (even if the field is blank, need NULL in mysql field)
Let me suggest a valid way to accomplish that in symfony2 and sonata-admin
specifically used for a boolean field with nullable = true
(nb: mysql field boolean in symfony2 are actually tinyint:1)
once it is edited, such a field would normally pass to true but if you intercept the request and evaluate the value of a given field you can modify it like this:
in your admin class, assuming the property of the object is property
public function prePersist($object)
{
if (in_array($object->getProperty(), array(NULL, '', '3',)))
$object->setProperty(NULL);
}
public function preUpdate($object)
{
if (in_array($object->getProperty(), array(NULL, '', '3',)))
$object->setProperty(NULL);
}
the number 3 is what I implemented in my case for a select where null is needed
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('property', 'choice', array(
'choices' => array(
'3' => 'Yes and No',
'0' => 'No',
'1' => 'Yes',
),
'empty_value' => false,// unset this and empty would work also
'required' => false,
))
// ->add('property', null, array('required' => false))// checkbox if bool
;
}
Hope that helps!
Cheers,
Vince

Resources