La principal motivación para escribir una función personalizada para twig, es que al migrar a este potente motor de plantillas integrado en Symfony2, me di cuenta de que en las mismas no se puede usar código php, lo que resulta fantástico en la teoría, ya que en las vistas solo hay unas cuantas condiciones y código html, sin embargo en la práctica y después de venir trabajando con Symfony 1.4, resulta bastante laborioso y tardío hacer todas las funciones en el controlador.

Bien sea por el factor tiempo, o por no salir de la zona de confort muchos optan por rechazar twig y seguir usando php como motor de plantillas.

En este post te enseñare como Crear funciones php en twig, y posteriormente usarlas a través de la Inyección de dependencias.

Creando la clase de la extensión

Para obtener la funcionalidad personalizada primero debes crear la clase para la extensión Twig, que use la clase EntityManager de symfony. Como ejemplo vas a crear una consulta que traiga todos las provincias de un país seleccionado

// src/Acme/DemoBundle/DependencyInjection/FunctionsExtension.php
namespace Acme\DemoBundle\DependencyInjection;
use Doctrine\ORM\EntityManager;

class FunctionsExtension extends \Twig_Extension
{

	protected $em;

	public function __construct(EntityManager $em) {
	$	this->em = $em;
	}

	/**
	* {@inheritdoc}
	*/
	public function getFunctions()
	{
		return array(
			'estadosFilter' => new \Twig_Function_Method($this, 'estadosFilter'),
		);
	}

	public function estadosFilter($pais)
	{
		$provincias = $this->em->getRepository('AcmeDemoBundle:Provincias')->findBy(array('paisid'=>$pais));

		return $provincias;
	}

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

Registrando una extensión como servicio

Ahora hay que dejar que el contenedor de servicios sepa de su nueva extensión declarando la misma:

YAML:
# src/Acme/DemoBundle/Resources/config/services.yml
services:
acme.twig.acme_funtions:
class: Acme\DemoBundle\DependencyInjection\FunctionsExtension
tags:
- { name: twig.extension }
arguments: [ @doctrine.orm.default_entity_manager, @doctrine.orm.entity_manager, @kernel ]
XML:
<!-- src/Acme/DemoBundle/Resources/config/services.xml -->
<services>
<service id="acme.twig.acme_funtions" class="Acme\DemoBundle\DependencyInjection\FunctionsExtension">
<tag name="twig.extension" />
<@doctrine.orm.default_entity_manager>
<@doctrine.orm.entity_manager>
<@kernel>
</service>
</services>
PHP:
// src/Acme/DemoBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;

$container
->register('acme.twig.acme_funtions', '\Acme\DemoBundle\DependencyInjection\FunctionsExtension')
->addTag('twig.extension')
->addArgument('@doctrine.orm.default_entity_manager')
->addArgument('@doctrine.orm.entity_manager')
->addArgument('@kernel');

Usando la extensión personalizada

Usar tu recién creada extensión de Twig no es diferente a cualquier otra, en este ejemplo se estaria llenando selects de acuerdo al objeto paises:

{% for p in paises %}
	{% set provincias = estadosFilter(p.id) %}
	<option value="">Seleccione una Provincia</option>
	{% for prov in provincias %}
		<option value="{{ prov.id }}">{{ prov.nombre }</option>
	{% endfor %}
{% endfor %}

De esta forma ya puedes crear cualquier otra función php como una inyección de dependencias y usarlas en cualquier momento en la plantilla twig

Notas

Ten en cuenta que las extensiones Twig no se cargan de manera diferida. Esto significa que hay una mayor probabilidad de que obtengas una CircularReferenceException o ScopeWideningInjectionException si cualquier servicio (o tu extensión Twig —en este caso—) es dependiente del servicio Petición. Para obtener más información, échale un vistazo a Cómo Trabajar con Ámbitos