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
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 have an entity "File" and i want to show boolean value of existing related file in filesystem. For checking existence need to use my DirectoriesManager service which can detect it uses this File entity. What is the right way of configuring ListMapper for this task or it can be solved only by rewriting some sonata templates?
So, what i made:
config.yml
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
globals:
container: '#service_container'
My Sonata Admin class
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('id')
->add('exist', null, [
'template' => 'AdminBundle:Files:exist.html.twig'
]);
}
And my template exist.html.twig
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
{% set value = container.get('dirs_manager').entityFileExist(object) %}
{% if value %}
{% set text = 'label_type_yes'|trans({}, 'SonataAdminBundle') %}
{% else %}
{% set text = 'label_type_no'|trans({}, 'SonataAdminBundle') %}
{% endif %}
{% if value %}
{% set class = 'label-success' %}
{% else %}
{% set class = 'label-danger' %}
{% endif %}
<span class="label {{ class }}">{{ text }}</span>
{% endblock %}
Where service DirectoriesManager has alias dirs_manager.
I know that in Yii2 can configure GridView widget columns with callback for all models that shows any value. May be i can make something like in ListMapper?
I would add listener callback for doctrine's postLoad event. And inside that callback would use the service and set corresponding boolean entity's value.
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.
I passed data in 3 languages to the twig template and display this data in this way:
{% set lang=app.request.get("lang")%}
{% for item in contests%}
{% if lang=="fa"%}
{{item.titlefa}}
{% elseif lang=="en"%}
{{item.titleen}}
{% elseif lang=="ar"%}
{{item.titlear}}
{% endif%}
{% endfor%}
It is wirking but I must create 3 if condition for each object in "contests"
How can i show data in this logic:
{% set lang=app.request.get("lang")%}
{{item.title~lang}}
{% endfor%}
that can call proper method in contest
You can use the attribute TWIG function for call at runtime a method name, as example:
{% set lang=app.request.get("lang")%}
{% methodname = 'title'~lang %}
{% for item in contests%}
{{ attribute(item, methodname) }}
{% endfor%}
Hope this help