I have a function in my Symfony controller which returns the following:
test+testString1+test2
TestController.php
public function getResultAction($fileName) {
$string1="test";
$string2="testString1";
$string3="test2";
$response = $string1."+".$string2."+".$string3;
return new Response($response);
}
In my twig I've rendered the function in my controller:
test.html.twig
{% set test %}
{{ render(controller('TestBundle:Test:getResult')) }}|split('+', 4)
{% endset %}
{{ test[0] }}
I'm using twig split filter so that I can display test, testString1 and test2 individually. But then whenever I attempt to display test[0], I receive the following error:
Impossible to access a key "0" on an object of class "Twig_Markup" that does not implement ArrayAccess interface in TestBundle:Test:test.html.twig
What's wrong with what I'm doing? I hope you could help me with this. Thanks
You miss the split filter inside the double brace as follow:
{% set test %}
{{ render(controller('TestBundle:Test:getResult')) |split('+', 4) }}
{% endset %}
Hovenever seems same problem with the set tag. From the doc:
The set tag can also be used to 'capture' chunks of text
Try with:
{%
set test=render(controller('TestBundle:Test:getResult'))|split('+', 4)
%}
Hope this help
Related
TL;DR: I am currently struggling with the idea how to elegantly generate CRUDs/GRID using Twig.
Long story:
I have a Phalcon app with AngularJs, templating system is Twig.
PHP is suppose to prepare a template for CRUD that contains:
List od entities, table that contains proper headers, columns, row items, action buttons
Simple and advanced search
Mass actions block
Main buttons - Add/Import/Export etc.
AngularJs is responsible for:
Paginating results in 2 modes: Ajax Loaded entities, All entities at once
Changing number of entities per page
Displaying various forms in modals - Add/Edit/Import
Filtering entities - simple and advanced search
Everything is working right now but I am not particularly fond of how it works.
For example a simple entity with few fields needs a following template:
{% extends 'index.twig' %}
{% block containerAttr %}ng-controller="InventoryController as ctrl"{% endblock %}
{% set ctrlName = 'ctrl' %}
{% set columnWidth = 'col-xs-12' %}
{% set tableClass = 'table table-bordered table-striped table-hover' %}
{% block header %}
{% include '#crud/crud/header.twig' %}
{% endblock %}
{% block content %}
{% embed '#crud/crud/main-box.twig' %}
{% block tableHeader %}
{% autoescape false %}
{{ crud.tableHeader('Id', 'id') }}
{{ crud.tableHeader('Nazwa', 'name') }}
<th class="col-xs-1">Actions</th>
{% endautoescape %}
{% endblock %}
{% block tableRow %}
<td ng-bind="e.id"></td>
<td ng-bind="e.name"></td>
{% endblock %}
{% endembed %}
{% embed '#crud/crud/form.twig' %}
{% block modalBody %}
{% autoescape false %}
<div class="row">
{{ crud.input('ctrl.entity.name', 'Name', 12) }}
</div>
{% endautoescape %}
{% endblock %}
{% endembed %}
{% embed '#crud/crud/upload.twig' %}{% endembed %}
{% embed '#crud/crud/advancedSearch.twig' %}{% endembed %}
<script>
window.xdata = {{ xdata | json_encode | raw }};
</script>
{% endblock %}
crud in this template is responsible for generating form fields compatible with AngularJs.
Idea is: To create new CRUDs/Grids with the least amount of work required. While still giving a room for some flexibility.
Right now to make a CRUD/Grid inside my application there is a really little work required:
PHP Controller needs to contain only one line:
class SubCategoryController extends CrudBase
{
protected $entityClass = InventoryCategory::class;
}
Thanks to extending it is really easy to customize every part of it.
Model needs to have just a few fields:
class InventoryCategory extends ModelBase
{
/** #var integer */
public $id;
/** #var string */
public $name;
public function initialize()
{
$this->setSource('inventory_subcategory');
$this::setup(['castOnHydrate' => true]);
}
}
Even the AngularJs controller contains only URLs and one line:
class InventoryCategoryController extends CrudBase {
constructor($injector, $scope) {
super($injector, $scope);
this.urlSave = '/inventory/category/save';
this.urlImport = '/inventory/category/import';
this.urlExport = '/inventory/category/export';
this.urlDelete = '/inventory/category/delete';
this.init({
advancedSearch: false
});
}
}
angular.module('App').controller('InventoryCategoryController', InventoryCategoryController);
Yes I use ES6, application is not compatible with older browsers anyways and it is admin panel so I do not care about older browsers.
But now only templates remains a tedious work to copy it over and change it. Not to mention they do look pretty ugly.
So my idea was to use Annotation Reader (Phalcon has one) to read fields from Model, then transform it into a base configuration, check if there is custom configuration in json and merge it. Then based on configuration, generate this whole template on the fly.
So the grand question is:
I was wondering if I can create some class that extends Twig_Template to generate this template on the fly?
I have seen Twig cache and it should be pretty easy but then how can i use it? How can i render it? Will extend still work?
I am trying to make a messaging system and I'm facing a small problem. I have a bigger template that displays my menu and my content. The menu includes the number of new messages, and the content can be any page(compose a new message, inbox, sent).
The problem is that I have to render each of the small templates by passing the number of new received messages to each of them, calling the doctrine each time and repeating code. Is there any way to send the number only to the parent template?
Here are my templates:
This is the parent containing the newmsg variable that gives me problems.
{% extends "::base.html.twig" %}
{% block body %}
inbox : {{ newmsg }}
sent
compose
{% endblock body %}
Here is an example of child template:
{% block body %}
{{ parent() }}
{% if messageList %}
{% for message in messageList %}
<li>title = {{ message.title|e }}</li>
<li>cont= {{ message.content|e }}</li>
<li>data= {{ message.date|date('d-m-Y H:m:s') }}</li>
<li>sender= {{ message.sender|e }}</li>
<hr>
{% endfor %}
{% else %}
<div>no messages</div>
{% endif %}
{% endblock body %}
The problem is that each child template is asking me for the newmsg variable
$messages = $this->getDoctrine()->getRepository('MedAppCrudBundle:Message');
$newMessagesNo = count($messages->findBy(array('seen' => '0', 'receiver' => $this->getUser())));
return $this->render(
'MedAppCrudBundle:UserBackend\Message:new.html.twig',
array(
'form' => $form->createView(),
'newmsg' => $newMessagesNo,
)
);
And I have to write this in every single controller. Any way I can shorten this problem?
You could implement a service that gives back the newmsg value and call it on your parent template. Then it would not be necessary to pass the variable.
You can add a service in your bundle's services.yml with something like:
services:
newmessages:
class: Full\Class\Name\NewMessagesService
arguments: ["#doctrine.orm.entity_manager"]
Then, implement the Full\Class\Name\NewMessagesService class. Keep in mind that this class will need a constructor that receives an EntityManager argument. Something like:
<?php
namespace Full\Class\Name;
class NewMessagesService{
private $entityManager;
public function __construct($entityManager){
$this->entityManager = $entityManager;
}
public function methodToCalculate(){
//Perform calculation and return result
}
}
Then, in your parent template, replace {{newmsg} with:
{{ newmessages.methodToCalculate() }}
Hi how use this part of code with twig,
$this->assign('title', 'Home');
not
echo $this->assign('title', 'Home');
I tried,
{% set assign = ('title', 'Home') %}
{% set this.assign = ('title', 'Home') %}
{% set assign = {'title', 'Home'} %}
{{ assign('title', 'Home') }}
But still don't work
Thanks you
I don't know about using $this in the context of a template (it would refer to some generated class instance), but you can perform arbitrary operations without printing by using the do statement.
The do tag works exactly like the regular variable expression ({{ ... }}) just that it doesn't print anything:
{% do 1 + 2 %}
To access the view itself when using TwigView, use the _view variable:
{% do _view.assign('title', 'Home') %}
i can't figure out, how to render a macro in Symfony 2 controller. This is how i can render a twig template
$this
->get("twig")
->render("AcmeBundle:Product:table.html.twig", array(
"product" => $product
))
;
So i searching for something similar, but for rendering a twig macro. Thx for any suggestions!
Twig macro's are something inside a template. They are run whenever you render a template executing the macro.
Just create another "wraper" template, which would have only that macro in it. Something like
macro.html.twig file
{% macro sample(item) %}
{# some code here #}
{% endmacro sample #}
sample_macro_wrapper.html.twig
{% from 'macro.html.twig' import sample %}
{{ sample(item) }}
controller.php
public function someAction()
{
// ...........
$renderedMacro = $this->get('twig')
->render('sample_macro_wrapper.html.twig', ['item' => $item]);
}
First of all, I know that the logic should be in the controller and not in the view and I keep it that way.
But in this particular situation I need to use preg_match within a ternary operation to set the css class of a div.
Example:
{% for list in lists %}
<div class="{{ (preg_match(list.a, b))>0 ? something : else }}"...>...</div>
{% endfor %}
How can I achieve the (preg_match(list.a,b))>0 condition in twig?
Thanks in advance
For those who came here from Google search results (like me).
There's containment operator that allows you to do something like this:
{{ 'cd' in 'abcde' }} {# returns true #}
You can't use preg_match() directly but there are ways to accomplish it:
if your list is an entity, add a method matches(): {{ (list.matches(b)) ? something : else }}
you could create a custom Twig extension function that uses preg_match() internally http://symfony.com/doc/master/cookbook/templating/twig_extension.html
Expanding from Serge's comment and is the correct answer.
In my example my string is "Message Taken - 12456756". I can then use |split to convert it into an array and use |replace to get ride of white space.
{% set mt = 'Message Taken' in d.note %}
{% if mt == true %}
{#.. you can then do something that is true#}
{% set mt_array = d.note|split('-') %}
{{ mt_array[0] }} | {{ mt_array[1]|replace({' ' : ''}) }}
{% endif %}
This would output my string but I now control two parts instead of 1.