Timber/TWIG for Wordpress - Questions about some basics - php

For instance in the Timber Documentation we have lots of examples like the following:
{% for item in menu.items %}
{{ post.author.name }}
Now the "item" and the "menu" etc. are all predefined, and nowhere defined by a user in the files (otherwise by my understanding Timber and TWIG would not make much sense).
What I do not understand is where I can find out a list these. In the Docs of Timber, there are a bunch of examples, then when I compare them to the Timber Starter Theme for Wordpress, I see a bunch of them, which aren't even mentioned in the documentations.
Where do I find a list of all available items?
What is the logic behind these items?

Don’t feel dumb, because you’re not! We’ve all been there. I had the same questions, when I started out with Timber. We’re trying to improve the documentation constantly, so questions like these help us to find out what we can do better 👍.
Whenever you see a variable in Twig, it can actually come from a couple of different places:
Variables from the global context
There’s a filter that most of us use to make a variable globally available whenever you set up your context through Timber::get_context(). When you see {% for item in menu.items %}, then it’s probably from the section of the docs about «Setting up a menu globally». I have to admit that we explain the filter through this example only, which is not ideal. But we’re changing this. In the next version of Timber, we try to explain the global context better (make sure to only read the section about the global context, because the part about «Template Contexts» describes functionality that is not available yet.)
Variables from the template
Consider the following example:
$context = Timber::get_context();
$context['post'] = new Timber\Post();
Timber::render( 'single.twig', $context );
Here you set up your context through Timber::get_context(). When you use Timber::get_context(), you’ll get a bunch of variables that make sense globally, but not for every template.
The $context variable is an array, holding all the variables you want to pass to your Twig template, in this case single.twig. When can add our own variables, like in the example above, where we add post that hold a Timber\Post object of the currently displayed post.
Variables from Timber’s classes
In the template, when you see {{ post.author.name }}, then author can be different things:
A property of the Timber\Post object that holds a value. These are all the variables that you’d see when you do {{ dump(post) }}.
A method of the Timber\Post object, that will be excecuted. This might be confusing at first, because in PHP, you always need to add braces to call a function, but in Twig, you don’t need to. When you call $post->author() in PHP, you would use post.author in Twig. To see a list of all the methods that are available for Timber\Post, you need to look into the Reference section for Timber\Post in the docs.
So if you would write the example {{ post.author.name }} in PHP, it would look like this:
$post->author()->name()
So author is a method of Timber\Post, which returns a Timber\User object. The Timber\User object has a method called name, which returns «the human-friendly name of the user».
But! When you just look at {{ post.author.name }}, it could also be a multi-dimensional array that you defined yourself:
$context['post'] = array(
'author' => array(
'name' => 'Tom Riddle',
),
);
You wouldn’t know that just from looking at it in Twig.
Variables from the Twig syntax
When you see {% for item in menu.items %}, then you’re looping over the variable menu.items. The variable menu probably comes from the global context (a Timber\Menu object), and items is a property of this object. The item variable is a new variable that is created to access the current loop item in the for loop. In this example, it’s the current menu item. You can choose any name for item you want.
Variables from includes
You can pass down a variable in Twig through the include statement. For example, if you defined post through your template file and wanted to use a different name in the template that you include, you could use it like this:
{% include 'teaser.twig' with { teaser: post } %}
Coming back to your specific questions:
Where do I find a list of all available items?
What is the logic behind these items?
I’m afraid there isn’t a definitive list, because what a variable is can be very dynamic. It’s mostly a combination of the global context, variables you set from the template, methods and properties from Timber’s classes as well as variables you define yourself in Twig.
If you have more question, add the to this answer as comments and I can update this answer accordingly.

Related

Twig keyword `services` always has the value `true`

I'm working in Symfony and tried to pass a variable called $services into my twig template. Naturally, I would also refer to the variable in twig with the same name.
$services = new ArrayCollection(); // Some array
$this->render('template.html.twig', [
'services' => $services
]);
For some reason however, services always is a boolean value of true. This is instead of the ArrayCollection I am trying to pass into it.
I've checked my twig configuration for anything that could be assigning itself as a global variable but everything checks out, and I can't seem to find any documentation about services being a reserved keyword.
FWIW, I renamed the twig variable name from services to foo for sanity checking and the $services variable behaves as expected, so it's definitely got something to do with assigning a variable to the word services in twig.
I'm currently running Twig 2.14.3
Apart from checking if there are any global variables set, are there any other avenues for trying to find why and where this variable would be defined?
After a few hours of troubleshooting, it seems like a pretty dumb answer.
I checked:
Global variables in my twig.yaml configuration
Made sure that any custom twig extensions weren't bound to service
Checked that any bound services weren't creating any functions with the name service
Made sure that I wasn't setting service in any of my templates up to my base template
It seemed like a whole lotta nothing, then I started turning to some 3rd party bundles that have some twig templates registered.
It turns out a bundle had a template with {% set services = ... %} written inside it, which was included in my base template.
+1 to #yivi for brining debug:twig to my attention, as it was incredibly useful knowing what is bound to twig. It was this info that made me aware of any namespaces that bundles had bound themselves to and gave some extra places to start looking.

