SonataAdminBundle Extending templates - symfony

I have set up my Admin class to render a custom template:
public function getTemplate($name)
{
switch ($name)
{
default:
case 'list':
return 'MyBundle:Admin:list.html.twig';
break;
return parent::getTemplate($name);
break;
}
}
This is working OK. I can enter some html in my template file and it renders OK. However, I want to extend the existing templates from the admin bundle as I only want to make some minor changes for this entity.
I've added the following to my template file:
{% extends 'SonataAdminBundle:CRUD:base_list.html.twig' %}
But this gives me the following error:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 77 bytes)
Can anyone tell me what I am doing wrong?

Your switch/case is incorrect.
It should be:
public function getTemplate($name)
{
switch ($name) {
case 'list':
return 'MyBundle:Admin:list.html.twig';
break;
default:
return parent::getTemplate($name);
break;
}
}

I'm not sure if you are doing anything 'wrong' (besides the weird case syntax that doesn't do what I assume you think it does, see http://php.net/manual/en/control-structures.switch.php and scroll down to section describing the importance of 'break' statements).
It does seem like it's possible put symfony in an infinite loop when extending templates. I've seen this with a couple of templates. I haven't figured out exactly what triggers it, but I think it has something to do with bundle inheritance using EasyExtends. In my application I had a child sonata-admin bundle:
class ApplicationSonataAdminBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function getParent()
{
return 'SonataAdminBundle';
}
}
I then had overridden the standard_layout.html.twig with just the contents:
{% extends "SonataAdminBundle::standard_layout.html.twig" %}
This was causing "SonataAdminBundle::standard_layout.html.twig" to be loaded an infinite number of times because the template seems to effectively be extending itself.
Assuming your setup is similar to mine. I suspect that the only way to try to do what you are trying to do is to use a different template name (e.g. "my_standard_layout.html.twig") and then set that template as the application wide default as described here: https://sonata-project.org/bundles/admin/master/doc/reference/templates.html#configuring-templates

Related

Sonata Block - Pass custom arguments

I'm new with Sonata Block Bundle.
I would like to put into my block a map. It uses some JS library. Function of the context, I need to pass different height, width etc... for example.
But I don't know if it fits with my needs.
At first, I wanted to use Sonata Block because my Maps has dependencies with some Services. So this is cool, I can centralise them.
But can I pass some arguments functions the parent who calls my block ?
Thanks for your answer.
Redfog
Okay, if I understood your question, what you want to do, is pass some custom arguments from your template (where you call your block to be precise) to the php class that is executing the block. Let's get started:
Lets add option to pass height attribute:
{% sample render of your block %}
{{ sonata_block_render({'type':'your.block.id'}, {'height': 50}) }}
Now, in your block service (php/class). You have to add this attribute as a default option in your method: setDefaultSettings, like this:
public function setDefaultSettings(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
// your options goes here, and we add our new option right after them
'height' => null // or whatever suits your needs
));
}
Finally, all you have to is access your option from your execute method like this:
public function execute(BlockContextInterface $blockContext, Response $response = null) {
$settings = $blockContext->getSettings();
// now your value can be access from $settings['height'];
}
Let me know if that's what you're looking for.

How can I override the layout of the form at /admin/sonata/user/user/{id}/edit

