I am currently developping using symfony2 and using FOSUserBundle for user management.
I built a menus.yml config file to separate html from menu structure. Basicaly I import menus.yml in my config.yml file and it's added to twig's global vars. Here's a look at my menus.yml (Abridged version)
twig:
globals:
menus:
loggedin:
topleft:
-
path: ~
caption: RĂ©seau
icon: glyphicon-comment
submenu:
-
path: nouvelles
caption: Fil de nouvelles
icon: glyphicon-globe
topright:
-
path: ~
caption: "{{ app.user.prenom }} {{ app.user.nom }}"
icon: glyphicon-user
Then, in my template html file, I render the menu using this
{% for m in menus.loggedin.topleft %}
<li class="dropdown">
{{ m.caption }}
<ul class="dropdown-menu">
{% for item in m.submenu %}
<li>{{item.caption}}</li>
{% if item.seperator is defined and item.seperator == true %}
<li class="divider"></li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endfor %}
But I am unable to display the user's first name and last name as the textual value gets printed as is into the html page. I tried hooking the app.user into caption like this
caption: %app.user.prenom% %app.user.nom%
But it doesn't work, saying the value doesn't exist (yet?)
Anybody has a clue how I can work this around?
I looked for an equivalent of the eval() PHP or Javascript function in Twig and found this SO question: Twig variables in twig variable.
Here is the code from an answer by Berry Langerak which define a Twig filter:
<?php
/**
* A twig extension that will add an "evaluate" filter, for dynamic evaluation.
*/
class EvaluateExtension extends \Twig_Extension {
/**
* Attaches the innervars filter to the Twig Environment.
*
* #return array
*/
public function getFilters( ) {
return array(
'evaluate' => new \Twig_Filter_Method( $this, 'evaluate', array(
'needs_environment' => true,
'needs_context' => true,
'is_safe' => array(
'evaluate' => true
)
))
);
}
/**
* This function will evaluate $string through the $environment, and return its results.
*
* #param array $context
* #param string $string
*/
public function evaluate( \Twig_Environment $environment, $context, $string ) {
$loader = $environment->getLoader( );
$parsed = $this->parseString( $environment, $context, $string );
$environment->setLoader( $loader );
return $parsed;
}
/**
* Sets the parser for the environment to Twig_Loader_String, and parsed the string $string.
*
* #param \Twig_Environment $environment
* #param array $context
* #param string $string
* #return string
*/
protected function parseString( \Twig_Environment $environment, $context, $string ) {
$environment->setLoader( new \Twig_Loader_String( ) );
return $environment->render( $string, $context );
}
/**
* Returns the name of this extension.
*
* #return string
*/
public function getName( ) {
return 'evaluate';
}
}
Example usage:
$twig_environment->addExtension( new EvaluateExtension( ) );
Use it in the template:
{% set var = 'inner variable' %}
{{'this is a string with an {{var}}'|evaluate}}
Related
So I have a menu built as shown in the example below:
<?php namespace AppBundle\Menu;
use Doctrine\ORM\EntityManager;
use Knp\Menu\FactoryInterface;
use Knp\Menu\MenuFactory;
class AdminMenuBuilder
{
public function sidebarMenu(FactoryInterface $factory, array $options)
{
$menu = $factory->createItem('root', array(
'navbar' => true,
'childrenAttributes' => [
'class' => 'nav main-menu',
],
));
$menu->addChild('Dashboard')
->setAttributes([
'icon' =>'fa fa-dashboard',
'class' => 'dropdown',
'dropdown' => true
]);
$menu['Dashboard']->addChild('Details', ['route' => 'app.admin.dashboard']);
$menu['Dashboard']->addChild('Details 2', ['route' => 'app.admin.dashboard']);
$menu->addChild('Users', ['route' => 'app.admin.dashboard.users'])
->setAttribute('icon', 'fa fa-users');
return $menu;
}
}
How can I create a breadcumb using KNPMenuBundle v2? I'm using symfony2 2.7.5
KnpMenuBundle 2.1.0 (released some weeks ago) has a knp_menu_get_breadcrumbs_array() function which you can use. For instance:
{% set item = knp_menu_get('some_menu_item') %}
{% set breadcrumbs = knp_menu_get_breadcrumbs_array(item) %}
{% for breadcrumb in breadcrumbs %}
{{ breadcrumb.label }}
{% endfor %}
If you have nested menu the above solution won't work. Here is the extension that returns array of active menu items recursively:
<?php
namespace AnalyticsBundle\Twig;
use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\MatcherInterface;
use Knp\Menu\Twig\Helper;
/**
* Class MenuExtension
* #package AnalyticsBundle\Twig
*/
class MenuExtension extends \Twig_Extension
{
private $helper;
private $matcher;
public function __construct(Helper $helper, MatcherInterface $matcher)
{
$this->helper = $helper;
$this->matcher = $matcher;
}
public function getFunctions()
{
return [
new \Twig_SimpleFunction('analytics_get_current_menu_items', function($menu, array $path = array(), array $options = array()) {
$menu = $this->helper->get($menu, $path, $options);
return $this->getCurrentItems($menu);
}),
];
}
private function getCurrentItems(ItemInterface $item)
{
$items = [];
$getActiveItems = function (ItemInterface $item) use (&$getActiveItems, &$items) {
if ($this->matcher->isCurrent($item)) {
$items[] = $item;
foreach ($item->getChildren() as $child) {
$getActiveItems($child);
}
}
};
$getActiveItems($item);
return $items;
}
public function getName()
{
return 'analytics_menu';
}
}
Example usage:
<ul class="page-breadcrumb">
{% for item in analytics_get_current_menu_items('main') %}
<li>
<span>{{ item.label }}</span>
{% if not loop.last %}
|
{% endif %}
</li>
{% endfor %}
</ul>
Is there a way to default to a blank string rather than the translation key in the event the translation has not been found in a Twig template?
I am attempting this sort of thing using the default Twig filter alongside the trans filter however this does not work:
{{ 'crmpicco.general.course.opening_hours_weekend'|default('')|trans }}
You can overwrite the translation extension with your own, so the trans and transchoice filter would behave as you want:
<?php
// src/AppBundle/Twig/EmptyTranslationExtension.php
namespace AppBundle\Twig;
use Symfony\Bridge\Twig\Extension\TranslationExtension;
class EmptyTranslationExtension extends TranslationExtension
{
public function trans($message, array $arguments = [], $domain = null, $locale = null)
{
$value = parent::trans($message, $arguments, $domain, $locale);
return ($message === $value) ? '' : $value;
}
public function transchoice($message, $count, array $arguments = [], $domain = null, $locale = null)
{
$value = parent::transchoice($message, $count, array_merge(['%count%' => $count], $arguments), $domain, $locale);
return ($message === $value) ? '' : $value;
}
}
And register your extension as replacement for the default one:
# app/config/services.yml
services:
twig.extension.trans:
class: AppBundle\Twig\EmptyTranslationExtension
public: false
arguments:
- #translator
tags:
- { name: twig.extension }
This way work, but it is not an optimal solution:
{% set trans_key = 'crmpicco.general.course.opening_hours_weekend' %}
{% set trans_value = trans_key | trans %}
{{ trans_key == trans_value ? '' : trans_value }}
This part works for me:
{{ crmpicco.general.course.opening_hours_weekend == '' ? '' : crmpicco.general.course.opening_hours_weekend|trans() }}
it's even easier than that. you can simply cast the type to string by concatenating with a string.
{{ null ~ ''|trans }}
but given the choice, clean data would be better. or a fix in the translationextension
In my symfony2 action, i have:
$twigCode = '<li>{{ data.value }}</li>'; //In database
$datas = array(array( 'value' => 'line 1'), array( 'value' => 'line 2'));
return $this->render(
'...List.html.twig',
array(
'twigCode' => $twigCode,
'datas' => $datas
)
);
In my twig template, i want something like:
<ul>
{% for data in data %}
{{ render({{ twigCode }}, {data: data}) }}
{% endfor %}
</ul>
Expected that:
<ul>
<li>line 1</li>
<li>line 2</li>
</ul>
You could render and concatenate the twig fragments in the controller:
$templating = $this->container->get('templating');
$someTwig = '';
foreach ($datas as $data)
{
$someTwig .= $templating->render($twigCode, $data);
}
return $this->render('...List.html.twig', array('someTwig' => $someTwig));
Then in the twig:
<ul>
{{ someTwig | raw }}
</ul>
Otherwise, if you really want to do this in the twig you can write a Custom Twig Extension that implements a twig function 'render' so that something like your suggested twig snippet will work:
In the twig extension (you will need to register it as a service, see link above):
class MyTwigExtension extends \Twig_Extension
{
private $templating;
public function__construct($templating)
{
$this->templating = $templating;
}
public function getFunctions()
{
return array(
'render' => new \Twig_Filter_Method($this, 'render'),
);
}
public function render($twigFragment, array $data)
{
return $this->templating->render($twigFragment, $data);
}
}
Then in the twig:
<ul>
{% for data in data %}
{{ render(twigCode, data) | raw }}
{% endfor %}
</ul>
NB - it's possible 'render' is a reserved word for twig, so the custom twig function may need a different name.
You will have to use twig core or may be customized view rendering
Check below code
$loader = new Twig_Loader_Array(array(
'index.html' => 'Hello {{ name }}!',
));
$twig = new Twig_Environment($loader);
echo $twig->render('index.html', array('name' => 'Fabien'));
check here for documentation: http://twig.sensiolabs.org/doc/api.html#built-in-loaders
I am trying to create a filter that will convert an Array to a String.
The error
The filter "toString" does not exist in FooBarMainBundle:Page:News.html.twig at line 15
So far I have the following, mainly followed the official documentation, but during render the filter can not be found.
app/config/services.yml
services:
foobar_main.twig.main_extension:
class: FooBar\MainBundle\Twig\MainExtension
public: false
tags:
- { name: twig.extension }
src/FooBar/MainBundle/Twig/MainExtension.php
<?php
namespace FooBar\MainBundle\Twig;
class MainExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
'toString' => new \Twig_SimpleFilter('toString', array($this, 'toString'))
);
}
public function toString($data)
{
return implode(", ", $data);
}
public function getName()
{
return 'main_extension';
}
}
the Twig template
{% createphp cmfMainContent as="rdf" %}
<div {{ createphp_attributes(rdf) }}>
{% set tags = createphp_content( rdf.tags ) %}
<h5 {{ createphp_attributes( rdf.tags ) }}>{{ tags|toString }}</h5>
</div>
{% endcreatephp %}
I've been having a problem trying to upload a single file with Symfony2.3. When I try to upload the file I get the following error:
FatalErrorException: Error: Call to a member function move() on a non-object in
I have checked $csvFileForm['csvFile']->getData(); and its a string (the name of the file), also $file = $this->getRequest()->files; has size zero.
Here is the show action:
/**
* Finds and displays a Project entity.
*
* #Route("/{id}", name="console_project_show")
* #Method("GET")
* #Template()
*/
public function showAction($id)
{
$csvFileForm = $this->createFormBuilder()
->add('csvFile', 'file')
->getForm();
return array(
'csvFileForm' => $csvFileForm->createView()
);
}
and the form in the template looks like this:
<form method="POST" action="{{ path('console_project_upload_urls', { 'id': entity.id }) }}" >
{{ form_row(csvFileForm.csvFile) }}
{{ form_row(csvFileForm._token) }}
<input type="submit" name="submit" class="submit"/>
</form>
The upload action is this:
/**
* Lists all Project entities.
*
* #Route("/{id}/upload-urls", name="console_project_upload_urls")
* #Method("POST")
*/
public function uploadUrlsAction(Request $request, $id)
{
$csvFileForm = $this->createFormBuilder()
->add('csvFile', 'file')
->getForm();
$request = $this->getRequest();
$csvFileForm->handleRequest($request);
$data = $csvFileForm['csvFile']->getData();
$data->move(".", "something.csv"); // This is where the exception occurs
$file = $this->getRequest()->files; // I have used this to check the size of the array
return $this->redirect(...);
}
Thanks for any help.
You have to add the enctype to your form tag.
<form method="POST" action="{{ path('console_project_upload_urls', { 'id': entity.id }) }}" {{ form_enctype(form) }}>
...
</form>
Note: This is deprecated as of Symfony2.3 and will be removed in Symfony3. You should use the new form_start() twig helper.
{{ form_start(form, {'method': 'POST', 'action': path('console_project_upload_urls', { 'id': entity.id })}) }}
...
{{ form_end(form) }}