If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Let's use the fancy new date picker. Fill in the other fields and hit submit.
Whoa! Validation Error!
First, the good news: the error looks nice! And we didn't do any work for that.
Now, the bad news: do you remember adding any validation? Because I don't! So, where is that coming from?
What you're seeing is a really amazing automatic thing that Symfony does. And I invented a term for it: "sanity validation".
Remember, even though this has a fancy date picker widget, this is just a text field, and it needs to be filled in with a very specific format. In this case, the widget fills in month/day/year.
But what if Symfony expects a different format - like year/month/day?
Well, that's exactly what's happening: Symfony expects the date in one format, but the widget is generating something else. When Symfony fails to parse the date, it adds the validation error.
So, it turns out that many fields have sanity validation. It basically makes sure that a sane value is submitted by the user. If an insane value is sent, it blocks the way and shows a message.
For example, the speciesCount
is using Symfony's NumberType
, which renders as
an HTML5 field with up and down arrows on some browsers:
... lines 1 - 11 | |
class Genus | |
{ | |
... lines 14 - 31 | |
/** | |
* @ORM\Column(type="integer") | |
*/ | |
private $speciesCount; | |
... lines 36 - 137 | |
} |
If we tried to type a word here and submit, the NumberType
would throw a validation
error thanks to sanity validation.
Our drop-down fields also have sanity validation. If a jerk user tries to hack the system by adding an option that we did not originally include, the field will fail validation. 99% of the time, you don't know or care that this is happening. Just know, Symfony has your back.
But how do we fix the date field? We just need to make Symfony's expected format match
the format used by the datepicker. In fact, the format
itself is an option on the
DateType
. I'll hold command
and click into DateType
. When you use the single_text
widget, it expects this HTML5_FORMAT
: so year-month-day.
Let's update the JavaScript widget to match this.
How? On its docs, you'll see that it also has a format
option. Cool!
Now, unfortunately, the format string used by the DateType and the format string
used by the datepicker widget are not exactly the same format - each has its own
system, unfortunately. So, you may need to do some digging or trial and error.
It turns out, the correct format is yyyy-mm-dd
:
... lines 1 - 8 | |
{% block javascripts %} | |
... lines 10 - 13 | |
<script> | |
jQuery(document).ready(function() { | |
$('.js-datepicker').datepicker({ | |
format: 'yyyy-mm-dd' | |
}); | |
}); | |
</script> | |
{% endblock %} | |
... lines 22 - 37 |
OK, go back and refresh that page. Fill out the top fields... and then select a date. Moment of truth. Got it!
So I keep telling you that the purpose of the field "types" is to control how a field is rendered. But that's only half of it. Behind the scenes, many fields have a "data transformer".
Basically, the job of a data transformer is to transform the data that's inside of
your PHP code to a format that's visible to your user. For example, the firstDiscoveredAt
value on Genus
is actually a DateTime
object:
... lines 1 - 11 | |
class Genus | |
{ | |
... lines 14 - 46 | |
/** | |
* @ORM\Column(type="date") | |
*/ | |
private $firstDiscoveredAt; | |
... lines 51 - 137 | |
} |
The data transformer internally changes that into the string that's printed in the box.
Then, when a date string is submitted, that same data transformer does its work in
reverse: changing the string back into a DateTime
object.
The data transformer is also kicking butt on the subFamily
field. The id
of the
selected SubFamily
is submitted. Then, the data transformer uses that to query
for a SubFamily
object and set that on Genus
.
You don't need to know more about data transformers right now, I just want you to realize that this awesomeness is happening.
Hi,
I have a similar setup where I have a value that is a DateTime object (your screen shot shows @ORM\Column(type="date")). I got everything to work, but when I submit the form the date is being added without the time. I have tried using the DateTime form type, but it returns NULL when I submit the form. Any ideas?
Yo Mike!
Hmm, is your @ORM\Column a type="date" or type="datetime"? If it's "date", change it to "datetime": our form is correctly submitting a DateTime with time, but then Doctrine is saving only the date part.
I'm not sure why the DateTimeType on the form would be returning null, however. If the above doesn't give you a hint, feel free to post some code (the important part if your entity, form, controller) and we'll see if we can spot the issue :).
Cheers!
Thanks for getting back to me.
My @ORM\Colum is type="datetime". Take a look below at my setup:
Entity:
/**
* @ORM\Column(type="datetime")
*/
protected $createdOn;
Controller:
$form = $this->createForm(JobTitleFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$jobTitle = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($jobTitle);
$em->flush();
$this->addFlash('success', 'Job Title "' . $form->get("jobTitle")->getData(). '" has been added.');
//return new Response($email);
return $this->redirectToRoute('setup_jobtitles');
}
FormType:
->add('createdOn', DateTimeType::class, [
'widget' => 'single_text',
'html5' => false,
]);
Twig:
<div class="form-group" id="jsDatepicker">
{{ form_label(jobTitleForm.createdOn) }}
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
{{ form_widget(jobTitleForm.createdOn, { 'attr': {'class': 'form-control' } } ) }}
</div>
</div>
When I submit the form it will add it to the DB like this:
2016-11-30 00:00:00
It turns out that if you set your input field to disabled it will return a NULL value. If you use readonly instead it will work just fine. I did not include the disabled bit in my code above, but that is how I had my form originally. I was also using a datepicker that did not include time so I needed to use the bootstrap datetimepicker to resolve that. In the end I decided to use readonly and add today's date by default.
Good detective work Mike! The disabled thing is by default (but I could see how that could be a gotcha) - so that if you have a disabled field that already has a value, a "clever" user can't just modify it in the HTML and submit. I believe this is based on some official spec somewhere (that disabled input should be ignored, but readonly shouldn't), but I can't remember for sure :).
Glad you sorted it out!
I have set 'widget' => 'single_text', 'format' => 'dd/MM/yyyy' and it won't work. I need to use spanish format for single_text (html5 false, jquery datapicker is configured to dateFormat: 'dd/mm/yy'). It works, if I set both config to expected Symfony format, but I need to provide users dd/MM/yyyy format. Thanks in advance.
Hey Vkgroup!
Hmm. So, you are setting Symfony's format to dd/MM/yyyy
and jQuery's date picker format also to dd/MM/yyyy
. I think I might know the problem. I believe Symfony's date field and jQuery's date picker use different "ways" to describe the correct format. See the red warning box at the bottom of this section: http://symfony.com/doc/current/reference/forms/types/date.html#rendering-a-single-html5-textbox (it starts with "The string used by a JavaScript date picker...").
For jQuery's date picker, you may need to use yyyy/mm/dd
... or something similar.
Let me know if that helps! Cheers!
Hi,
I'm following this tutorial. I added :
$('.js-datepicker').datepicker();
as was mentioned in the tutorial, entered Genus data in the form, selected date and hit save. I was expecting to see a validation error like the tutorial but I got a *bigggg* explosion:
Warning: IntlDateFormatter::parse(): Date parsing failed
500 Internal Server Error - ContextErrorException
in vendor\symfony\symfony\src\Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer.php at line 123
// to DST changes
$dateOnly = $this->isPatternDateOnly();
$timestamp = $this->getIntlDateFormatter($dateOnly)->parse($value);
if (intl_get_error_code() != 0) {
throw new TransformationFailedException(intl_get_error_message());
any idea what's going on?
thanks
Hey Mehdi,
Hm, looks like Symfony can't parse the date you passed. Try to specify the next format when initializing datepicker:
$('.js-datepicker').datepicker({
format: 'yyyy-mm-dd'
});
Does it work? The problem could be in Intl version, I suppose you're on Windows, right? I also wonder what Symfony version do you use? I think as much as you can do here is to update your application to the latest Symfony version. It should probably fix the problem. But maybe not, then probably you need also to upgrade Intl on your computer, but it's difficult to do on Windows as I know. So probably, the easiest way is to pass the correct date format.
Cheers!
Hey Joram,
Most probably you opened a wrong file. Please, double check the path - it should be: ./vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateType.php
. Also, the namespace of this file should be Symfony\Component\Form\Extension\Core\Type
.
Cheers!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
"doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0", // v3.1.3
"nelmio/alice": "^2.1", // 2.1.4
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}
Is there a way to change the Symfony's date format, without showing the ugly "2016-10-10" to the user?