Symfony http://symfony.com/doc/current/index.html
Vidéos https://symfonycasts.com/screencast/symfony
Doctrine https://symfony.com/doc/current/doctrine.html http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html
Most of the time, you’ll be working in src/, templates/ or config/.
When you install new packages, new directories will be created automatically when needed.
- config/ Contains… configuration!. You will configure routes, services and packages.
- src/ All your PHP code lives here.
- templates/ All your Twig templates live here.
- bin/ The famous bin/console file lives here (and other, less important executable files).
- var/ This is where automatically-created files are stored, like cache files (var/cache/) and logs (var/log/).
- vendor/ Third-party (i.e. “vendor”) libraries live here! These are downloaded via the Composer package manager.
- public/ This is the document root for your project: you put any publicly accessible files here.
An autoloader is a tool that makes it possible to start using PHP classes without explicitly including the file containing the class.
git update-index --assume-unchanged app/config/parameters.yml
git reset --hard HEAD
git checkout HEAD fichier
git revert HEAD
php composer.phar --version
php composer.phar self-update
php composer.phar self-update --rollback
php composer.phar update
php -d memory_limit=-1 composer.phar update
# run this if you are building a traditional web application
composer create-project symfony/website-skeleton my_project_name
# run this if you are building a microservice, console application or API
composer create-project symfony/skeleton my_project_name
# The Symfony Demo application
symfony new my_project_name --demo
symfony server:start
symfony server:stop
# ou
Ctrl+C dans la console
symfony check:requirements
symfony check:security
php bin/console
php bin/console about
php bin/console cache:clear
php bin/console debug:router
php bin/console debug:router app_lucky_number
php bin/console router:match /lucky/number/8
php bin/console debug:autowiring
php bin/console make:controller BrandNewController
php bin/console make:crud Product
php bin/console lint:twig
php bin/console lint:twig templates/email/
php bin/console lint:twig templates/article/recent_list.html.twig
php bin/console lint:twig --show-deprecations templates/email/
php bin/console debug:twig
php bin/console debug:twig --filter=date
php bin/console debug:twig @Twig/Exception/error.html.twig
Flex executed a recipe, which is a set of automated instructions that tell Symfony how to integrate an external package. Flex recipes exist for many packages and have the ability to do a lot, like adding configuration files, creating directories, updating .gitignore and adding new config to your .env file. Flex automates the installation of packages so you can get back to coding.
A common practice when developing Symfony applications is to install packages (Symfony calls them bundles) that provide ready-to-use features. Packages usually require some setup before using them (editing some file to enable the bundle, creating some file to add some initial config, etc.)
Most of the time this setup can be automated and that’s why Symfony includes Symfony Flex, a tool to simplify the installation/removal of packages in Symfony applications. Technically speaking, Symfony Flex is a Composer plugin that is installed by default when creating a new Symfony application and which automates the most common tasks of Symfony applications.
Symfony Flex modifies the behavior of the require, update, and remove Composer commands to provide advanced features. Consider the following example:
If you execute that command in a Symfony application which doesn’t use Flex, you’ll see a Composer error explaining that logger is not a valid package name. However, if the application has Symfony Flex installed, that command installs and enables all the packages needed to use the official Symfony logger.cd my-project/ composer require logger
Sometimes a single feature requires installing several packages and bundles. Instead of installing them individually, Symfony provides packs, which are Composer metapackages that include several dependencies. For example, to add debugging features in your application, you can run the
command. This installs the symfony/debug-pack, which in turn installs several packages like symfony/debug-bundle, symfony/monolog-bundle, symfony/var-dumper, etc. By default, when installing Symfony packs, your composer.json file shows the pack dependency (e.g. "symfony/debug-pack": "^1.0") instead of the actual packages installed. To show the packages, add the --unpack option when installing a pack:composer require --dev debug
Or run this command to unpack the already installed packs:composer require debug --dev --unpackcomposer unpack PACK_NAMEcomposer unpack debug
A controller is the PHP function you write that builds the page. You take the incoming request information and use it to create a Symfony Response object, which can hold HTML content, a JSON string or even a binary file like an image or PDF.
A controller is a PHP function you create that reads information from the Request object and creates and returns a Response object. The response could be an HTML page, JSON, XML, a file download, a redirect, a 404 error or anything else. The controller executes whatever arbitrary logic your application needs to render the content of a page.
In Symfony, a controller is usually a class method which is used to accept requests, and return a Response object. When mapped with a URL, a controller becomes accessible and its response can be viewed.
To facilitate the development of controllers, Symfony provides an AbstractController. It can be used to extend the controller class allowing access to some frequently used utilities such as render() and redirectToRoute(). The AbstractController also provides the createNotFoundException() utility which is used to return a page not found response.
But be careful not to confuse the terms front controller and controller. Your app will usually have just one front controller, which boots your code. You will have many controller functions: one for each page.
# The generateUrl() method is just a helper method that generates the URL for a given route:
$url = $this->generateUrl('app_lucky_number', ['max' => 10]);
# If you want to redirect the user to another page, use the redirectToRoute() and redirect() methods:
use Symfony\Component\HttpFoundation\RedirectResponse;
// ... public function index() {
// redirects to the "homepage" route
return $this->redirectToRoute('homepage');
// redirectToRoute is a shortcut for:
// return new RedirectResponse($this->generateUrl('homepage'));
// does a permanent - 301 redirect
return $this->redirectToRoute('homepage', [], 301);
// redirect to a route with parameters
return $this->redirectToRoute('app_lucky_number', ['max' => 10]);
// redirects to a route and maintains the original query string parameters
return $this->redirectToRoute('blog_show', $request->query->all());
// redirects externally
return $this->redirect('http://symfony.com/doc');
}
# If you’re serving HTML, you’ll want to render a template. The render() method renders a template and puts that content into a Response object for you:
// renders templates/lucky/number.html.twig
return $this->render('lucky/number.html.twig', ['number' => $number]);
# Symfony comes packed with a lot of useful classes and functionalities, called services. These are used for rendering templates, sending emails, querying the database and any other “work” you can think of.
# If you need a service in a controller, type-hint an argument with its class (or interface) name. Symfony will automatically pass you the service you need:
use Psr\Log\LoggerInterface;
// ...
/**
* @Route("/lucky/number/{max}")
*/
public function number($max, LoggerInterface $logger) {
$logger->info('We are logging!');
// ...
}
# What if you need to read query parameters, grab a request header or get access to an uploaded file? That information is stored in Symfony’s Request object. To access it in your controller, add it as an argument and type-hint it with the Request class:
use Symfony\Component\HttpFoundation\Request;
public function index(Request $request, $firstName, $lastName) {
$page = $request->query->get('page', 1);
// ...
}
# Symfony provides a session service that you can use to store information about the user between requests. Session is enabled by default, but will only be started if you read or write from it.
Session storage and other configuration can be controlled under the framework.session configuration in config/packages/framework.yaml.
To get the session, add an argument and type-hint it with Symfony\Component\HttpFoundation\Session\SessionInterface:
use Symfony\Component\HttpFoundation\Session\SessionInterface;
public function index(SessionInterface $session) {
// stores an attribute for reuse during a later user request
$session->set('foo', 'bar');
// gets the attribute set by another controller in another request
$foobar = $session->get('foobar');
// uses a default value if the attribute doesn't exist
$filters = $session->get('filters', []);
}
# As mentioned earlier, Symfony will pass the Request object to any controller argument that is type-hinted with the Request class:
use Symfony\Component\HttpFoundation\Request;
public function index(Request $request) {
$request->isXmlHttpRequest();
// is it an Ajax request?
$request->getPreferredLanguage(['en', 'fr']);
// retrieves GET and POST variables respectively
$request->query->get('page');
$request->request->get('page');
// retrieves SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by foo
$request->files->get('foo');
// retrieves a COOKIE value
$request->cookies->get('PHPSESSID');
// retrieves an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content-type');
}
# To return JSON from a controller, use the json() helper method. This returns a JsonResponse object that encodes the data automatically:
// ... public function index() {
// returns '{"username":"jane.doe"}' and sets the proper Content-Type header
return $this->json(['username' => 'jane.doe']);
// the shortcut defines three optional arguments
return $this->json($data, $status = 200, $headers = [], $context = []);
}
# If the serializer service is enabled in your application, it will be used to serialize the data to JSON. Otherwise, the json_encode function is used.
# You can use the file() helper to serve a file from inside a controller:
public function download() {
// send the file contents and force the browser to download it
return $this->file('/path/to/some_file.pdf');
}
# The file() helper provides some arguments to configure its behavior:
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
public function download() {
// load the file from the filesystem $file = new File('/path/to/some_file.pdf');
return $this->file($file);
// rename the downloaded file
return $this->file($file, 'custom_name.pdf');
// display the file contents in the browser instead of downloading it
return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}
Routes can be configured in YAML, XML, PHP or using annotations.
All formats provide the same features and performance, so choose your favorite.
Symfony recommends annotations because it’s convenient to put the route and controller in the same place.
Each route name must be unique in the application.
/** * @Route("/blog", name="blog_list") */ public function list() { // ... }
By default, routes match any HTTP verb (GET, POST, PUT, etc.) Use the methods option to restrict the verbs each route should respond to:
/** * @Route("/api/posts/{id}", methods={"GET","HEAD"}) */
public function show(int $id) {
// ... return a JSON response with the post
}
/** * @Route("/api/posts/{id}", methods={"PUT"}) */
public function edit(int $id) {
// ... edit a post
}
In Symfony routes, variable parts are wrapped in { ... } and they must have a unique name. For example, the route to display the blog post contents is defined as /blog/{slug}
/**
* @Route("/blog/{slug}", name="blog_show")
*/
public function show(string $slug) {
// $slug will equal the dynamic part of the URL
// e.g. at /blog/yay-routing, then $slug='yay-routing'
// ...
}
Routes can define any number of parameters, but each of them can only be used once on each route:
/blog/posts-about-{category}/page/{pageNumber}
Imagine that your application has a blog_show route (URL: /blog/{slug}) and a blog_list route (URL: /blog/{page}). Given that route parameters accept any value, there’s no way to differentiate both routes.
If the user requests /blog/my-first-post, both routes will match and Symfony will use the route which was defined first. To fix this, add some validation to the {page} parameter using the requirements option:
/** * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}) */
public function list(int $page) {
// ...
}
/** * @Route("/blog/{slug}", name="blog_show") */
public function show($slug) {
// ...
}
If you prefer, requirements can be inlined in each parameter using the syntax {parameter_name<requirements>}.
This feature makes configuration more concise, but it can decrease route readability when requirements are complex:
/** * @Route("/blog/{page<\d+>}", name="blog_list") */ public function list(int $page) { // ... }
In the previous example, the URL of blog_list is /blog/{page}. If users visit /blog/1, it will match. But if they visit /blog, it will not match. As soon as you add a parameter to a route, it must have a value. You can make blog_list once again match when the user visits /blog by adding a default value for the {page} parameter. When using annotations, default values are defined in the arguments of the controller action. In the other configuration formats they are defined with the defaults option:
/** * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}) */ public function list(int $page = 1) { // ... }
You can have more than one optional parameter (e.g. /blog/{slug}/{page}), but everything after an optional parameter must be optional.
For example, /{page}/blog is a valid path, but page will always be required (i.e. /blog will not match this route).
To give a null default value to any parameter, add nothing after the ? character (e.g. /blog/{page?}).
default values can also be inlined in each parameter using the syntax {parameter_name?default_value}. This feature is compatible with inlined requirements, so you can inline both in a single parameter:
/** * @Route("/blog/{page<\d+>?1}", name="blog_list") */ public function list(int $page) { // ... }
New in version 5.1: The priority parameter was introduced in Symfony 5.1
When defining a greedy pattern that matches many routes, this may be at the beginning of your routing collection and prevents any route defined after to be matched.
A priority optional parameter is available in order to let you choose the order of your routes, and it is only available when using annotations.
/** * This route has a greedy pattern and is defined first. * * @Route("/blog/{slug}", name="blog_show") */ public function show(string $slug) { // ... }
/** * This route could not be matched without defining a higher priority than 0. * * @Route("/blog/list", name="blog_list", priority=2) */ public function list() { // ... }
The priority parameter expects an integer value. Routes with higher priority are sorted before routes with lower priority. The default value when it is not defined is 0.
A common routing need is to convert the value stored in some parameter (e.g. an integer acting as the user ID) into another value (e.g. the object that represents the user). This feature is called “param converter” and is only available when using annotations to define routes.
Now, keep the previous route configuration, but change the arguments of the controller action. Instead of string $slug, add BlogPost $post:
/** * @Route("/blog/{slug}", name="blog_show") */ public function show(BlogPost $post) { // $post is the object whose slug matches the routing parameter // ... }
If your controller arguments include type-hints for objects (BlogPost in this case), the “param converter” makes a database request to find the object using the request parameters (slug in this case). If no object is found, Symfony generates a 404 response automatically.
In the defaults option of a route you can optionally define parameters not included in the route configuration. This is useful to pass extra arguments to the controllers of the routes:
/** * @Route("/blog/{page}", name="blog_index", defaults={"page": 1, "title": "Hello world!"}) */ public function index(int $page, string $title) { // ... }
Route parameters can contain any values except the / slash character, because that’s the character used to separate the different parts of the URLs. For example, if the token value in the /share/{token} route contains a / character, this route won’t match.
A possible solution is to change the parameter requirements to be more permissive:
/** * @Route("/share/{token}", name="share", requirements={"token"=".+"}) */ public function share($token) { // ... }
In addition to your own parameters, routes can include any of the following special parameters created by Symfony:
You can include these attributes (except _fragment) both in individual routes and in route imports.
Symfony defines some special attributes with the same name (except for the leading underscore) so you can define them easier:
/** * @Route( * "/articles/{_locale}/search.{_format}", * locale="en", * format="html", * requirements={ * "_locale": "en|fr", * "_format": "html|xml", * } * ) */ public function search() { }
It’s common for a group of routes to share some options (e.g. all routes related to the blog start with /blog) That’s why Symfony includes a feature to share route configuration.
/** * @Route("/blog", requirements={"_locale": "en|es|fr"}, name="blog_") */ class BlogController { /** * @Route("/{_locale}", name="index") */ public function index() { // ... } /** * @Route("/{_locale}/posts/{slug}", name="show") */ public function show(Post $post) { // ... } }
In this example, the route of the index() action will be called blog_index and its URL will be /blog/. The route of the show() action will be called blog_show and its URL will be /blog/{_locale}/posts/{slug}. Both routes will also validate that the _locale parameter matches the regular expression defined in the class annotation.
The Request object created by Symfony stores all the route configuration (such as the name and parameters) in the “request attributes”.
You can get this information in a controller via the Request object:
public function list(Request $request) { // ... $routeName = $request->attributes->get('_route'); $routeParameters = $request->attributes->get('_route_params'); // use this to get all the available attributes (not only routing ones): $allAttributes = $request->attributes->all(); }
In templates, use the Twig global app variable to get the request and its attributes:
{% set route_name = app.request.attributes.get('_route') %} {% set route_parameters = app.request.attributes.get('_route_params') %} {# use this to get all the available attributes (not only routing ones) #} {% set all_attributes = app.request.attributes.all %}
Routing systems are bidirectional: 1) they associate URLs with controllers (as explained in the previous sections); 2) they generate URLs for a given route. Generating URLs from routes allows you to not write the <a href="..."> values manually in your HTML templates. Also, if the URL of some route changes, you only have to update the route configuration and all links will be updated.
To generate a URL, you need to specify the name of the route (e.g. blog_show) and the values of the parameters defined by the route (e.g. slug = my-blog-post).
For that reason each route has an internal name that must be unique in the application. If you don’t set the route name explicitly with the name option, Symfony generates an automatic name based on the controller and action.
If your controller extends from the AbstractController, use the generateUrl() helper:
// generate a URL with no route arguments
$signUpPage = $this->generateUrl('sign_up');
// generate a URL with route arguments
$userProfilePage = $this->generateUrl('user_profile', [ 'username' => $user->getUsername(), ]);
// generated URLs are "absolute paths" by default. Pass a third optional
// argument to generate different URLs (e.g. an "absolute URL")
$signUpPage = $this->generateUrl('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);
// when a route is localized, Symfony uses by default the current request locale
// pass a different '_locale' value if you want to set the locale explicitly
$signUpPageInDutch = $this->generateUrl('sign_up', ['_locale' => 'nl']);
If you pass to the generateUrl() method some parameters that are not part of the route definition, they are included in the generated URL as a query string::
$this->generateUrl('blog', ['page' => 2, 'category' => 'Symfony']);
// the 'blog' route only defines the 'page' parameter; the generated URL is: /blog/2?category=Symfony
If your JavaScript code is included in a Twig template, you can use the path() and url() Twig functions to generate the URLs and store them in JavaScript variables.
The escape() function is needed to escape any non-JavaScript-safe values:
<script> const route = "{{ path('blog_show', {slug: 'my-blog-post'})|escape('js') }}"; </script>
composer create-project symfony/website-skeleton my_project_name
composer require symfonycasts/verify-email-bundle
composer require symfony/google-mailer
composer require symfonycasts/reset-password-bundle
php bin/console make:user
php bin/console make:auth
php bin/console make:registration-form
php bin/console make:reset-password
php bin/console make:migration
# ajouter la version de MariaDB au .env: ?serverVersion=mariadb-10.4.13
php bin/console doctrine:migrations:sync-metadata-storage
php bin/console doctrine:migrations:migrate
.env
Update la ligne:
DATABASE_URL=mysql://DofusElevage:ES6yKA5RS5qg0ljy@127.0.0.1:3306/dofuselevage?serverVersion=mariadb-10.4.13
Décommenter & update la ligne:
MAILER_DSN=smtp://localhost
The process of authorization has two different sides:
Every role must start with ROLE_ (otherwise, things won’t work as expected)
Other than the above rule, a role is just a string and you can invent what you need (e.g. ROLE_PRODUCT_ADMIN).
There are two ways to deny access to something:
The most basic way to secure part of your app is to secure an entire URL pattern in security.yaml. For example, to require ROLE_ADMIN for all URLs that start with /admin, you can:
access_control:
# require ROLE_ADMIN for /admin*
- { path: '^/admin', roles: ROLE_ADMIN }
# or require ROLE_ADMIN or IS_AUTHENTICATED_FULLY for /admin*
- { path: '^/admin', roles: [IS_AUTHENTICATED_FULLY, ROLE_ADMIN] }
# the 'path' value can be any valid regular expression
# (this one will match URLs like /api/post/7298 and /api/comment/528491)
- { path: ^/api/(post|comment)/\d+$, roles: ROLE_USER }
public function adminDashboard()
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
// or add an optional message - seen by developers
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'User tried to access a page without having ROLE_ADMIN');
}
Thanks to the SensioFrameworkExtraBundle, you can also secure your controller using annotations:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
/**
* Require ROLE_ADMIN for *every* controller method in this class.
*
* @IsGranted("ROLE_ADMIN")
*/
class AdminController extends AbstractController
{
/**
* Require ROLE_ADMIN for only this controller method.
*
* @IsGranted("ROLE_ADMIN")
*/
public function adminDashboard()
{
// ...
}
}
{% if is_granted('ROLE_ADMIN') %}
<a href="...">Delete</a>
{% endif %}
If you only want to check if a user is logged in (you don’t care about roles), you have two options. First, if you’ve given every user ROLE_USER, you can just check for that role. Otherwise, you can use a special “attribute” in place of a role:
public function adminDashboard()
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
// ...
}
// GOOD - use of the normal security methods
$hasAccess = $this->isGranted('ROLE_ADMIN');
$this->denyAccessUnlessGranted('ROLE_ADMIN');
A template is the best way to organize and render HTML from inside your application, whether you need to render HTML from a controller or generate the contents of an email. Templates in Symfony are created with Twig: a flexible, fast, and secure template engine.
Twig syntax is based on these three constructs:
- {{ ... }}, used to display the content of a variable or the result of evaluating an expression;
- {% ... %}, used to run some logic, such as a conditional or a loop;
- {# ... #}, used to add comments to the template (unlike HTML comments, these comments are not included in the rendered page).
// the template path is the relative file path from `templates/`
return $this->render('user/notifications.html.twig', [
// this array defines the variables passed to the template,
// where the key is the variable name and the value is the variable value
// (Twig recommends using snake_case variable names: 'foo_bar' instead of 'fooBar')
'user_first_name' => $userFirstName,
'notifications' => $userNotifications,
]);
Symfony recommends the following for template names:
Use snake case for filenames and directories (e.g. blog_posts.twig, admin/default_theme/blog/index.twig, etc.);
Define two extensions for filenames (e.g. index.html.twig or blog_posts.xml.twig) being the first extension (html, xml, etc.) the final format that the template will generate.
Although templates usually generate HTML contents, they can generate any text-based format. That’s why the two-extension convention simplifies the way templates are created and rendered for multiple formats.
Templates are stored by default in the templates/ directory. When a service or controller renders the product/index.html.twig template, they are actually referring to the
Twig provides quick access to complex PHP variables. Consider the following template:
{{ user.name }} added this comment on {{ comment.publishedAt|date }}
The user.name notation means that you want to display some information (name) stored in a variable (user). Is user an array or an object? Is name a property or a method? In Twig this doesn’t matter. When using the foo.bar notation, Twig tries to get the value of the variable in the following order:
If none of the above exists, use null.
This allows to evolve your application code without having to change the template code (you can start with array variables for the application proof of concept, then move to objects with methods, etc.)
Instead of writing the link URLs by hand, use the path() function to generate URLs based on the routing configuration.
/** * @Route("/", name="blog_index") */ public function index() { // ... }
<a href="{{ path('blog_index') }}">Homepage</a>
The path() function generates relative URLs. If you need to generate absolute URLs (for example when rendering templates for emails or RSS feeds), use the url() function, which takes the same arguments as path() (e.g. <a href="{{ url('blog_index') }}"> ... </a>).
{# the image lives at "public/images/logo.png" #}
<img src="{{ asset('images/logo.png') }}" alt="Symfony!"/>
{# the CSS file lives at "public/css/blog.css" #}
<link href="{{ asset('css/blog.css') }}" rel="stylesheet"/>
{# the JS file lives at "public/bundles/acme/js/loader.js" #}
<script src="{{ asset('bundles/acme/js/loader.js') }}"></script>
If you’d like help packaging, versioning and minifying your JavaScript and CSS assets in a modern way, read about Symfony’s Webpack Encore.
If you need absolute URLs for assets, use the absolute_url() Twig function as follows:
<img src="{{ absolute_url(asset('images/logo.png')) }}" alt="Symfony!"/>
<link rel="shortcut icon" href="{{ absolute_url('favicon.png') }}">
Symfony creates a context object that is injected into every Twig template automatically as a variable called app. It provides access to some application information:
Username: {{ app.user.username ?? 'Anonymous user' }}
{% if app.debug %}
<p>Request method: {{ app.request.method }}</p>
<p>Application Environment: {{ app.environment }}</p>
{% endif %}
The app variable (which is an instance of Symfony\Bridge\Twig\AppVariable) gives you access to these variables:
In addition to the global app variable injected by Symfony, you can also inject variables automatically to all Twig templates.
If your controller extends from the AbstractController, use the render() helper:
// src/Controller/ProductController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class ProductController extends AbstractController
{
public function index()
{
// ...
// the `render()` method returns a `Response` object with the
// contents created by the template
return $this->render('product/index.html.twig', [
'category' => '...',
'promotions' => ['...', '...'],
]);
// the `renderView()` method only returns the contents created by the
// template, so you can use those contents later in a `Response` object
$contents = $this->renderView('product/index.html.twig', [
'category' => '...',
'promotions' => ['...', '...'],
]);
return new Response($contents);
}
}
Templates are loaded in the application using a Twig template loader, which also provides a method to check for template existence. First, get the loader:
// in a controller extending from AbstractController
$loader = $this->get('twig')->getLoader();
Then, pass the path of the Twig template to the exists() method of the loader:
if ($loader->exists('theme/layout_responsive.html.twig')) {
// the template exists, do something
// ...
}
First, create a new Twig template called blog/_user_profile.html.twig (the _ prefix is optional, but it’s a convention used to better differentiate between full templates and template fragments).
Then, remove that content from the original blog/index.html.twig template and add the following to include the template fragment:
{# templates/blog/index.html.twig #}
{# ... #}
{{ include('blog/_user_profile.html.twig') }}
You can also pass variables to the included template. This is useful for example to rename variables. Imagine that your template stores the user information in a variable called blog_post.author instead of the user variable that the template fragment expects. Use the following to rename the variable:
{# templates/blog/index.html.twig #}
{# ... #}
{{ include('blog/_user_profile.html.twig', {user: blog_post.author}) }}
You can call to this controller from any template to embed its result:
{# templates/base.html.twig #}
{# ... #}
<div id="sidebar">
{# if the controller is associated with a route, use the path() or url() functions #}
{{ render(path('latest_articles', {max: 3})) }}
{{ render(url('latest_articles', {max: 3})) }}
{# if you don't want to expose the controller with a public URL,
use the controller() function to define the controller to execute #}
{{ render(controller(
'App\\Controller\\BlogController::recentArticles', {max: 3}
)) }}
</div>
Embedding controllers requires making requests to those controllers and rendering some templates as result. This can have a significant impact on the application performance if you embed lots of controllers. If possible, cache the template fragment.
As your application grows you’ll find more and more repeated elements between pages, such as headers, footers, sidebars, etc. Including templates and embedding controllers can help, but when pages share a common structure, it’s better to use inheritance.
Symfony recommends the following three-level template inheritance for medium and complex applications:
If you are rendering a variable that is trusted and contains HTML contents, use the Twig raw filter to disable the output escaping for that variable:
<h1>{{ product.title|raw }}</h1>
# config/packages/twig.yaml
twig:
# ...
paths:
'email/default/templates': 'email'
'backend/templates': 'admin'
Now, if you render the layout.html.twig template, Symfony will render the templates/layout.html.twig file. Use the special syntax @ + namespace to refer to the other namespaced templates (e.g. @email/layout.html.twig and @admin/layout.html.twig).
Symfony provides a session service that is injected in your services and controllers if you type-hint an argument with Symfony\Component\HttpFoundation\Session\SessionInterface:
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class SomeService
{
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
public function someMethod()
{
// stores an attribute in the session for later reuse
$this->session->set('attribute-name', 'attribute-value');
// gets an attribute by name
$foo = $this->session->get('foo');
// the second argument is the value returned when the attribute doesn't exist
$filters = $this->session->get('filters', []);
// ...
}
}
Sessions are automatically started whenever you read, write or even check for the existence of data in the session. This may hurt your application performance because all users will receive a session cookie. In order to prevent that, you must completely avoid accessing the session.
For example, if your templates include some code to display the flash messages, sessions will start even if the user is not logged in and even if you haven’t created any flash messages. To avoid this behavior, add a check before trying to access the flash messages:
{# this check prevents starting a session when there are no flash messages #}
{% if app.request.hasPreviousSession %}
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
{% endif %}