I am using twig files for some of the forms in my PHP application. I did everything following this link
http://twig.sensiolabs.org/doc/extensions/i18n.html#
which tells how twig files can be translated using Twig_Extensions_Extension_I18n. I believe i have followed all the steps but still i cannot seem to solve the problem.
This is how my PHP code looks like
require_once 'Twig/Autoloader.php';
Twig_Autoloader::register();
$twig_loader = new Twig_Loader_Filesystem(APP_PATH . 'corporate/views');
$GLOBALS['twig'] = new Twig_Environment($twig_loader, array(
'cache' => TMP_PATH . 'twig_cache',
'debug' => DEBUG,
'auto_reload' => true
));
$GLOBALS['twig']->addExtension(new Twig_Extensions_Extension_I18n());
return $GLOBALS['twig']->
render('path/to/twig/template'.$formTemplate,
array_merge($formVariables,
array(
"help" => isset($helpbox) ? $helpbox : array(),
"type" => $current_data['type'],
"integration_id" => $current_data['action_comp'],
"automation_type" => $current_data['automation_type']
)
)
);
There is another PHP file where i read all the twig files and run the following command as following.
$exe = 'xgettext --force-po --default-domain emarketeer --keyword="pgettext:1c,2" -c -j -o /tmp/messages.po '.escapeshellarg($file).' >/dev/null 2>&1';
system($exe);
this is how my twig template ($formTemplate) looks like
{% extends 'integrations/_mixed/automation_form_wrapper.twig' %}
{% block formular %} {% import "_utils/form.twig" as form %}
{{ form.boxlabel( 'Name (choose automation Name)'|trans, "Name") }}
{{ form.input("action_name", action_name.value, "", "",
action_name.collection ) }}
{% endblock %}
The imported twig template ('_utils/form.twig') as form looks like this
{% macro boxlabel(label, label_for, tooltip, class, extra) %} {% set
label_parts = label|split("\n") %} {{- label_parts[0] -}} {% if tooltip %} {% endif %} {% if label_parts[1] %}
{{ label_parts[1]}} {%
endif %} {% endmacro %}
{% macro input(name, value, type, class, extra) %}
{% endmacro %}
Can anyone please tell me if i am missing something. There will be a lot of code which i cannot defin or explain but i hope i have delivered the idea and the question. Any help would be greatly appreciated.
Related
I'm working on a template and I need to check if something is an array. How do I do that in Twig?
I've tried
{% if my_var is iterable %}
{% for v in my_var %}
...
{% endfor %}
{% else %}
{{ my_var }}
{% endif %}
but it always prints my_var, even when my_var is really an array, as evidenced when it prints out
Array
Array
myusername
../data/table.sqlite3
Another way :
{% if my_var.count()>1 %}
If you don't want to create a custom filter use iterable, as per the docs :
iterable checks if a variable is an array or a traversable object
{% if myVar is iterable %} ... {% endif %}
Just add a custom filter:
$twig->addFilter('is_array', new \Twig_Filter_Function('is_array'));
Then use it like this:
{% if my_var|is_array %}
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 get the following error: Variable "file" does not exist.
The included template Contractor:file.html.twig looks like this:
{{ dump(file) }}
The main template form.html.twig looks like this:
{% extends '::base.html.twig' %}
{% form_theme form 'NaSoftEdmBundle:Form:fields.html.twig' %}
...
{% block files %}
{% for file in form.files %}
{{ include('NaSoftEdmBundle:Contractor:file.html.twig', {'file': file}) }}
{% endfor %}
{% endblock %}
Controller:
public function editAction(Request $request, Contractor $contractor)
{
...
return $this->render('NaSoftEdmBundle:Contractor:form.html.twig', array(
'form' => $form->createView(),
));
}
But when I try to display a variable inside the main template file (form.html.twig), the variable becomes available:
{% for file in form.files %}
{{ dump(file) }} {# it work! #}
{% endfor %}
ContractorFile {#499 ▼
-id: 154
-uploadedFile: null
-name: "57c6d217d9a92.jpg"
-origName: "1471590585502.jpg"
-path: "/contractor/docs/files/2/335"
-size: 153536
-mimeType: "image/jpeg"
-updateDate: DateTime {#496 ▶}
-contractor: Contractor {#370 ▶}
}
command php app/console cache:clear did not help
The problem was that I was before starting the cycle "for" included the same template for attribute data-prototype:
data-prototype="{% filter escape %}
{% include 'NaSoftEdmBundle: Contractor: file.html.twig' with { '_form':form.files.vars.prototype} %}
{% endfilter %}"
I've created for the attribute "data-prototype" new template and it worked.
data-prototype="{% filter escape %}
{% include 'NaSoftEdmBundle: Contractor: filePrototype.html.twig' with { 'file':_form.files.vars.prototype} %}
{% endfilter %}"
This has been implemented to customize the template collection
The correct way to pass variables to a Twig include command is this:
{% include 'NaSoftEdmBundle:Contractor:file.html.twig' with {'file': file} %}
As #walther pointed out in comments, it is not actually required to pass variables since an included template get access to variables of the active context, i.e. you can simply write:
{% include 'NaSoftEdmBundle:Contractor:file.html.twig' %}
and the file variable will be used.
You still have to use the with syntax if you want to assign a new variable, e.g.
{% include 'NaSoftEdmBundle:Contractor:files.html.twig' with {'files': form.files} %}
See http://twig.sensiolabs.org/doc/tags/include.html
I have 2 TWIG renderblock's working on a base and standard template. The two blocks render on the page, everything before {% extends "base.php" %} and after {% block body %}{% endblock %} in base.htm does not render, I see why as I have used renderblock not render which should render the whole template. 1 how do I get whole template to render and 2. {% block head %} will not render unless I use a for loop, I am sure there is a better way of doing this. Edit 1: added $data2.
API
$template = $twig->loadTemplate('event.htm');
echo $template->renderBlock('head', (array('heads' => $data2)));
echo $template->renderBlock('content', (array('events' => $data)));
base.htm
<html>
<head>
{% block head %}
{% for head in heads %}
<title>{{ head.title }}</title>
<meta charset="UTF-8">
</head>
<body>
<h1>{{ head.title2 }}</h1>
{% endfor %}
{% endblock %}
{% block body %}{% endblock %}
</body>
</html>
event.htm
{% extends "base.php" %}
{% block content %}
{% for event in events %}
{{event.uri}}
{{event.desc}}
{% else %}
no events!
{% endfor %}
{% endblock %}
$data2
Array ( [0] => Array ( [id] => 1 [uri] => /event1/1 [title] => some title ) )
1/ As soon as you are rendering a block, you get the content of that block, nothing more.
You need to render the whole template using:
$template = $twig->loadTemplate('event.htm');
echo $template->render(array(
'heads' => $data2,
'events' => $data,
));
2/ You need to use a loop because there are big chances $data2 contains an array or an object instead of the expected header. You should use a string instead, or know in which index you can access your header. This is difficult to help you as I don't know what does contain your $data2 variable, but an ugly solution could be to use the reset function this way:
echo $template->render(array(
'heads' => reset($data2),
'events' => $data,
));
2/ If you know which index (in this case 0) as suggested by Alain
echo $template->render(array(
'heads' => $data2[0],
'events' => $data,
));
I'm new to Symfony, so this is most certain a simple mistake from my side.
I get the following error: Variable "worker" does not exist.
The template looks like this:
{% extends "NTSBSServiceBundle::layout.html.twig" %}
{% block body %}
<h1>Rapportera</h1>
{% for worker in workers if workers %}
{{ worker.name }}
{% else %}
<em>Det finns inga öppna protokoll för närvarande...</em>
{% endfor %}
{% endblock %}
And the controller method look like this:
/**
* List all open protocols, grouped by worker.
*
* #Route("/", name="report")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$workers = $em->getRepository('NTSBSServiceBundle:Worker')->findAll();
return array(
'workers' => $workers,
);
}
I have checked, and $workers does contain entities from the database. The twig gets rendered. If I remove the for-loop, naturally the error message disappears.
Hoping that someone can explain to me what I'm doing wrong.
UPDATE:
Have confirmed that the correct controller is used by exiting in indexAction(). If i do a print_r of $workers, I get the following output:
Array
(
[0] => NT\SBSServiceBundle\Entity\Worker Object
(
[id:NT\SBSServiceBundle\Entity\Worker:private] => 2
[name:protected] => Worker 1
[mail:protected] => worker1#example.com
[phone:protected] => 123456789
)
[1] => NT\SBSServiceBundle\Entity\Worker Object
(
[id:NT\SBSServiceBundle\Entity\Worker:private] => 3
[name:protected] => Worker 2
[mail:protected] => worker2#example.com
[phone:protected] => 123456789
)
)
Also I have tried to change the rendering-method by changing from annotation to using the render-method, as such:
return $this->render('NTSBSServiceBundle:Report:index.html.twig',array( 'workers' => $workers ));
You can not do {% for i in x if x %}
You have to do
{% if x | length > 0 %}
{% for i in x %}
instructions
{% endfor %}
{% endif %}
Use twig doc : http://twig.sensiolabs.org/doc
I do always loop through arrays in Twig like that:
{% for b in books %}
{{ b.name }}
{% endfor %}
{% if not books %}
<i>{% trans %}utils.nothing{% endtrans %}</i>
{% endif %}
But your error looks like a variable is missing. What is your error message?
Symfony2 Variable “name” does not exist
or
Variable "worker" does not exist.