I have extended the Sonata UserAdmin by creating Application\Sonata\UserBundle\Admin\Model\UserAdmin and extending Admin, then commenting out some fields I would rather not display.
From sonata_user in config.yml:
admin: # Admin Classes
user:
class: Application\Sonata\UserBundle\Admin\Entity\UserAdmin
controller: SonataAdminBundle:CRUD
translation: SonataUserBundle
Where is the template for the form which gets displayed at /admin/sonata/user/user/{id}/editand what are the steps required to override it?
The templates for your forms are in vendor/Sonata/...Resources/views
There are two ways to override these templates. The easiest is to override an individual template by creating it at app/Resources/PATH/view.html.twig.
PATH => the path to access the view you override in vendor, you have to recreate it. I said view.html.twig, but it can be another name, just need to be the same.
So the same way you did with the UserAdmin entity, but in the resources.
The other way is in the case you did your own bundle, that will be the son of one of your vendors bundle.
To get more information, FOSUserBundle documentation is great about how to override things from a parent bundle.
Check this : https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/overriding_templates.md
There is also doc on how to override form & controllers.
Good luck !
Override getTemplate method in UserAdmin class:
public function getTemplate($name)
{
switch ($name) {
case 'edit':
return 'Application\Sonata:User:edit.html.twig';
break;
default:
return parent::getTemplate($name);
break;
}
}
and create Application\Sonata\Resources\views\User\edit.html.twig that will override Sonata's template:
{# edit.html.twig #}
{% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}
And now you can override the blocks from SonataAdminBundle:CRUD:edit.html.twig as you want.

creating css using twig

reading symfony documentation about templating I found mention about twig being able to output css files.
how is this used? is it possible to generate dynamic css same way as I generate html?
for example when I want to display some html template I create controller action and inside I render .html.twig file possibly passing it some parameters.
can I render .css.twig same way? where would be this file stored and how could I possibly include it to another html template.
I would like to keep all styles in separate files, but some of these styles change under some conditions. for example, right now I'm setting height of some divs according to calculations in controller and I pass result height as parameter to template. but I don't feel like this is very MVC, having part of representation logic in controller (or even model).
It certainly is possible. You would do most of things exactly the same as if you would do for html template.
Create file, eg:
/* src/Acme/MyBundle/Resources/views/somefile.css.twig */
.someclasss {
background-color: {{ backgroundColor }};
}
Create controller action
// src/Acme/MyBundle/Controller/MyStyleController.php
// ...
public function styleAction()
{
// Fetch it from service or whatever strategy you have
$backgroundColor = '#ff0000';
return $this->render(
'AcmeMyBundle::somefile.css.twig',
['backgroundColor' => $backgroundColor],
['Content-Type' => 'text/css']
);
}
// ...
Create route for that action
# src/Acme/MyBundle/Resources/config/routing.yml
css_route:
path: /some/path
defaults: { _controller AcmeMyBundle:MyStyleController:style }
methods: [GET]
Use that css in your layout
{# src/AcmeMyBundle/Resources/views/mypage.html.twig #}
{# ... #}
<link href="{{ path('css_route') }}" rel="stylesheet">
{# ... #}
Now, whether or not this is a good idea should be a separate question. There certainly are some cases where this approach is perfectly valid, but there are cases where you can avoid this. Keep in mind that serving CSS file like this is a lot more expensive than serving static CSS file. Furthermore, since it's a CSS file and it's in HEAD section of your response, it will slow down page load time since it will have to fetch CSS file before rendering body itself.
If you do decide to do this be sure to check out caching possibilities you have to make this as fast as possible.
Actually
public function styleAction()
{
// Fetch it from service or whatever strategy you have
$backgroundColor = '#ff0000';
return $this->render(
'AcmeMyBundle::somefile.css.twig',
['backgroundColor' => $backgroundColor],
['Content-Type' => 'text/css']
);
}
should be more like this
/**
* #Route("/css/style", name="style")
* #param Request $request
* #return Response
*/
public function styleAction(Request $request)
{
$firstColor = 'rgba(130, 30, 30, 0.9)';
/** #var Response $response */
$response = $this->render(':css:style.css.twig', [
'firstColor' => $firstColor,
]);
$response->headers->set('Content-Type', 'text/css');
return $response;
}
Note that I have annotations because I use Symfony 3. But the important thing in my code sample is that for the response I set Content-Type to 'text/css'.

Symfony2 twig mobile template fallback

I need a simple way to fallback on a default template if no mobile version exists.
With some regular expressions I recognize mobile platforms and want to render a template with the following pattern:
<template_name>.mobile.html.twig
But if this template doesn't exist, I want it to automatically fallback on:
<template_name>.html.twig
which always exists.
I tried nearly all the answers from this post:
Symfony 2 load different template depending on user agent properties
but with no success. Unfortunately there are no version numbers referenced.
At the moment I am trying to copy and modify the default twig loader.
By the way, What I want to achieve with this is the possibility to deploy different templates for mobile devices by just adding a template of the same name and adding a .mobile.
UPDATE:
http://www.99bugs.com/handling-mobile-template-switching-in-symfony2/
This one is also a good approach. It modifies the format property of the request object which affects the automatic template guessing when you don't specify a template in the controller with the render function (or annotation) but just return an array.
Resulting template name:
view/<controller>/<action>.<request format>.<engine>
So you could switch the request format from html to mobile.html based on the device detection.
The downside of this is that every template needs a mobile.html pendant (which then could just include the non-mobile version if not needed).
UPDATE:
Besides using a custom templating provider there is also the possibility to hook into the kernel.view event.
You could create a service to handle it and then use it in the same way that you do the templating service like so..
Create a service with the templating and request service injected into it..
Service (YAML)
acme.templating:
class: Acme\AcmeBundle\Templating\TemplatingProvider
scope: request
arguments:
- #templating
- #request // I assume you use request in your platform decision logic,
// otherwise you don't needs this or the scope part
- 'html'
Class
class TemplatingProvider
{
private $fallback;
private $platform;
... __construct($templating, $request, $fallback) etc
private function setPlatform() ... Your platform decision logic
private function getPlatform()
{
if (null === $this->platform) {
$this->setPlatform();
}
return $this->platform;
}
private function getTemplateName($name, $platform)
{
if ($platform === 'html') {
return $name;
}
$template = explode('.', $name);
$template = array_merge(
array_slice($template, 0, -2),
array($platform),
array_slice($template, -2)
);
return implode('.', $template);
}
public function renderResponse($name, array $parameters = array())
{
$newname = $this->getTemplateName($name, $this->getPlatform());
if ($this->templating->exists($newname)) {
return $this->templating->render($newname);
}
return $this->templating->renderResponse($this->getTemplateName(
$name, $this->fallback));
}
And then you could just call your templating service instead of the current one..
return $this->container->get('acme.templating')
->renderResponse('<template_name>.html.twig', array());
Can't you check if the template exist before ?
if ( $this->get('templating')->exists('<templatename>.html.twig') ) {
// return this->render(yourtemplate)
} else {
// return your default template
}
OR :
You can create a generic method, to insert in your root controller like :
public function renderMobile($templateName, $params)
{
$templateShortName = explode('.html.twig', $templateName)[0];
$mobileName = $templateShortName.'.mobile.html.twig';
if ( $this->get('templating')->exists($mobileName) ) {
return $this->renderView($mobileName, $params);
} else {
return $this->renderView($templateName, $params)
}
}
with this you can do :
return $this->renderMobile('yourtemplate', [yourparams]);
You can easily do this by harnessing the bundle inheritance properties in Symfony2 http://symfony.com/doc/current/cookbook/bundles/inheritance.html
create a bundle which holds your desktop templates (AcmeDemoDesktopBundle)
create a bundle which will hold your mobile templates (AcmeDemoMobileBundle) and mark the parent as AcmeDemoDesktopBundle
Then when you render a template simply call AcmeDemoMobileBundle:: - if the template exists, it'll be rendered otherwise you'll neatly fall back to the desktop version. No extra code, listeners or anything none-obvious required.
The downside of this of course is that you move your templates out of the individual bundles.
The fallback behavior you describe isn't that easy to implement (we found out the hard way..). Good news is we wanted the same setup as you ask for and ended up using the LiipThemeBundle for this purpose. It allows you to have different "themes" based on for example a device. It will do the fallback part for you.
For example:
Rendering a template:
#BundleName/Resources/template.html.twig
Will render and fallback to in order:
app/Resources/themes/phone/BundleName/template.html.twig
app/Resources/BundleName/views/template.html.twig
src/BundleName/Resources/themes/phone/template.html.twig
src/BundleName/Resources/views/template.html.twig
Edit: so with this approach you can have default templates that will always be the final fallback and have a special template for mobile where you need it.

Twig CamelCase Filter in Symfony2

So I'm pretty new to Symfony2 and I'm trying to use the camelize filter in a twig template. However when I request the page I get an error saying that the filter doesn't exist:
The filter "camelize" does not exist in ::base.html.twig
Here's the line from my template file:
{{ 'hello world' | camelize }}
The filter is listed on Twig's quick reference page.
I'm confused, doesn't Symfony2 support all of twig's filters? There seem to be quite a few missing, why? And if it doesn't support them, then is there any way to add the missing ones in?
Thanks in advance!
edit Ok, so it turns out I'm retarded and I need to remember to check that I've actually got the right git project. No wonder I was confused. Thanks replies!
Symfony 2 has title filter for camel case use
{{ entity.yourstring | title }}
to camel case your string
Your link points to a fork on GitHub, meaning a modified copy of the original project. The original project is https://github.com/fabpot/Twig.
There is no camelize filter in Twig. Built-in filters are here. You can write your own camilize filter (it's easy, actually...) following this tutorial: How to write a custom Twig Extension.
EDIT: just for fun, you can write something like:
class MyTwigExtension extends Twig_Extension
{
public function getFilters()
{
return array(
'camelize' => new Twig_Filter_Method($this, 'camelizeFilter'),
);
}
public function camelizeFilter($value)
{
if(!is_string($value)) {
return $value;
}
$chunks = explode(' ', $value);
$ucfirsted = array_map(function($s) { return ucfirst($s); }, $chunks);
return implode('', $ucfirsted);
}
public function getName()
{
return 'my_twig_extension';
}
}
Note that this is a quick and dirty filter! Take a look at the built-in filters to learn best practice!
The filter you're looking for is named "title": http://twig.sensiolabs.org/doc/filters/title.html
Here is the best solution by default in Craft CMS 3
Craft 3 now has a |camel filter for twig
https://docs.craftcms.com/v3/dev/filters.html#camel
{{ 'foo bar'|camel }}
{# Output: fooBar #}

Resources