Skip to main content

Creating a Symfony2 helper

While working on a Symfony2 project yesterday, I realized I needed a better way to format dates in my templates. The reason behind this was the fact that my MongoDB document entities contained date values that were either null or DateTime instances. This entailed using a bunch of if statements for extra checks that made template code unnecessarily verbose.

My solution was to create a template helper in an application specific framework bundle that I had created previously to contain all common assets and other necessary common resources.

Create a Helper directory in the framework bundle directory and create a DateHelper class:

src/Acme/FrameworkBundle/Helper/DateHelper.php

namespace Acme\FrameworkBundle\Helper;

use Symfony\Component\Templating\Helper\Helper;

class DateHelper extends Helper
{
    protected $options;
    
    public function __construct(array $options)
    {
        $this->options = $options;
    }
    
    public function getName()
    {
        return 'date';
    }
    
    public function format($date, $detailed = false)
    {
        if (!empty($date)) {
            if ($date instanceof \DateTime) {
                if ($detailed === true) {
                    return $date->format($this->getOption('detailed_format'));
                } else {
                    return $date->format($this->getOption('default_format'));
                }
            }
        }
        
        return null;
    }
    
    protected function getOption($name)
    {
        if (array_key_exists($name, $this->options)) {
            return $this->options[$name];
        }
        
        throw new Exception('Options does not exist');
    }
}

Define the configuration options for your helper:

src/Acme/FrameworkBundle/DependencyInjection/Configuration.php

namespace Acme\FrameworkBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('acme_framework');

        $rootNode->children()
            ->arrayNode('helper')
                ->isRequired()
                ->children()
                    ->arrayNode('date')
                        ->isRequired()
                        ->children()
                            ->scalarNode('default_format')->defaultValue('Y-m-d')->cannotBeEmpty()->end()
                            ->scalarNode('detailed_format')->defaultValue('Y-m-d H:i:s')->cannotBeEmpty()->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ->end();

        return $treeBuilder;
    }
}

Add the new configuration section that you have defined for your helper to your config.yml:

app/config/config.yml

acme_framework:
    helper:
        date:
            default_format: "Y-m-d"
            detailed_format: "Y-m-d H:i:s"

The above approach is a very simplistic approach and would not be the correct one if you are dealing with localization. However, in my case, this is more than enough.

Add the necessary code to your extension class to load your new configuration:

src/Acme/FrameworkBundle/DependencyInjection/AcmeFrameworkExtension.php

namespace Acme\FrameworkBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

class AcmeFrameworkExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);
                
        $container->setParameter('acme_framework.helper.date', $config['helper']['date']);
        
        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');
    }
}

The next step is to define our service and tag it as a helper:

src/Acme/FrameworkBundle/Resources/config/services.xml

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        
        <service id="acme_framework.helper.date" class="Acme\FrameworkBundle\Helper\DateHelper">
            <tag name="templating.helper" alias="date" />
            <argument>%acme_framework.helper.date%</argument>
        </service>
        
    </services>

</container>

And that is pretty much it. You can now call your helper in your templates like this:

echo $view['date']->format($activity->getStartedAt());

Comments

  1. Great !
    How can we use this with Twig ?

    ReplyDelete
  2. I have the same question

    "How can we use this with Twig ? "

    And

    Without "option", what is the structure of file ?
    - src/Acme/FrameworkBundle/DependencyInjection/Configuration.php
    - app/config/config.yml

    ReplyDelete
  3. +1 for "How can we use this with Twig ? "

    ReplyDelete
    Replies
    1. I don't use Twig so I don't know the answer to your questions. However, a quick search returns the Twig Extensions page located at http://twig.sensiolabs.org/doc/extensions.html.

      Delete

Post a Comment

Popular posts from this blog

Securing Symfony2 REST services with FOSOAuthServerBundle

Overview In my previous article, I wrote about setting up a Symfony2 REST service using FOSRestBundle. However, this REST service was behind a firewall protected by a generic form_login provider. Not really ideal if you wish to open your REST API to other applications. So in this article, I will try to explain how to set up FOSOAuthServerBundle to protect your REST API methods using OAuth2. Before we start getting into the gritty details, it is a good idea to have a look at the official OAuth2 documentation . Let's begin... FOSOAuthServerBundle Installation You have to install v1.1.0 of FOSOAuthServerBundle if you are using Symfony 2.0.x. If not, see the docs . First, add the following entries to your deps file: [FOSOAuthServerBundle] git=git://github.com/FriendsOfSymfony/FOSOAuthServerBundle.git target=bundles/FOS/OAuthServerBundle version=origin/1.1.x [oauth2-php] git=git://github.com/FriendsOfSymfony/oauth2-php.git Run the vendors script to install these...

Unexpected token "name" of value "if" ("end of statement block" expected) in "WebProfilerBundle:Collector:logger.html.twig"

Encountered this WebProfilerBundle error message when I ran the bin/vendors script to update my Symfony2 bundles. Make sure your deps file is up to date; you need to pay special attention to your version values. In this case, update your twig version to v1.2.0 as illustrated below: [twig] git=http://github.com/fabpot/Twig.git version=v1.2.0 Run the vendors script to update your bundle and the error message should disappear. You can get the most up to date deps file from the symfony-standard repository located at: https://github.com/symfony/symfony-standard/blob/master/deps

A Parcelable Tutorial for Android

Parcelable Interface Overview In one of my earlier posts, I mentioned writing an article about FOSOAuthBundle integration with an Android client. To keep that article to the point, I need to explain some concepts beforehand. One of the important concepts is the Android Parcelable interface that allows data to be transferred between different processes/threads. Certain network operations with Android such as authentication with OAuth2 and then fetching data from a REST endpoint should be performed in the background in order not to block the UI thread. This requires data to be fetched by a service (I have opted for Intent Services in my implementation) in the background and then passed back to the calling activity/fragment with a result callback. This is where the Parcelable interface comes into play. Basically, the Parcelable interface allows your classes to be flattened inside a message container called a Parcel to facilitate high performance inter process communication. The rece...