I want to set a layout like main.htm for all of my twig files in yii 2, but it loads the folder that controller call first, so I can not call my layout with extends.
in SiteController
public function actionIndex()
{
return $this->render('index.htm');
}
This code calls views/site/index.htm
In my index.htm
{% extends "main.html" %}
So if I put the main.html in site folder, there is no problem, but I want to put it in the layout folder and can be called every controller views .. How can I do that ??
views
->site
->index.html
->layouts
->main.html
For example in symfony we can call like this
{% extends '::base.html.twig' %}
But in Yii2 I can not found a way to do this ....
I have done this with hardcoded the file viewRenderer.php. This is not the way I like it because a composer update the changes are gone away...
Now the function init is like this the loader is null here. I have added loader for my layouts.
$this->twig = new \Twig_Environment(null, array_merge([
'cache' => Yii::getAlias($this->cachePath),
'charset' => Yii::$app->charset,
], $this->options));
After adding a loader ..
$this->loader = new \Twig_Loader_Filesystem($_SERVER["DOCUMENT_ROOT"].'\views\layouts');
And last thing is render function in ViewRenderer.php
public function render($view, $file, $params)
{
$this->twig->addGlobal('this', $view);
//$this->twig->setLoader(new TwigSimpleFileLoader(dirname($file)));
$this->loader->addPath(dirname($file));
return $this->twig->render(pathinfo($file, PATHINFO_BASENAME), $params);
}
I want it without hardcoded changes in the core file of yii2-twig ....
Have you tried using a full path here {% extends "main.html" %}. Something like {% extends "\layouts\main.html" %}
Or even {% extends "#app/views/layouts/main.html" %}
I have not used twig with yii, but it is worth a shot.
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.
I'm using Laravel basic authentication but I am really struggling to use the Auth object in the views.
There are good examples using Blade, but I am using Twig (twig bridge) and cannot solve this.
I can vardump(Auth::user()->name) in my controller in regular php but how do I get the same in the twig file (my view file)?
How do I do something like;
{% if auth.guest %}
Or;
{{ Auth.user().name }}
I've tried so many different ways but just get nothing.
This has to do with the way twig works, accessing static variables /classes or static methods on classes is impossible without adding a function to the Twig Environment.
You can register a function like so:
$twig = new Twig_Environment($loader);
$twig->addFunction('staticCall', new Twig_Function_Function('staticCall'));
function staticCall($class, $function, $args = array())
{
if (class_exists($class) && method_exists($class, $function))
return call_user_func_array(array($class, $function), $args);
return null;
}
Then i think you should be able to do:
{% if staticCall('Auth', 'guest') %}
Hi there stranger, why don't you login?
{% endif %}
I took the code from this answer: twig template engine, using a static function or variable
I'm new to Symfony2 and after several tutorials decided to migrate on of my projects to this framework.
In my project I've got few modules that got different templates but share the same data. For example, menu: menu items are lifted from database and used by standart website menu and also by footer menu. I've read that the best practice is to create controller for such task, and embed it straight into main layout, like this:
//Controller
class PageComponentController extends Controller
{
public function menuAction()
{
//get menu items from database...
$menu_items = ...
return $this->render('MyProjectBundle:PageComponent:menu.html.twig', [
'menu_items' => $menu_items
]);
}
}
//Template
{% block body %}
{% render controller('MyProjectBundle:PageComponent:menu', { 'current_page': current_page }) %}
{% endblock %}
But then I need to pass those $menu_items in footer as well, and render footer from the footerAction() in PageComponentController.
I know that there are ways to do it, but what is the best way to share such mutual data between different embedded controllers in Symfony2?
UPDATE (solution based on oligan's answer):
Service container scope is both accessible from main controller (responsible for page rendering) and from embedded controllers. So it would be pretty clean and dry to write service class with getMenu() method that fetches data from DB, and outputMenu() method that returns existing data. Then, service data is set from main controller and could be retrieved from service container in embedded controllers.
I think the wisest is to use a service which would retrieve all the data you want http://symfony.com/doc/current/book/service_container.html (it's more or less like a controller but accessible everywhere )
To give you an idea what it is
class MenuService
{
public function getMyMenu()
{
... your code to get the menu
}
}
Then declare it in services.yml in your bundle
services:
getMenuService:
class: ..\...\...\MenuService
And then just use it in any controller by doing so
$menu = $this->container->get('getMenuService');
if you have to use template heritage then you can still access to an object in the parent template by doing
{% extends "...Bundle..:myMenu.html.twig" %}
{% set menu = myMenuTwigvar %}
and then myMenu for example menu.button1
I'm trying to render an action which is in a sub-namespaced controller from within a Twig view in Symfony 2.
The problem is: The render helper cannot find the controller/action because it's in a namespace below Controller.
This is my controller:
src/Acme/Bundle/TestBundle/Controller/FirstModule/ExampleController.php
namespace Acme\Bundle\TestBundle\Controller\FirstModule;
class ExampleController extends Controller
{
public function exampleAction(Request $request)
{
return $this->render('AcmeTestBundle:FirstModuleExample:example.html.twig');
}
public function embedAction(Request $request)
{
return $this->render('AcmeTestBundle:FirstModuleExample:embed.html.twig');
}
}
This is my view:
src/Acme/Bundle/TestBundle/Resources/views/FirstModuleExample/example.html.twig
{% render "AcmeTestBundle:Example:embed" %}
// or
{% render "Acme\Bundle\TestBundle\Controller\FirstModule\Example:example" %}
// or
{% render "Acme\Bundle\TestBundle\Controller\FirstModule\ExampleController:exampleAction" %}
I have read the Embedding Controllers documentation but no clue there how to handle Controllers that are in a sub-namespace.
Thanks.
Either of these should work. Remember, backslashes in strings need to be escaped (i.e., doubled)
{% render "AcmeTestBundle:FirstModule\\Example:embed" %}
or
{% render "Acme\\Bundle\\TestBundle\\Controller\\FirstModule\\ExampleController::embedAction" %}
Have you tried this?
{% render "AcmeTestBundle:FirstModule/Example:embed" %}
or this?
{% render "AcmeTestBundle:FirstModule\\Example:embed" %}
I think you should be able to use backslash-notation but I haven't tried it since I practice to put all controllers into single namespace (which is bad if you have a lot of them).
Something like this:
{% render "Acme\Bundle\TestBundle\Controller\FirstModule\Example:example" %}