Can Symfony serve Twig templates with routes that end in .html? - symfony

Is it possible to put a collection of static html files under a symfony 2.x app such that the URLs won't change at all?
I want to maintain urls like this: http ://example.com/town/Blah-blah-in-Bayeux.html
I know that I can rewrite example.com/app.php/town/Blah-blah-in-Bayeux.html so that the app.php isn't part of the script, but how can I ensure that my route to, say, the town action of the default controller ends in .html?

It's quite simple! Here I've used a FrameworkExtraBundle #Route annotation where the routing pattern simply ends in .html.
<?php
namespace Foo\BarBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class HomeController extends Controller
{
/**
* Demo of a route which includes a .html suffix
*
* #Route("/town/Blah-blah-in-{townName}.html", name="town")
* #Template()
*/
public function townAction($townName)
{
# Lookup $town slug to get a town
# ...
# 404 if town not found
# ...
# just for illustration
$town = $townName;
return array('town' => $town);
}
}
{# src/Foo/BarBundle/Resources/Home/town.html.twig #}
{% extends 'FooBarBundle::layout.html.twig' %}
{% block title %}Blah blah in {{ town }} - {{ parent() }}{% endblock %}
{% block content %}
<h1>Blah blah in {{ town }}!</h1>
<p> and other content from the original /town/Blah-blah-in-{{ town }}.html</p>
{% endblock %}

Related

Sonata Admin List Field Template is Ignored

I'm using Symfony 4.1.1 and Sonata Admin Bundle 3.35.2.
I want to use a custom template for a field in an admin's list view. The template is ignored. I am using Twig as my templating engine.
In the admin:
# /src/Admin/ImageAdmin.php
protected function configureListFields(ListMapper $listMapper) {
$listMapper
->add('filename', 'string', ['template' => 'list_image.html.twig'])
;
}
The template:
# /templates/list_image.html.twig
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<img src="{{ value }}" style="width: 200px" />
{% endblock %}
Should be
# /templates/list_image.html.twig
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<div>
<img src="{{ object.filename }}" style="width: 200px" />
</div>
{% endblock %}
SRC will be just filename - not full path for file, so image will not be printed. Fix that problem also.
The other problem is that, you accesed some mystical value? I don't see where you assign value to it.
You can access getters of object by writing object.fieldname. This one works as printing getter function of your current object.
I've had same problem (Symfony 4.1), try solution from here Use custom column in Sonata Admin list
so change:
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
to:
{% extends '#SonataAdmin/CRUD/base_list_field.html.twig' %}
it did work for me. The problem is that even if your location is right (i got to it after some experiments) and you extend the wrong template you wont get any error.
Might be a bit late but for everyone who comes across with the same problem like me - I´ve solved it by creating a specific path within twig.yaml for admin-templates, simply create a subfolder _admin or try to use 'templates/': 'admin' to keep your files where they are (haven't tested this possibility)
# /config/packages/twig.yaml
twig:
paths:
'templates/_admin/': 'admin'
# /src/Admin/ImageAdmin.php
protected function configureListFields(ListMapper $listMapper) {
$listMapper
->add('filename', 'string', ['template' => '#admin/list_image.html.twig'])
;
}

twig inheritance and symfony2 controller variables

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

Permanently extend symfony twig template

In my symfony2 application I created a dashboard which currently consists of many navigation elements.
Now I am trying to split those elements into several bundles.
This is the code I have:
{# app/Resources/views/base.html.twig #}
{# ... #}
{% block body %} {% endblock %}
{# ... #}
Then in the ProfileBundle:
{# src/MyApp/ProfileBundle/Resources/views/Dashboard/index.html.twig #}
{% block body %}
<p>Heading</p>
<ul>
{% block dashboardNavi %} {% endblock %}
</ul>
{% block %}
edit: The controller:
class DashboardController extends Controller
{
public function indexAction()
{
return $this->render('MyAppProfileBundle:Dashboard:index.html.twig', array());
}
}
The routing:
pricecalc_profile_dashboad_security:
pattern: /dashboard
defaults: {_controller: MyAppProfileBundle:Dashboard:index }
That template is rendered correctly, when my route "/dashboard" is loaded.
What I now'd like to do, is extend that dashboardNavi-Block in multiple Bundles without changing the route from my ProfileBundle.
Each of those Bundles brings it`s own routes and controllers for custom actions, but all bundles should extend that one block to add links for their custom actions to the dashboard screen.
What I have so far is:
{# src/MyApp/ProfileNewsletterBundle/Resources/views/Dashboard/indexNewsletter.html.twig #}
{% extends 'MyAppProfileBundle:Dashboard:index.html.twig' %}
{% block dashboardNavi %}
{{ parent() }}
<li>Test</li>
{% endblock %}
but that template is never rendered.
edit 2:
Maybe my understanding of how symfony is working in terms of template inheritance is kind of wrong. I'll specify what I am trying to do.
I got one Bundle (DashboardBundle) which consists of an own route, controller, view etc. The view contains two blocks - like navigation and dashboard.
Now, I would like to have those two blocks extended by some other Bundles - just adding new navigation items and shortcuts on that dashboard and navigation block.
I would like to do those enhancements without modifying my Dashboard-Bundle - if that is possible at all.
When finished, I will have 16 Bundles, each providing own functionality in own Controllers - and they should just be linked on that dashboard.
Is it possible to have the dashboard-view extended that way without modifying the view itself?
I finally managed to fix that after having understood how symfony works in extending controllers and views.
I added a new Controller:
{# src/MyApp/ProfileNewsletterBundle/Controllers/DashboardController.php #}
class DashboardController extends Controller {
public function indexAction()
{
return $this->render('ProfileNewsletterBundle:Dashboard:index.html.twig', array());
}
}
modified the bundle ProfileNewsletterBundle to let the method getParent return ProfileBundle,
and modified the view:
{% extends 'ProfileBundle:Dashboard:index.html.twig' %}
{% block dashboardNavi %}
<li>Test</li>
{% endblock %}
That seems to work fine so far.
Thank you all for spending your time on that.

Twig: Cannot override a block that's defined in a macro

I am importing a macro from a layout file, and inside that macro I define a few twig blocks.
When I extend my 'base' layout file in a new 'view' file, I cannot override the content of the blocks that I defined in the macro.
Here is a very simplified, minimal version of my use-case.
Structure:
base.twig
{% import 'macros' as macros %}
<section>
{{ macros.render_sections() }}
{% block working %}
content1-default
{% endblock %}
</section>
macro.twig
{% macro render_sections() %}
<section>
{% block notworking %}
content2-default
{% endblock %}
</section>
{% endmacro %}
view.twig
{% extends "base" %}
{% block working %}
content1-override
{% endblock %}
{% block notworking %}
content2-override
{% endblock %}
Expected behavior:
I expect to see "content1-override content2-override" in my html.
What actually happens:
I see 'content1-override content2-default'
Is there a way to pass the blocks scope to a macro?
I have tried defining the macro inside the base.twig file, as to rule out the import function, but that didn't help.
and obviously everything else works because I can see the block that does get overriden.
You should learn about Twig's internals:
all Twig files are converted to PHP classes
{% extends %} is the equivalent for the litteral PHP extends (used for inheritance).
{% import %} assign a property of your context to an object of type "the twig file you're importing"
blocks are no more than php methods, and the native php inheritance let you overwrite Twig blocks sweetly.
So, taking this into account, your converted twig code will look like this:
class base {
public function display() {
$context['macros'] = new macros();
$context['macros']->render_sections();
echo '<section>';
echo $this->blockWorking();
echo '</section>';
}
public function blockWorking() {
echo "content1-default";
}
}
class macros {
public function render_sections() {
echo '<section>';
echo $this->blockNotWorking();
echo '</section>';
}
public function blockNotWorking() {
echo "content2-defualt";
}
}
class view extends base {
public function blockWorking() {
echo "content1-override";
}
public function blockNotWorking() {
echo "content2-override";
}
}
$view = new view();
$view->display();
You can clearly see here that the blockNotWorking() method of the view class can never overwrite the macro.

How can I change symfony2 form fields default options globally?

Is there a way to change default options for form fields globally in symfony2?
More specifically, I want to change the render of ALL datetime fields to use single_text instead of the default choice widget.
Can it be done? Or do I need to implement a custom type and set the default in there, like for example the birthdate type?
I prefer an option that leads to minimal changes in the codebase.
The post is old, but you can use an alternative method, overriding the DateType symfony class ...
service.yml
services:
form.type.date:
class: "YourApp\YourBundle\Form\DateType"
tags:
- { name: "form.type", alias: "date" }
DateType.php
<?php
namespace YourApp\YourBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\DateType as SymfonyDateType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DateType extends SymfonyDateType
{
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions( $resolver );
$resolver->setDefault( 'widget', 'single_text' );
}
}
You can check if the service is taken by container
$ ./app/console debug:container | grep form.type.date
form.type.date YourApp\YourBundle\Form\DateType
form.type.datetime Symfony\Component\Form\Extension\Core\Type\DateTimeType
You have to define a form theme.
It's very easy and requires only a little bit coding time. First of all, you have to know which block to customize; in that case, you can do something like
{% block my_data_widget %}
{% spaceless %}
{% if type is defined and type == 'date' %}
// do all your customization
{% else %}
// don't know ...
{% endif %}
{% endspaceless %}
{% endblock form_widget_simple %}
Now that you have defined this snippet of code, you can use it into your main template (or whatever you use into your form views) in that way
{% form_theme form 'YourBundle:Form:myDataWidget' %}
Last but not least important, you have to place your form theme into Resources/views folder. In my example, your path will be Resources/views/Form/myDataWidget
Update
Did you tried with
{% set type = type|default('single_text') %}
or something like that?

Resources