Default to blank string in Twig template if translation is not found - symfony

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

Related

Symfony 5 : handling multiple forms in one controller

I am trying to manage multiple forms in the same page in Symfony 5 with the following function, but it seems that every time I try to submit a form, only the first form of the list is handled even if it is not the one that has been submitted:
class ContentController extends AbstractController
{
/**
* #Route("/category/edition/{content}", name="edit_category")
*/
public function edition(Request $request, Category $content): Response
{
$forms = [
"edit_category" => $this->createForm(EditCategoryType::class, $content),
"create_post" => $this->createForm(CreatePostType::class)
];
foreach($forms as $form) {
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
// Always prints edit_category
// even when that is the the create_post that is submitted
return var_dump($form->getName());
}
}
return $this->render(
'content/edition.html.twig',
[
'forms' => \array_map(
function($form) {
return $form->createView();
},
$forms
),
'content' => $content,
]
);
}
}
I have seen in other posts that the name of the forms can sometime raise an issue, but I have checked that the forms do have different names, and I have also tried to call handleRequest() on every form in a separate foreach loop because I have seen this done in some posts, but it (quite expectedly I must say) didn't change the behavior.
And I didn't seem to find any unanimous best practice tips about how to handle multiple forms in the same Controller in Symfony, so I was wondering what is the best way to do it, or if it would be cleaner to define a separate action route for each form in order to avoid this problem altogether.
If needed, the content/edition.html.twig file looks something like that:
{% set edit_category_form = forms['edit_category'] %}
{% set create_post_form = forms['create_post'] %}
{{ form_start(edit_category_form) }}
{{ form_errors(edit_category_form) }}
{{ form_end(edit_category_form) }}
{{ form_start(create_post_form) }}
{{ form_errors(create_post_form) }}
{{ form_end(create_post_form) }}
(Category is a classical Symfony entity, EditCategoryType is a form associated with the Category entity and CreatePostType is a form associated with another Symfony entity)
After some research, it seems for some reason like it works if (and only if?) the form is build just before handling the request:
class ContentController extends AbstractController
{
/**
* #Route("/category/edition/{content}", name="edit_category")
*/
public function edition(Request $request, Category $content): Response
{
$self = $this;
$formBuilders = [
"edit_category" => function() use ($self, $content) {
return $self->createForm(EditCategoryType::class, $content);
},
"create_post" => function() use ($self, $content) {
return $self->createForm(CreatePostType::class);
},
];
$forms = [];
foreach($formBuilders as $key => $formBuilder) {
$form = $formBuilder();
$forms[$key] = $form;
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
// Does print the name of the right form
return var_dump($form->getName());
}
}
return $this->render(
'content/edition.html.twig',
[
'forms' => \array_map(
function($form) {
return $form->createView();
},
$forms
),
'content' => $content,
]
);
}
}
It works but it doesn't feel like a proper way to handle this !

Sonata Action with custom form

I have a custom action, based on the editAction from Sonata. Only the form is different.
public function customAction(){
$id = $this->get('request')->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id : %s', $id));
}
if (false === $this->admin->isGranted('EDIT', $object)) {
throw new AccessDeniedException();
}
// On vérifie que l'annonce appartient bien à l'utilisateur connecté
if($this->getUser()->getId() !== $object->getUser()->getId()) {
throw new AccessDeniedException();
}
$em = $this->getDoctrine()->getManager();
$preparechoices = $em->getRepository('AcmeBundle:Entity')->findAll();
foreach($preparechoices as $c){
$choices[$c->getId()] = $c->getLibelle();
}
$form = $this->createFormBuilder(array('choix'=>1))
->add('choix','choice',array('choices'=>$choices))
->add('submit','submit')
->getForm();
$view = $form->createView();
$this->admin->setSubject($object);
$this->get('twig')->getExtension('form')->renderer->setTheme($view, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate('EDIT'), array(
'action' => 'edit',
'object' => $object,
'form' => $view,
));
}
But I got this error :
Impossible to access a key ("default") on a boolean variable ("")
The error come from this line in the twif file :
{{ form_helper.render_groups(admin, form, admin.formtabs['default'].groups, has_tab) }}
I can't find how to fix it, does anyone know ?
Thanks to Joshua, i was able to fix the error by adding this line:
$this->admin->setFormTabs(array('default'=>array('groups' => array())));
But now, i got a new error :
Impossible to access an attribute ("help") on a null variable
Form form_admin_fields.html.twig, this line, because sonata_admin.field_description is null :
{% if sonata_admin.field_description.help %}
title="{{ sonata_admin.admin.trans(sonata_admin.field_description.help, {}, sonata_admin.field_description.translationDomain)|raw }}"
{% endif %}
I don't know how to fix it, i tried several test, whitout success, in the form definition like :
$form = $this->createFormBuilder(array('choix'=>1))
->add('choix','choice',array('choices'=>$choices,'sonata_admin'=>array('field_description'=>array('help'=>'help_message'))))
->add('submit','submit')
->getForm();

