Is it possible in Phalcon to render controller from view with Volt?
Like it is done in Symfony:
{{ render(controller('AcmeArticleBundle:Article:recentArticles', { 'max': 3 })) }}
Purpose: create incapsulated component with complex logic, views, assets and reuse it in multiple places
Better just use some service or pass parameter to view and include some other view etc.
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.
In the past I've used a proprietary framework that kinda followed an Objective-C like View Controller scheme.
Basically I was able to in one controller instantiate another and pass it some values like an array of products and then inject the reference to the controller to my view and render it whenever I wanted by issuing: $controllerReference->render();
This could be useful in many cases, eg. if I had a controller responsible for rendering a catalog, I would just pass it an array with all the items I would like to see and it would take take of pagination and displaying the items by itself...
Example:
At \UI\Homepage\Controller.php (the controller responsible for the homepage):
// Instantiate a ProductDisplay Controller
$controllRef = new \UI\ProductDisplay\Controller();
$controllRef->setProducts( ... ); // Inject the product array
// Load the current view for this controller and pass it a reference for the ProductDisplay controller
$this->loadSelfView(["controllRef" => $controllRef]);
At \UI\Homepage\View.php (the view loaded before):
// some html of the view
$controllRef->render(); // Render the ProductDisplay view here!
// some other html
How should this functionality be accomplished in Laravel? From what I've been reading Laravel tries to avoid this kind of actions, why? What are the workarounds?
Thank you.
Here is how I will do this, it only work if the called controller method return a View object like return view('home');):
// Get the controller class from the IOC container
$myController= \App::make(MyController::class);
// Execute the controller method (which return a View object)
$view = $myController->myControllerMethod($someParams);
// Return the View html content
$html = $view->render();
you can use the Controller.php class which is extended by all other controller to make a generic method in it to:
Get a controller instance
Call the right method with x parameters
Return the rendered content if it's a view class OR throw an exception (for exemple)
In recent versions of Laravel, it's possible to use Blade's #inject directive. It seems its main purpose is to inject services but I successfully injected controller actions.
Here is a snippet on what you have to do:
#inject('productController', 'App\Http\Controllers\ProductController')
{!! $productController->index() !!}
Just remember: since you're calling the controller method directly, and not through the router, you'll have to pass all required params. If that action requires a request instance, you may call it this way:
{!! $productController->index(request()) !!}
Probably the view returned by the called action contains HTML code. It's important to wrap the method call within {!! and !!}. Using regular {{ }} tags will cause the HTML code be escaped by the template engine.
I want to retrieve Cities names from a table in the database and put them as options in a select input (combobox) which is defined in 'layout.html.twig' . All my views extends 'layout.html.twig', so how can I access to cities names in every page?
[Solution]
I'm not able to respond to my topic ,I didn't have much reputation so I edit my topic
I have found the solution, using "embedding controllers"
first I've created an action to retreive all cities names:
public function listCitiesAction(){
// retreiving cities
$entities = $this->getDoctrine()->getRepository("MedAdBundle:City")->findAll();
return $this->render('MedAdBundle:Ville:list_cities.html.twig',
array('entities' => $entities));
}
this action render list_cities.html.twig defined as :
<select class="form-control">
{% for entity in entities %}
<option>{{ entity.name}}</option>
{% endfor %}
</select>
finnaly I edit my layout.html.twig
<div>
{{ render(controller('MedAdBundle:City:listCities'))}}
</div>
In this way I can access to cities combobox in every page in my app ;)
Another nice way would be use render.
This allows you to call a controller out of your layout.html.twig
{{ render(controller("AcmeDemoBundle:Helper:citySelector")) }}
you also can cache the output with ESI.
It's well explained in the cookbook.
http://symfony.com/doc/current/cookbook/templating/global_variables.html
I would go with these steps:
Write a form hosting a symfony entity field configured to work with the Cities table
Define this form as a service in the DIC
Define a twig extension that exposes a function to output the form HTML
Use the twig function in the layout.html.twig that is extended by all the other templates
As an optimization I would look how I could wire Doctrine with some caching system (e.g. memcached) to avoid hitting the database on each page load.
This is where you can find documentation about the entity field: http://symfony.com/doc/current/reference/forms/types/entity.html
Use the Symfony documentation to find how to define a form as a service and how to write your own twig extension.
I used to work with CodeIgniter. Now I am starting to learn Symfony2. I was just wondering, in CodeIgniter I could load a view from another view. Like, I could load menu.php from index.php. This is how I used to do it -
//in index.php
<?php $this->load->view('menu.php'); ?>
Is it possible to do the same thing in Symfony2 and Twig?
There are a few different ways you can do it depending on what you're trying to accomplish.
If you want to render the response of a controller you can do this in your twig template.
{{ render(controller('AcmeArticleBundle:Article:recentArticles', {
'max': 3
})) }}
In the above example, the parameter passed max would be passed as an argument to your controller. Then the controller would be responsible for returning a response that will be inserted into the view where it was called from.
You can also use include to render just the twig template as an embedded view:
{% for article in articles %}
{{ include(
'AcmeArticleBundle:Article:articleDetails.html.twig',
{ 'article': article }
) }}
{% endfor %}
In the above example article would be passed into the context of the twig template articleDetails.html.twig but not to any controller. So this method is ideal for repetitious front-end code that is used in many places such as templates for tables, lists, sidebars, etc.
http://symfony.com/doc/current/book/templating.html#including-other-templates
http://twig.sensiolabs.org/doc/functions/include.html