Extending Twig

Craft provides two ways for plugins to extend its Twig templating environment.

# Extend the Global craft Variable

The global craft template variable is an instance of craft\web\twig\variables\CraftVariable (opens new window). Methods and properties of that class are available in Twig via dot notation—for example, when a template uses craft.entries (or craft.entries()), Twig calls CraftVariable::entries() (opens new window) behind the scenes.

That CraftVariable instance can be extended by plugins with behaviors and services. Choosing the right approach depends on what you’re trying to add to it.

  • Use a behavior to add custom properties or methods directly onto the craft variable (e.g. craft.foo()).
  • Use a service to add a sub-object to the craft variable, which can be accessed with a custom property name, called the service’s “ID”. (e.g. craft.foo.*).

You can attach your behavior or service to the CraftVariable instance by registering an EVENT_INIT (opens new window) event handler from your plugin’s init() method:


















 
 
 


 




use craft\web\twig\variables\CraftVariable;
use yii\base\Event;
use mynamespace\myplugin\behaviors\MyBehavior;
use mynamespace\myplugin\services\MyService;

public function init()
{
    parent::init();

    Event::on(
        CraftVariable::class,
        CraftVariable::EVENT_INIT,
        function(Event $e) {
            /** @var CraftVariable $variable */
            $variable = $e->sender;

            // Attach a behavior:
            $variable->attachBehaviors([
                MyBehavior::class,
            ]);

            // Attach a service:
            $variable->set('serviceId', MyService::class);
        }
    );
}

On their own, behaviors are typically attached in response to the dedicated EVENT_DEFINE_BEHAVIORS event.

# Create a Twig Extension

New features can be added to Twig by creating a Twig extension (opens new window).

Extensions follow this signature:

namespace mynamespace\myplugin\twig;

use Craft;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;

class Extension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            // First argument is the filter name; second is a callable:
            new TwigFilter('total', 'array_sum'),
        ];
    }

    public function getFunctions(): array
    {
        return [
            // First argument is the function name; second is a callable:
            new TwigFunction('log', [Craft::class, 'info']),
        ];
    }

    public function getTests(): array
    {
        return [
            // First argument is the test name; second is a callable:
            new TwigTest('infinite', 'is_infinite'),
        ];
    }
}

Filters, functions, and tests are mostly equivalent in their capabilities, but as language constructs have specific uses in the template layer. Consider which makes the most sense for your users! It should be clear in a template what features come from which extensions—either from their naming or how they’re used.

You do not need to define methods that your extension has no use for—except when explicitly implementing the GlobalsInterface to inject global variables:





 


 












namespace mynamespace\myplugin\twig;

use Craft;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
use mynamespace\myplugin\MyPlugin;

class Extension extends AbstractExtension implements GlobalsInterface
{
    // ...

    public function getGlobals(): array
    {
        // Keys are variable names!
        return [
            'myPlugin' => MyPlugin::getInstance(),
        ];
    }
}

Here, we’ve set a variable (myVariable) to our plugin’s singleton instance. This means that any services you’ve defined will be accessible in all Twig templates:

{{ myPlugin.myServiceName.myServiceMethod() }}

Avoid defining global variables that are ambiguous (like plugin or elements), apt to be frequently overwritten (like entry or block), or conflict with built-in globals.

# Registering the Extension

Register your Twig extension in your plugin or module’s init() method by calling craft\web\View::registerTwigExtension() (opens new window):







 
 



public function init()
{
    parent::init();

    if (Craft::$app->getRequest()->getIsSiteRequest()) {
        // Instantiate + register the extension:
        $extension = new mynamespace\myplugin\twig\Extension();
        Craft::$app->getView()->registerTwigExtension($extension);
    }
}

The getIsSiteRequest() check is optional. If your extension provides features that will be useful in system emails (commonly triggered from the control panel), or will be used when rendering templates from console requests (less common, but still valid), you may want to register it in all contexts.

We’ve only covered the simplest means of extending the Twig environment—you can also provide custom tags or operators. Craft’s own Twig extension (opens new window) is a great place to look for examples of advanced features.