Twig custom filter not recognized

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 %}

How to include user info in yaml menu in symfony2?

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}}

Bundle Does Not Exist: Symfony2

I'm pretty new to Symfony2. I can't figure out what's going on. This code (set up to test if the bundle can be detected):
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Ivory\GoogleMap\Overlays\Animation;
use Ivory\GoogleMap\Overlays\Marker;
class DefaultController extends Controller {
public function mapAction() {
$map = $this->get ( 'ivory_google_map.map' );
return $this->render ( 'KrewMediaLocalFarmBundle:Default:map.html.twig', array('map' => $map) );
}
}
works, rendering a simple map, while this code (the real code that involves embedding a controller to render a map with data)
<?php
// localfarm/src/KrewMedia/Bundle/LocalFarmBundle/Controller/DefaultController.php
namespace KrewMedia\Bundle\LocalFarmBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Ivory\GoogleMap\Overlays\Animation;
use Ivory\GoogleMap\Overlays\Marker;
class DefaultController extends Controller {
public function mapAction() {
//$map = $this->get ( 'ivory_google_map.map' );
return $this->render ( 'KrewMediaLocalFarmBundle:Default:maptest.html.twig');
}
}
gives me this error: "An exception has been thrown during the rendering of a template ("Bundle "LocalFarmBundle" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your AppKernel.php file?") in KrewMediaLocalFarmBundle:Default:maptest.html.twig at line 3."
Both maptest.html.twig and map.html.twig are in the same folder in the LocalFarmBundle. I wonder why the bundle is found in the first piece of code but not in the second. The relevant routing is this
krew_media_local_farm_homepage:
pattern: /index
defaults: { _controller: KrewMediaLocalFarmBundle:Default:index }
krew_media_local_farm_map:
pattern: /map
defaults: { _controller: KrewMediaLocalFarmBundle:Default:map }
krew_media_basic_map:
pattern: /map/basic
defaults: { _controller: KrewMediaLocalFarmBundle:Map:basic }
map.html.twig:
{{ google_map_container(map) }}
{{ google_map_js(map) }}
maptest.html.twig:
Map Test
{% render(controller( 'LocalFarmBundle:Map:basic')) %}
The controller for 'LocalFarmBundle:Map:basic':
public function basicAction() {
// set up map
$map = $this->get ( 'ivory_google_map.map' );
// Get User geo info
$user = $this->getUser ();
if (isset ( $user )) { // check to see if logged in: map is useless without it
$map->setAutoZoom ( true );
$map->setStylesheetOption('width', '500px');
$map->setStylesheetOption('height', '500px');
$radius = 1;
// get repository for user class
$coordList = $this->container->get ( 'sylius.repository.user' )->findUsersNearUser ( $user, $radius );
// set user marker
$this->placeMarker ( $user, "/assets/img/home.png", $map );
if (! empty ( $coordList )) {
foreach ( $coordList as $geo ) {
$this->placeMarker ( $geo, "/assets/img/neighbor.png", $map );
}
}
// get furthest distance
$dist = $this->getFurthestDistance ( $user, $coordList );
// set invisible boundary markers
$this->addBoundaries ( $user, $dist, $map );
// render the map
} else {
echo "You are not logged in. Please log in and try again.";
}
return $this->render ( 'KrewMediaLocalFarmBundle:Default:map.html.twig', array (
'map' => $map
) );
Any help would be appreciated in figuring out this problem.
render tag requires full name of bundle, so change
{% render(controller( 'LocalFarmBundle:Map:basic')) %}
to
{% render(controller( 'KrewMediaLocalFarmBundle:Map:basic')) %}

Resources