in Symfony 5, i want to display entities Categories in my navbar for each page of my website. Actually i return the same data in each function in my controller like
return $this->render('post/index.html.twig', ['Categories' => $Categories]);
I want to know if there is a better way to return always my categories instead of always retrieve data in each function and return the same data ?
If your navbar is the same on all pages, instead of including the navbar template like {% include 'base/navbar.html.twig' %}, you can include a Controller that will pull the Categories from the database and render the template. It's explained here : https://symfony.com/doc/current/templates.html#embedding-controllers.
In your template :
{{ render(controller('App\\Controller\\BaseController::navbar')) }}
In BaseController
public function navbar(CategoriesRepository $categoriesRepository) {
return $this->render('base/navbar.html.twig', [
'categories' => $categoriesRepository->findAll(),
]);
}
You don't need to add annotations, because there's no route. It's simply a function that returns html.
You need to use global variables. For example in config/packages/twig.yaml:
twig:
globals:
varKey: varValue
or to create own Twig Extension
Related
I'm making a presonification menu on my navbar so that i can access from any page i don't want to use the getRepository inside a controller and pass to the frontend.
Is there a way on symfony 4 that I can get all the users from my database and call it to front? like the app.session.user (to get info on the logged user) for example?
Thanks!
As Cerad mentioned, you can embed a Controller in your templates : https://symfony.com/doc/current/templates.html#embedding-controllers
Instead of doing {% include 'base/navbar.html.twig' %} you will do
In your template :
{{ render(controller('App\\Controller\\BaseController::renderNavbar')) }}
In BaseController
public function renderNavbar(UserRepository $userRepository) {
return $this->render('base/navbar.html.twig', [
'activeUsers' => $userRepository->findActiveUsers(),
]);
}
It's not a route, it's simply a function that renders html, so you don't need to add annotations.
I'm starting a project using Phalcon framework with Volt as a template engine. I have some experience with Symfony/Twig.
I read documentation and tried searching all over the internet but can't find a satisfying way to accomplish what I want (I find ugly the solution described here: How do I create common template with header and footer for phalcon projects with Volt Engine; it's not using Volt per se for the navigation.)
So the story is pretty easy: my base template consists of 4 parts: navigation, header, content and footer. I use partials to include the common areas in the base template like the navigation, header and the footer, works fine with "static data".
Now the question is: how do I get to dynamically generate the navigation menu with items coming from the database? The template will have common areas that have to come from DB also in the header, footer and a sidebar. Having to fetch that in all Controller actions sounds like overkill and not very DRY (maybe do it on the init part? but will have to be done in every controller. Maybe in an abstract controller, I dunno.)
What is the best way to accomplish this in Phalcon/Volt? In Symfony/Twig you can call from the view a controller action, so you can have like a LayoutController that renders partials from a page.
Thanks!
Here are few variants:
1) Your controllers can extend a BaseController and in it's initialize() method you can assign those variables to the view.
class BaseController extends \Phalcon\Mvc\Controller
{
public function initialize()
{
// Common Variables
$this->view->assetsSuffix = $this->config->debug ? '' : '.min';
}
2) Create a custom Volt function which loads the data.
// In your Volt service:
$compiler->addFunction('getMenu', function($resolvedArgs, $exprArgs){
return 'Helpers\CommonFunctions::getMenu(' . $resolvedArgs . ')';
})
// Helper file and function
public static function getMenu()
{
return \Models\Menu::find();
}
// Volt usage
{% set menuItems = getMenu() %}
{% for item in menuItems %}
{% endfor %}
3) Use the models to query the DB directly from the template. However this is not yet supported with Volt (not sure if it is added in latest version, have to confirm).
<?php $menuItems = \Models\Menu::find(); ?>
{% for item in menuItems %}
{% endfor %}
4) Ajax/Javascript, but this is really dependant on your application. Something like Angular approach, but I will not get into details with this varaiant.
Here is the use case:
I have a simple navbar with a simple dropdown menu.
I have a list of cities in the Database with the name of the cities. I want to get all the cities and add them to the dropdown menu.
Simple way is to send additional data, like array of cities to twig. But i have a lot of routes and i don't think that this is good to repeat fetching every time. This is not good, right?
public function testpageAction(){
$em = $this->getDoctrine()->getEntityManager();
$cities = $em->getRepository('AppBundle:City')->findAll();
return $this->render('appviews/testpage.html.twig',array('cities'=>$cities));
}
There is a method to inject variable into all templates, but this is static data, so it is not the solution.
There is a plan in my head to make a method that will be run before controller methods and it will send the data to those methods. But it does not seem good and I believe there is a better way to solve it.
You can have a CityController or a BaseController with method getAllCities, and embed this controller in your base template.
From Symfony docs :
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.
Something like this:
class BaseController extends Controller
{
public function getAllCities()
{
// make a database call or other logic
// to get all cities
$cities = ...;
return $this->render(
'cities.html.twig',
array('cities' => $cities)
);
}
}
Your cities template:
{# app/Resources/views/cities.html.twig #}
{% for city in cities %}
{{ city}}
{% endfor %}
And in your base template:
{# app/Resources/views/base.html.twig #}
<div id="sidebar">
{{ render(controller(
'AppBundle:Base:getAllCities'
)) }}
</div>
I'm writing a website using Slim Framework and Twig.
The sidebar on the website will have dynamically created links and i want to get the links from the database/cache every time a page is rendered, i can do this in each controller action but i would like to do this in my base template so i don't have to get and render the data manually in every controller action.
I have looked through the twig documentation and I haven't seen anything that could be of use besides the include function/tag but i don't see how i could get the data easily.
Is my only option to write an twig extension to do this or is there an easier way?
First of all: templates usually shouldn't contain any type of bussines logic but only render the data you provide it.
However you could write a custom twig function and use that to aquire the menu data from the DB in order to render it in your base template.
Alternativeley you could write some Slim middleware that aquires the data which might be able to inject the data into the template.
Hope that helps.
After reading Anticoms answer i was able to do it by writing a Twig extension.
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('render', array($this, 'render'))
);
}
public function render($template, $controller, $action) {
$app = Slim::getInstance();
return $app->$controller->$action($template);
}
Now i can simply write
{{ render('Shared/sidebar.twig', 'Controller', 'Index') }}
Into my twig template to render the template with the data i want.
Note that my render() function uses slim dependency injection to instanciate the controller at runtime.
I have setup a general layout for all my views - all fine.
Now, I would like to get some information from database to put in the header, which is part of the layout.
At the moment, the only way I can find out is something like:
// OneControler.php
static public function hello()
{
$data['hey'] = 'heeey';
return View::make('layouts.partial.nav', $data);
}
And from inside the layout:
// master.blade.php
...
{{ OneController::hello() }}
...
This works fine but... I think there must be another way? I don't think loading a controller from inside the view/layout is the best way to do this?
You can use View Composers to help you with this:
View::composer(array('your.first.view','your.second.view'), function($view)
{
$view->with('count', User::count());
});
Then in your view your.first.view or your.second.view, or even your layout you can just:
{{ $count }}
In the array of views, you put the name of the view:
View::composer(array('layouts.partial.nav') ...
or you can just set it to all views:
View::composer(array('*') ...
If you want to define a variable for all views you can also use share:
View::share('name', 'Steve');
In your view or layout:
{{ $name }}