twig inheritance and symfony2 controller variables - symfony

Im trying my first project using symfony2 + twig. I created basic twig template with defined blocks. It basically looks like this
{% block content %}
some content...
{% endblock %}
{% block footer %}
{{ footer.content}}
{% endblock %}
I want footer to be same for all pages. Footer is loaded from DB and its set in controller. I wanted inherit from template described above to other pages but I have to always set footer in controller otherwise variable is not defined.
My questions is if exists any 'nice' way how to set footer variable for multiple templates inherited from parent template?

The solution : Embedding Controllers
In some cases, you need to do more than include a simple template.
Suppose you have a sidebar in your layout that contains the three most
recent articles. Retrieving the three articles may include querying
the database or performing other heavy logic that can't be done from
within a template.
The solution is to simply embed the result of an entire controller
from your template. First, create a controller that renders a certain
number of recent articles:
Controller
// src/AppBundle/Controller/ArticleController.php
namespace AppBundle\Controller;
// ...
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// make a database call or other logic
// to get the "$max" most recent articles
$articles = ...;
return $this->render(
'article/recent_list.html.twig',
array('articles' => $articles)
);
}
}
View
{# app/Resources/views/article/recent_list.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}
Layout
{# app/Resources/views/base.html.twig #}
{# ... #}
<div id="sidebar">
{{ render(controller(
'AppBundle:Article:recentArticles',
{ 'max': 3 }
)) }}
</div>

you can do with one of the following, 'write a custom Twig Extension'
<?php
namespace AppBundle\Extension;
class MyTwigExtension extends \Twig_Extension
{
private $em;
private $conn;
public function __construct(\Doctrine\ORM\EntityManager $em) {
$this->em = $em;
$this->conn = $em->getConnection();
}
public function getFunctions()
{
return array(
'users' => new \Twig_Function_Method($this, 'getUsers'),
);
}
public function getUsers()
{
$sql = "SELECT * FROM users ORDER BY accountname";
return $this->conn->fetchAll($sql);
}
public function getName()
{
return 'smproc4_twig_extension';
}
}
Register an Extension as a Service
services:
my.twig.extension:
class: AppBundle\Extension\MyTwigExtension
tags:
- { name: twig.extension }
arguments:
em: "#doctrine.orm.entity_manager"
Using the custom Extension
Hello {{ name }}!
<ul>
{% for user in users() %}
<li>{{ user.accountname }}</li>
{% endfor %}
</ul>
have a look at the
How to Write a custom Twig Extension
Creating an Extension

Related

EasyAdmin3: Passing additional variables to twig-template (EDIT page)

Background
I want to display entity-independent information in the EDIT-page. According to the official documentation it can be solved with configureResponseParameters-function.
Problem
So I implemented that function into my CRUD-controller.
on INDEX twig template: I can use/find the variable
on EDIT twig template: I cannot use/find the variable
Any idea why?
How can I achieve to pass data to my EDIT-twig template? Many Thanks!
My Files
PersonCrudController.php
class PersonCrudController extends AbstractCrudController {
public function configureCrud(Crud $crud): Crud {
$crud->setFormThemes([
'mystuff/person_edit_form.html.twig', '#EasyAdmin/crud/form_theme.html.twig'
]);
return $crud;
}
public function configureFields(string $pageName): iterable {
// ...
yield TextField::new('specialDisplay')->setFormTypeOption('mapped', false);
// ...
}
public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
{
$responseParameters->set('testtesttest', '123xyz');
return $responseParameters;
}
// ...
}
mystuff/person_edit_form.html.twig
{% block _Person_specialDisplay_row %}
{{ testtesttest }} {# throws: Variable "testtesttest" does not exist. #}
{{ dump() }} {# cannot find "testtesttest" anywhere in dump #}
{# ... here comes the rest that is working perfectly ... #}
{% endblock %}

How can I determine if key is an object (twig)?

I want to determine if my key is an object:
{% for key in columns %}
{% if key is object %}
This is an object
{% else %}
This in not an object
{% endif %}
{% endfor %}
But I get the error message:
Unknown "object" test.
You can create your own Twig extension. I see you've tagged your question with Symfony so assuming you use Twig in Symfony, you can follow this tutorial:
https://symfony.com/doc/3.4/templating/twig_extension.html
What you need to do is add new TwigTest based on this example:
https://twig.symfony.com/doc/2.x/advanced.html#tests
You should end up with something like this:
// src/AppBundle/Twig/AppExtension.php
namespace AppBundle\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigTest;
class AppExtension extends AbstractExtension
{
public function getTests()
{
return array(
new TwigTest('object', array($this, 'isObject')),
);
}
public function isObject($object)
{
return is_object($object);
}
}
Code above is not tested, but should work fine.
An easy way to check if a variable is an object or a string :
{% if var.id|default('') is not same as ('') %}

Twig Slimframework base template data

so I've been working on a simple website and have a simple question, I guess. I have my base template named template.html and I'm using it on every html page I have. My question is, can a base template have a data from a database? Because it isn't being rendered right? For example I have here my 404 page (I'm using slim framework btw).
$app->notFound(function () use ($app) {
$app->render('404.html');
});
And what is inside looks like this:
As you can see in the image, there is the part <!-- Loaded data from the base template.html -->. Can a base template have a data from a database? If so, how? Thank you!
You can use inheritance.
Simple method
Your route definition:
<?php
/**
* Not found.
*/
$app->notFound(function () use ($app) {
//Your amazing business to get data
$books = array(
"The Hobbit",
"Leaf by Niggle",
"The Lay of Aotrou and Itroun",
"Farmer Giles of Ham",
"The Homecoming of Beorhtnoth Beorhthelm's Son"
);
$app->render('404.html', array(
'books' => $books
));
});
Your template.html:
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">
{% block content %}
{% block books %}
<h3>My magic books</h3>
<ul class="books">
{% for book in books %}
<li>{{ book }}</li>
{% endfor %}
</ul>
{% endblock %}
{% endblock %}
</div>
</body>
</html>
Your 404.html:
{% extends "template.html" %}
{% block title %}404{% endblock %}
{% block content %}
{% block books %}
<div id="">
{{ parent() }}
</div>
{% endblock %}
{% endblock %}
Hook method
If you need it everywhere, you could use a hook.
$app->hook('slim.before.router', function() use ($app) {
//Your amazing business to get data
$books = array(
"The Hobbit",
"Leaf by Niggle",
"The Lay of Aotrou and Itroun",
"Farmer Giles of Ham",
"The Homecoming of Beorhtnoth Beorhthelm's Son"
);
$app->view()->setData('books', $books);
});
//And let your notFound handler be light
$app->notFound(function () use ($app) {
$app->render('404.html');
});
Yes, a base template can display a DB content:
you have to retrieve the data you want, render it to specific template to display and then, in your base template, you use the render(controller) method, something like
public function xyzAction(){
$data=$this->getDoctrine .... ;
return $this->render('XYZYourBundle:test.html.twig',array('data'=>$data));
}
in test.html.twig:
{℅ for d in data %}
// do your stuff
{% endfor ℅}
and in where you want to add this to your base template:
{{ render(controller('XYZYourBundle:Controller_name:xyz'))}}
you can pass arguments to the controller action too, this would be helpful.
In Slim 3 you can use Middleware instead hooks.
BooksMiddleware.php
class BooksMiddleware{
protected $container;
public function __construct($container) {
$this->container = $container;
}
public function __invoke($request, $response, $next){
$sql = "SELECT * FROM books";
$stmt = $this->container->db->prepare($sql);
$stmt->execute();
$books= $stmt->fetchAll(PDO::FETCH_OBJ);
$request = $request->withAttribute('books', $books);
$response = $next($request, $response);
return $response;
}
}
routes.php
$app->get('/home', '\HomeController:home')->add(BooksMiddleware::class);
$app->get('/about', '\AboutController:about')->add(BooksMiddleware::class);
HomeController.php
class HomeController{
protected $container;
public function __construct($container) {
$this->container = $container;
}
public function home($request, $response) {
return $this->container->view->render($response, 'home.html', [
'books' => $request->getAttribute('books')
]);
}
AboutController.php
class AboutController{
protected $container;
public function __construct($container) {
$this->container = $container;
}
public function about($request, $response) {
return $this->container->view->render($response, 'about.html', [
'books' => $request->getAttribute('books')
]);
}
}

Set Twig layout in controller

I have multiple subdomains, each with its own layout. Some controllers are shared across subdomains (e.g login), some not. What I'd like to do is to set layout according to domain, so that I would not need to write in each template:
{% if app.request.domain == 'one' %}
{% set layout = '::layout-one.html.twig' %}
{% elseif app.request.domain == 'two' %}
{% set layout = '::layout-two.html.twig' %}
...
{% endif %}
{% extends layout %}
Is it possible to set default layout in controller (or somewhere)? E.g:
class FooController
{
function fooAction()
{
...
$templating = $this->get('templating');
$templating->setLayout($layout);
return $templating->renderResponse($view, $parameters, $response);
}
}
If you have a separate config file for each of the domains, you can put the layout config in there and have it available in twig as a global variable:
config_one.yml
twig:
globals:
base_layout: '::layout-one.html.twig'
Then in twig you can just do:
{% extends base_layout %}
You can set layout variable in your FooController:
class FooController
{
function fooAction()
{
...
return $this->render($template, array(
'layout' => $layout
));
}
}
And then use it in your template:
{% extends layout %}

Problems showing fields with Sonata Admin - Symfony 2.0

I want to show some fields of an object with SonataAdmin. One of these fields is an integer (status) in the database, but I don't want to show the integer, else a specific string depending on the value of this field.
public function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('notice')
->add('start')
->add('end')
->add('status')
;
}
Is it possible?
And a second question:
in the same example, I want to add a field which is not mapped in the database (people) because this is calculated with data related with other objects.
public function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('notice')
->add('start')
->add('end')
->add('status')
->add('people')
;
}
Can I do this with Sonata Admin?
Thanks in advance.
I guess your best way to do this is to make custom setters and getters in your entity.
For example you have the entity user:
private $customState; // NOTE -> NO ORM MAPPING because you don't want an actual column
public function setCustomState() {
if($this->state){
$this->customState = 'yup!';
return $this;
}
$this->customState = 'nope!';
return $this;
}
public function getCustomState() {
return $this->customState;
}
Thanks for the answer. However, I've been looking for information on Internet and I've found other way to do this. You can render a specific template for your field. In your Admin class, in the configureListFields function you could do this:
public function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('notice')
->add('start')
->add('end')
->add('status', 'string', array('template' => 'YourBundle:YourController:status_field.html.twig'))
->add('Resources', 'string', array('template' => 'YourBundle:YourController:resources_field.html.twig'))
}
For the "status" field, the status_field.html.twig template will be rendered as below:
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<div>
{% if object.status == 0 %}
<strong>Inactivo</strong>
{% elseif object.status == 1 %}
<strong>Activo</strong>
{% endif %}
</div>
{% endblock %}
And for my second question, the same solution is valid. The template rendered for the "resources" field would be as below:
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<div>
<strong>{{ object.getResources|length }}</strong>
</div>
{% endblock %}
In this way, the object is passed to the template, and you can use its methods to get the information that you need. In this case, the method getResources() is used to display its length.

Resources