Can twig macros return values?

I'm trying to write a template in Twig. Inside it I would like it to perform some manipulations on the string data that comes from the controller. In particular, there's a common manipulation (convert from underscore_case to CamelCase) that I'd like to bring out into a separate function. Then later I can use it repeatedly like {% set x = magic(a) ~ magic(b) %}. However I cannot find how to make such a reusable function inside the template itself. There are macros, but those don't seem to be able to return values. Filters are another option that seem to fit the bill, but I can only define those on PHP side.
Can this be done? Or should I do all the advanced string manipulation controller-side? It kinda feels like I'm pulling parts of display logic in there; things that should be in the view.
Twig is for outputting data. If you need to "transform" the data you need to do that before you send it to twig or you need to extend twig
Ideally, all the data you send to twig is just variables and arrays that needs the least amount of manipulation on their own.
When you're actually "in" twig, the data processing can be assumed to be "done" and only needs to be outputted in the appropriate places with minimal logic to decide user interface styles.
So revisit your logic and prepare your data better before sending it to twig.
An example for extending a toolkit class that contains our magic methods to do real wizardry.
class CustomToolkit
{
public function magic_a($a)
{
return strtolower($a); }
public function magic_b($b)
{
return camel_case($b);
}
public function magic_tidle($a, $b)
{
return $this->magic_a($a) ~ $this->magic_b($b);
}
}
Then you add this to your twig instance. I added here a complete instantiation loop. if you have a service provider you can just grab the instance from there and add it to that one.
$twig = new Twig_Environment(new Twig_Loader_Array([
'html' => $contents
]),[
'auto_reload' => true,
'debug' => false,
]);
$twig->addExtension('toolkit', new CustomToolkit ());
echo $twig->render('html', $values);
Then in your twig code you should be able to do something along the lines of
{% set x = toolkit.magic_tidle("value","value_b") %}
You are right, macros do not have return values and you cannot really make them have any. All they do is outputting strings.
Still, you are able to capture string output using set: https://twig.symfony.com/doc/2.x/tags/set.html
The syntax looks similar to this:
{% set var %}
{{ call.macro() }}
{% endset %}
The output of the macro call is then stored inside var. You may want to strip the whitespace though.
But then, consider rethinking what you are doing. Is this still presentation logic, or is your controller simply "too lazy" to transform the strings prior to passing them to twig? If it's really presentation logic, simply adding a twig filter by extending twig surely is worth the hassle. Not only because your code becomes testable.
Can this be done?
Yes! However, Twig templates are not the ideal places to run logic. At least, we should do our best to avoid it. Instead, Controller should return what Twig template needs. Logic should run in Service, Utility, Helper (you name it) etc. and Controller returns it to Twig. Twig then just displays it.
Can twig macros return values?
Yes! Look at this example. It accepts parameters and returns (not a real "return" thing but you get the idea) something back.
Example:
Assuming that the data you are trying to manipulate is something simple.
use Doctrine\Common\Inflector\Inflector;
Controller
{
action() {
$data = Inflector::camelize('hello_world'); // This will become helloWorld
return ....;
}
}
Look into Inflector class. It has useful stuff.

Laravel passing variable out of view partial to main blade, or between partials

