Symfony Bundle

Available versions:

Installation

composer require aeon-php/symfony-bundle

Configuration

Register Bundle
<?php

// AppKernel.php or bundles.php

$bundles = [
    new Aeon\Symfony\AeonBundle\AeonBundle(),
];
Configure Bundle
# config/packages/aeon.yaml

aeon:
  calendar_timezone: 'UTC'
  calendar_holidays_factory_service: 'calendar.holidays.factory.google'
  ui_timezone: 'America/Los_Angeles'
  ui_datetime_format: 'Y-m-d H:i:s'
  ui_date_format: 'Y-m-d'
  ui_time_format: 'H:i:s'

Aeon Symfony Form Types

  • Aeon\Symfony\AeonBundle\Form\Type\AeonDayType
  • Aeon\Symfony\AeonBundle\Form\Type\AeonDateTimeType
  • Aeon\Symfony\AeonBundle\Form\Type\AeonTime
  • Aeon\Symfony\AeonBundle\Form\Type\AeonTimeZone

Aeon Symfony Validators

  • Aeon\Symfony\AeonBundle\Validator\Constraints\After
  • Aeon\Symfony\AeonBundle\Validator\Constraints\AfterOrEqual
  • Aeon\Symfony\AeonBundle\Validator\Constraints\Before
  • Aeon\Symfony\AeonBundle\Validator\Constraints\BeforeOrEqual
  • Aeon\Symfony\AeonBundle\Validator\Constraints\Equal
  • Aeon\Symfony\AeonBundle\Validator\Constraints\Holiday
  • Aeon\Symfony\AeonBundle\Validator\Constraints\NotHoliday

How to get current time in Symfony

The easiest, cleanest and most reliable way to get current DateTime in Symfony is to use Calendar. For example in order to get current time in Symfony Controller use following code (AeonBundle register Calendar as Autowired service).

Controller
<?php

namespace App\Controller;

use Aeon\Calendar\Gregorian\Calendar;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class AeonCalendarController extends AbstractController
{
    private Calendar $calendar;

    public function __construct(Calendar $calendar)
    {
        $this->calendar = $calendar;
    }

    /**
     * @Route("/aeon/calendar", name="aeon_calendar")
     */
    public function index(): Response
    {
        return $this->render('aeon_calendar/index.html.twig', [
            'calendar' => $this->calendar
        ]);
    }
}
Twig

How to test features that depends on time in Symfony

AeonBundle automatically replace GregorianCalendar with GregorianCalendarStub instance in test environment.

Knowing that we must just get the GregorianCalendarStub::class class from the service container (GregorianCalendarStub::class is an alias for Calendar::class) and use setNow method.

<?php

namespace App\Tests;

use Aeon\Calendar\Gregorian\DateTime;
use Aeon\Calendar\Gregorian\GregorianCalendarStub;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class AeonCalendarControllerTest extends WebTestCase
{
    public function testSomething()
    {
        $client = static::createClient();

        $client->getContainer()->get(GregorianCalendarStub::class)->setNow(DateTime::fromString('2020-01-01 00:00:00 America/Los_Angeles'));

        $client->request('GET', '/aeon/calendar');

        $this->assertResponseIsSuccessful();
        $this->assertSelectorTextContains('code#calendar-year', '2020');
        $this->assertSelectorTextContains('code#calendar-month', '2020-01');
        $this->assertSelectorTextContains('code#calendar-day', '2020-01-01');
        $this->assertSelectorTextContains('code#calendar-datetime', '2020-01-01T00:00:00-08:00');
    }
}

How to check in Symfony Form if a given date is a holiday

Aeon Symfony Bundle provides two Symfony Validators.

  • Aeon\Symfony\AeonBundle\Validator\Constraints\Holiday
  • Aeon\Symfony\AeonBundle\Validator\Constraints\NoyHoliday

Example usage:

<?php

declare(strict_types=1);

namespace App\Form;

use Aeon\Symfony\AeonBundle\Validator\Constraints\Holiday;
use Aeon\Symfony\AeonBundle\Validator\Constraints\NotHoliday;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class AeonFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options) : void
    {
        $builder->add('datetime', AeonDateTimeType::class, [
            'widget' => 'single_text',
            'input' => 'string',
            'data' => $options['calendar']->now()->format('Y-m-d H:i:s'),
        ]);

        $builder->add('submit', SubmitType::class);
    }

    public function configureOptions(OptionsResolver $resolver) : void
    {
        parent::configureOptions($resolver);

        $resolver->setRequired('calendar');
        $resolver->setAllowedTypes('calendar', Calendar::class);
    }
}

How to set current date in Symfony Form field

The bast practice is to always take the current date and time from one Calendar instance registered in Symfony Service Container, because it can be also mocked in the tests making them predictable and stable. In order to use Calendar in the form first it needs to be configured as an required option.

<?php

declare(strict_types=1);

namespace App\Form;

use Aeon\Symfony\AeonBundle\Validator\Constraints\Holiday;
use Aeon\Symfony\AeonBundle\Validator\Constraints\NotHoliday;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class AeonFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options) : void
    {
        $builder->add('holiday', AeonDayType::class, [
            'constraints' => [new Holiday(['countryCode' => 'US'])]
        ]);

        $builder->add('not_holiday', AeonDayType::class, [
            'constraints' => [new NotHoliday(['countryCode' => 'US'])]
        ]);

        $builder->add('submit', SubmitType::class);
    }
}

Later when creating form this option is required:

<?php

declare(strict_types=1);

namespace App\Controller;

use Aeon\Calendar\Gregorian\Calendar;
use App\Form\AeonFormType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class AeonCalendarController extends AbstractController
{
    private Calendar $calendar;

    public function __construct(Calendar $calendar)
    {
        $this->calendar = $calendar;
    }

    /**
     * @Route("/aeon/form", name="aeon_form")
     */
    public function form(Request $request) : Response
    {
        $form = $this->createForm(AeonFormType::class, null, ['calendar' => $this->calendar]);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

        }

        return $this->render('aeon_calendar/form.html.twig', [
            'form' => $form->createView(),
        ]);
    }
}

How to compare two Symfony DateTime fields

In order to compare two symfony fields when not using object as a form model in order to compare one field with another we need to use constraint propertyPath option and select other field through current field parent (form)

<?php

declare(strict_types=1);

namespace App\Form;

use Aeon\Symfony\AeonBundle\Validator\Constraints\Holiday;
use Aeon\Symfony\AeonBundle\Validator\Constraints\NotHoliday;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class AeonFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options) : void
    {
        $builder->add('datetime_compare_1', AeonDateTimeType::class, [
            'widget' => 'single_text',
            'input' => 'string',
            'data' => $options['calendar']->now()->sub(TimeUnit::second())->format('Y-m-d H:i:s'),
            'constraints' => [
                new Before(['propertyPath' => 'parent.all[datetime_compare_2].data']),
            ],
        ]);
        $builder->add('datetime_compare_2', AeonDateTimeType::class, [
            'widget' => 'single_text',
            'input' => 'string',
            'data' => $options['calendar']->now()->format('Y-m-d H:i:s'),
        ]);

        $builder->add('submit', SubmitType::class);
    }
}

How to use Yasumi holidays provider

By default symfony bundle registers calendar.holidays.factory.google service as a holiday provide however Yasumi is much more reliable and precise. It's also recommended holidays provider, in order to use it first install:

composer require aeon-php/calendar-holidays-yasumi

And then configure the bundle

# config/packages/aeon.yaml

aeon:
  calendar_holidays_factory_service: 'calendar.holidays.factory.yasumi'