Wrap contextual html around a specific twig variable {{ product.name }}
Asked Answered
A

3

6

I want to automatically wrap some html, lets say <span data-id=".."> when I call {{ product.name }} in my twig template.

So when in a twig template, I use {{ product.name }}, I want the output to be: <span data-type="product" data-id="8" data-prop="name">My product name</span>. I cannot use twig filters or macros, since I really need the template syntax to be {{ product.name }}, so the end-user (template designer), does not have to care about it.

The reason I need this is because I am building an on-page editting tool for twig templates, so I need to know the contexts of those variables from within HTML.

I have tried to override the Compiler that the Twig_Environment uses, but I cannot seem to alter the output of the twig variable node.

How can I do this?

EDIT

I wanted to mention that I need this to use the {{ product.name }} syntax, since other designers will work with those templates outside of Symfony 2. I want to make almost all twig variables editable in the front-end, so a solution with filters or macros can indeed work, but it kills the usability and readability of the platform I am writing. There is no public API currently in twig that can achieve what I want, that is why I am fiddling with the twig compiler. I do not have the required knowledge of the Twig internals to achieve this. If someone could point me into a direction that would be great!

UPDATE 2

I have found a place where I can achieve what I want. Every GetAttr node is compiled to $this->getAttribute($someContext, "property"). So if I can change the subclass of compiled twig template, I can achieve what I want. By default all twig templates extend from Twig_Template. I want to extend this method.

How can I change the subclass of all compiled twig templates?

UPDATE 3 I've found a way to use my own base class for all compiled twig templates. I can simply set it as an option on the twig environment, see this link. I hope to get it working tomorrow and I will post a proper answer on how I solved it all together. Not sure how I will handle escaping, since that will happen after the $this->getAttribute() is called.

Allspice answered 10/12, 2015 at 13:11 Comment(6)
Have you tried to write a twig extension? Which problem do you have?Ionic
Yes I did, but there I only have the context of the whole file. But what I need is the context that is given to the GetAttr node and use the property that it is trying to get out of it for the final twig output. When I extend the compiler I can use the subcompile method to get to those nodes, but I cannot find a way to alter the output of those. My knowledge for the internals of twig are not enough to accomplish what I want.Allspice
Why the downvote? If my question is not clear, please say so, so I can try to explain it better.Allspice
probably because is not clear what you have tried to doIonic
Hi Steffen, what do you think about my answer?Ionic
@Ionic Thanks for helping, i've updated my question and also made some progress with my issue! I am afraid I cannot use filters not macros, so the easy solutions won't fit.Allspice
A
2

I've solved it by wrapping the PrintNode that is created when parsing a VAR_START token (inside the twig parser) with my own EditablePrintNode. In that node I traverse the expression that is compiled by the print node and get the necessary property path and pass that as an argument to a wrapper function around the default twig escape function that is compiled by the PrintNode.

Allspice answered 24/2, 2016 at 2:12 Comment(1)
Care to share the code? I am looking to solve a similar problem, and an illustration of your solution would be really helpful.Deianira
S
2

I think macros are the best candidates for those kind of wrappings.

For example:

main.twig

{% import "macros.twig" as macros %}

{{ macros.display_product(product) }}

macros.twig

{% macro display_product(product) %}

  <span data-id="{{ product.id }}" data-prop="name">{{ product.name }}</span>

{% endmacro %}

Context

product:
     id: 8
     name: My Georgeous Product

Result

<span data-id="8" data-prop="name">My Georgeous Product</span>

See fiddle

Staub answered 11/12, 2015 at 12:56 Comment(1)
Hi @Staub seems macro sounds better approach instead of a twig filter. +1Ionic
A
2

I've solved it by wrapping the PrintNode that is created when parsing a VAR_START token (inside the twig parser) with my own EditablePrintNode. In that node I traverse the expression that is compiled by the print node and get the necessary property path and pass that as an argument to a wrapper function around the default twig escape function that is compiled by the PrintNode.

Allspice answered 24/2, 2016 at 2:12 Comment(1)
Care to share the code? I am looking to solve a similar problem, and an illustration of your solution would be really helpful.Deianira
I
0

I suggest to write a custom filter. You can find the doc here about how to write and configure in your environment

// an anonymous function
$filter = new Twig_SimpleFilter('my_custom_product_filter', function ($product) {
    return '<span data-id="'.$product->getId().'" data-prop="name">'.$product->getName().'</span>';
});

You need to register as described in the doc

then you can use as follow:

{{ myProduct|my_custom_product_filter}}

Hope this help

Ionic answered 10/12, 2015 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.