I have a Laravel 5.5 app that uses view blades conventionally, nothing fancy. My blade is a long table of data and I split the group sections (item categories) into their own partials to manage the code better.
However, I can't find a way to pass variable data from the partials back to the main blade, or even between partials. The scope of the variables are bound within the partials and I need to show a page total that aggregates the sub-totals from the various partials (sections). I did try using
View::share('portfolio_total', 100);
in the AppServiceProvider, to no avail. I can set a value (like the 100 shown) that stays intact in the master blade, but doesn't pick up the value set in that variable in the partial. Changing the variable value inside the partial doesn't update the value available to the master blade. I've searched extensively for this and there are many examples of passing values into partials, but not back out.
Is there a Laravel trick for defining the equivalent of global variables that can have their value set in an included partial blade, and carry that value to another partial blade, or out to the master blade?
Thanks in advance.
I can't find a way to pass variable data from the partials back to the main blade, or even between partials.
That is because this is absolute bad practice. Hence the framework does not allow that (without abuses).
Why?
In the MVC world (which Laravel implements), views are responsible for displaying data.
If you are computing anything in your views (partials or not), then it means you are giving Views the responsibility of the Controller and/or Model. Which is not good of course.
Then what?
Idea is to compute everything you need before using the views. Two options:
If your view/partial is used only by a single controller method, simply pass the data to the view as usual.
If your view/partial is used in various places, setup a ViewComposer which will be able to pass the computed data each time that partial is used.
What usually I do is the following.
On my controller, to return data I do this:
$data = [];
$data['dummyData'] = Dummy::all();
$data['extraData'] = 'Extra string';
return view('myroute.index', array('data' => $data));
Then on each of my views (The view and the partials)
#php
$dummy = $data['dummyData'];
$extra = $data['extraData'];
#endphp
And then I can use the variable in the blade as any other: {{ $extra }}

Twig add multiple filters?

is it possible to add multiple filters in twig ?
for example i have this single filter
$app->twig->addFilter('_bah',new Twig_Filter_Function('_bah'));
if i want to add all my functions ill do this
$app->twig->addFilter('_bah1',new Twig_Filter_Function('_bah1'));
$app->twig->addFilter('_bah2',new Twig_Filter_Function('_bah2'));
..... etc
if i have many functions i want to use inside Twig template but without calling them by class name like {{ classname.method }} , i want to call them as a filter like {{ "bla bla bla"|trim_me}} is it possible ?
You can create an extension in Twig...
The main motivation for writing an extension is to move often used
code into a reusable class like adding support for
internationalization. An extension can define tags, filters, tests,
operators, global variables, functions, and node visitors.
Creating an extension also makes for a better separation of code that
is executed at compilation time and code needed at runtime. As such,
it makes your code faster.
Most of the time, it is useful to create a single extension for your
project, to host all the specific tags and filters you want to add to
Twig.
http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension
Then you just need one line...
$twig->addExtension(new My_Twig_Extension_Class());

Zend Framework passing a variable to all views from a controller to anything outside of $this->layout->content()

Ok, the subject makes no sense so Ill try to better describe it here.
Zend Frame work in use here. And I have run into a problem passing variables to my views, well the views included into the "top.phtml" that make up the template. What I am trying to do is implement a breadcrumb concept. The bread crumb file is included into the top.phtml before the content view file. So the breadcrumb variable isn't defined as far as the breadcrumb file is concerned.
I can print_r my array of settings for the breadcrumbs within the controllers view, no problem so it is working I know that much, just anything above that view in particular in the order of things can't get the variable. So I guess what I am looking to have answered is is there a means off passing a variable to the overall scheme of things similar in concept to
$this->view->variable_name = blah;
where something as high as the top.phtml can pick it up for use?
You may be looking for Placeholders.
Example:
Setting a placeholder value from a controller:
$this->view->placeholder('some_placeholder_name')->set('blah');
Setting a placeholder value from a view
$this->placeholder('some_placeholder_name')->set('blah');
Retrieving the placeholder value in a view script or layout:
$value = $this->placeholder('some_placeholder_name');
Placeholder content is rendered towards the end of your application execution so the value set in your controller should be available in your top level top.phtml view script.
I think this will work:
$this->layout()->breadcrumbs = ...
And then print $this->layout()->breadcrumbs in your top.phtml.
Zend Layout
After sending hours trying to get placeholder() to work with partialLoop(), I finally gave up and hacked a fix to pass vars to a partial:
$vars = (array) $this->getVars();
foreach ($this->rows as $row) {
$partialVars = array(
'row' => $row,
'vars' => $vars,
);
echo $this->partial('row.phtml', $partialVars);
}
ugly, but it worked.
+1 for everyone for giving me a clue, however none of the above worked well in my favor. However between them all, they lead me towards finding my answer which is
Zend_Registry
In my Controller I built my array and passed it to Zend_Registry like
$breadArray = array(
array("icon"=>"i_robot", "href"=>"systems/"),
array("href"=>"metrics","text"=>"Metrics")
);
Zend_Registry::set('breaded_crumbs', $breadArray);
Then in my breadcrumb.phtml which is loaded before the content and view I used
print_r(Zend_Registry::get('breaded_crumbs'));
to see if it was working, and it gave me the array's so I for anyone in the future looking to extend a variable outside of the view itself, the registry seems to be the way to go. I tried placeholder and layout, both gave me errors about not being something or another, and when I got them to work in part I wasn't getting what I was expecting.

Categories