Symfony Forms and Bootstrap Datetimepicker

Eneko
enekochan
Published in
3 min readNov 21, 2015

In this example I’m going to use the spanish locale and the “Europe/Madrid” timezone.

First we need an entity that will hold the DateTime, for example a Booking entity:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="bookings")
*/
class Booking
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @ORM\Column(type="datetime")
*/
protected $date;

/**
* Constructor
*/
public function __construct()
{
$this->date = new \DateTime();
}

/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Set date
*
* @param \Datetime $date
* @return Booking
*/
public function setDate($date)
{
$this->date = $date;

return $this;
}

/**
* Get date
*
* @return \Datetime
*/
public function getDate()
{
return $this->date;
}
}

Our entity uses a DateTime object but the field in the form is going to be an input that can only have text. That’s why we need a DataTransformer. It will transform a DateTime object to string and vice versa when needed:

<?php

namespace AppBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class DateTimeTransformer implements DataTransformerInterface
{
/**
* Transforms an object (DateTime) to a string.
*
* @param DateTime|null $datetime
* @return string
*/
public function transform($datetime)
{
if (null === $datetime) {
return '';
}

return $datetime->format('d/m/Y H:i');
}

/**
* Transforms a string to an object (DateTime).
*
* @param string $datetime
* @return DateTime|null
*/
public function reverseTransform($datetime)
{
// datetime optional
if (!$datetime) {
return;
}

return date_create_from_format('d/m/Y H:i', $datetime, new \DateTimeZone('Europe/Madrid'));
}
}

Now the form class. You can see how addModelTransformer is used to link the form date field with the DataTransformer. When defining the date field it's important to render it as a text field and use the attr option to add a data-provide and a data-format that will be added in the HTML input field as attributes. Also some CSS classes are needed (form-control input-inline datetimepicker), you can add them with attr here or later in the Twig template. I'll show both but only one of them is needed.

<?php

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Form\DataTransformer\DateTimeTransformer;

class BookingFormType extends AbstractType
{
private $class;

/**
* @param string $class The Booking class name
*/
public function __construct($class)
{
$this->class = $class;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date', 'text', array(
'required' => true,
'label' => 'form.label.datetime',
'translation_domain' => 'AppBundle',
'attr' => array(
'class' => 'form-control input-inline datetimepicker',
'data-provide' => 'datepicker',
'data-format' => 'dd-mm-yyyy HH:ii',
),
))
->add('submit', 'submit')
;

$builder->get('date')
->addModelTransformer(new DateTimeTransformer());
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'data_class' => $this->class,
'intention' => 'edit',
)
);
}

public function getName()
{
return 'booking_edit';
}
}

Let’s register the form as a service:

services:
app.form.type.booking_edit:
class: AppBundle\Form\Type\BookingFormType
arguments: [AppBundle\Entity\Booking]
tags:
- { name: form.type, alias: booking_edit }

In the template we’ll include the form and the needed JavaScript and CSS for jQuery, MomentJS, Bootstrap and Bootstrap Datetimepicker. Remember that MomentJS must be loaded BEFORE Bootstrap Datetimepicker. Also just before the body closing tag we have to call the datetimepicker method on the input field. It's very important to add "useCurrent: false" if you want to load a datetime as we do in this example.

<html>
<head>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/css/bootstrap-datetimepicker-standalone.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
<title>Symfony and Bootstrap Datetimepicker</title>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>Symfony and Bootstrap Datetimepicker</h1>
<div class="row">
{{ form_start(form) }}
{{ form_errors(form) }}
<div class="form-group">
{{ form_label(form.date, null, { 'label_attr': {'class': 'col-sm-2 control-label'} }) }}{{ form_errors(form.date) }}
<div class='input-group date' id='datetimepicker'>
{{ form_widget(form.date, {'attr': {'class': 'form-control input-inline datetimepicker'}}) }}
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
{{ form_row(form.submit, { 'label': 'OK' }) }}
</div>
{{ form_end(form) }}
</div>
</div>
</div>
</div>

<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment-with-locales.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript">
$('#datetimepicker').datetimepicker({
locale: 'es',
useCurrent: false,
sideBySide: true
});
</script>
</body>
</html>

The controller is very simple (the handling of the form is not shown):

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use AppBundle\Entity\Booking;
use AppBundle\Form\Type\BookingFormType;

class MainController extends Controller
{
/**
* @Route("/booking/edit/{id}", name="booking_edit")
* @ParamConverter("id", class="AppBundle:Booking")
* @param Request $request The Request object
* @param Booking $booking The Booking object
* @return Response A Response instance
*/
public function formAction(Request $request, Booking $booking)
{
$form = $this->createForm(new BookingFormType('\AppBundle\Entity\Booking'), $booking);

$form->handleRequest($request);

if ($form->isValid()) {
...
}

return $this->render('form.html.twig', array(
'form' => $form->createView(),
));
}
}

Ref: https://eonasdan.github.io/bootstrap-datetimepicker/
http://ajaxray.com/blog/symfony2-forms-bootstrap-3-datepicker-for-date-field
http://symfony.com/doc/current/reference/forms/types/datetime.html
http://symfony.com/doc/current/cookbook/form/data_transformers.html

--

--