Include a twig file and pass variables from a separate file?
Asked Answered
E

3

10

I have container.twig including component.twig and passing an object called 'mock'.

In container.twig:

{% set mock = {
    title : "This is my title"
}
%}

{% include 'component.twig' with mock %}

This is working fine but I want to move the mock data to its own file. This isnt working:

Container.twig

{% include 'component.twig' with 'mock.twig' %}

In mock.twig

{% set mock = {
    title : "This is my title"
}
%}

Im using gulp-twig but it works like standard twig in most respects. https://github.com/zimmen/gulp-twig

Excretory answered 28/4, 2016 at 16:0 Comment(5)
Is there one or two files in the second code block?Despoil
@Despoil They are separate files, I've updated my question.Excretory
Can you eloborate why you want do this? Having a look at the include documentation a template name is not expected. Perhaps you could solve it with a custom twig function.Stakeout
Its partly for project organisation to separate out mock content from the templates, and also so that I can reuse the mock content in different files.Excretory
Twig context is never stored in the template object, so this will be very difficult to find a clean way to achieve this.Erickericka
E
5

The problem

Twig context is never stored in the template object, so this will be very difficult to find a clean way to achieve this. For example, the following Twig code:

{% set test = 'Hello, world' %}

Will compile to:

<?php

class __TwigTemplate_20df0122e7c88760565e671dea7b7d68c33516f833acc39288f926e234b08380 extends Twig_Template
{
    /* ... */

    protected function doDisplay(array $context, array $blocks = array())
    {
        // line 1
        $context["test"] = "Hello, world";
    }

    /* ... */
}

As you can see, the inherited context is not passed to the doDisplay method by reference, and is never stored in the object itself (like $this->context = $context). This deisgn allow templates to be reusable, and is memory-friendly.

Solution 1 : using global variables

I don't know if you are aware of Global Variables in Twig. You can do a bunch of hacks with them.

The easiest usage is to load all your globals inside your twig environment.

$loader = new Twig_Loader_Filesystem(__DIR__.'/view');
$env = new Twig_Environment($loader);
$env->addGlobal('foo', 'bar');
$env->addGlobal('Hello', 'world!');

Then, you can use {{ foo }} and {{ Hello }} in your whole application.

But there are 2 problems here:

  • As you're trying to load variables from twig files, I assume you have lots of variables to initialize depending on your feature and don't want to load everything all time.

  • you are loading variables from PHP scripts and not from Twig, and your question want to import variables from a twig file.

Solution 2 : using a Twig extension

You can also create a storage extension that provide a save function to persist some template's context somewhere, and a restore function to merge this stored context in another one.

proof_of_concept.php

<?php

require __DIR__.'/vendor/autoload.php';

class StorageTwigExtension extends Twig_Extension
{
    protected $storage = [];

    public function getFunctions() {
        return [
            new Twig_SimpleFunction('save', [$this, 'save'], ['needs_context' => true]),
            new Twig_SimpleFunction('restore', [$this, 'restore'], ['needs_context' => true]),
        ];
    }

    public function save($context, $name) {
        $this->storage = array_merge($this->storage, $context);
    }

    public function restore(&$context, $name) {
        $context = array_merge($context, $this->storage);
    }

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

/* usage example */

$loader = new Twig_Loader_Filesystem(__DIR__.'/view');
$env = new Twig_Environment($loader);

$env->addExtension(new StorageTwigExtension());

echo $env->render('test.twig'), PHP_EOL;

twig/variables.twig

{% set foo = 'bar' %}
{% set Hello = 'world!' %}
{{ save('test') }}

twig/test.twig

{% include 'variables.twig' %}
{{ restore('test') }}
{{ foo }}

Note: if you only want to import variables without actually rendering what's inside twig/variables.twig, you can also use:

{% set tmp = include('variables.twig') %}
{{ restore('test') }}
{{ foo }}

Final note

I'm not used to the JavaScript twig port, but it looks like you can still extend it, that's your go :)

Erickericka answered 4/5, 2016 at 10:40 Comment(0)
C
4

Because of Twig's scoping rules (which I assume are replicated by the gulp version), you cannot pass variables up from a child template without creating a helper function. The closest thing you can do is to use inheritance to replicate this.

As such, your mock.twig file would become

{% set mock = {
      title : "This is my title"
  }
%}
{% block content %}{% endblock %}

Your container.twig would then become

{% extends 'mock.twig' %}
{% block content %}
    {% include 'component.twig' with mock %}
{% endblock %}

This achieves your goals of separating the mock content from the templates for the most part and, using dynamic extends, you can do something like

{% extends usemock == 'true'
    ? 'contentdumper.twig'
    : 'mock.twig' %}

with a contentdumper.twig file that is just a stub like so

 {% block content %}{% endblock %}

and then set the usemock variable to determine if you will be using the mock data or not.

Hopefully this helps, even though it doesn't really solve the exact problem you are having.

Cuttlebone answered 3/5, 2016 at 17:53 Comment(0)
L
0

The answer provided by Reid Johnson above helped me.

However, in case you have different sets of mock data to pass on components that extend another, there's a trick using both block and embed. Let's say your container.twig is a layout that will be extended by a page.twig.

If you have mock data specific to this page, you might have to write things like this :

{% extends "page.mock.twig" %}

{% block page_mock %}
    {% embed "layout.twig" %}
        {% block layout_content %} ... {% endblock %}
    {% endembed %}
{% endblock %}
Lacasse answered 4/5, 2023 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.