How to load a view from a view? - php

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

Related

Phalcon / Volt dynamically build/render common template areas (partials)

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.

Phalcon - render controller from view

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.

How to send repeating data from all symfony's controller routes to twig?

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>

Inherit dynamic template in Phalcon Volt

I need to load a page, that will be "inserted" in a template - as I read it, Volt's Template Inheritance should do the trick and it does... kinda. Hardcoded values, as shown in the examples, work fine - the following example works:
<!-- Template -->
<div id="site_content">
{% block test %}
{% endblock %}
</div>
and the page, that inherits the template:
{% extends "../../templates/de/index.volt" %}
{% block test %}
{{ content() }} {# this is a registered volt function that outputs the generated content #}
{% endblock %}
However, the same page might need to inherit a different template and that must be decided on runtime, so the name of the template must be generated dynamically. Two options occurred to me:
Set the template name to a variable and use it when extending - the problem here is that I don't see a way to use it afterwards. That guy seems to have had the same problem, but there is neither an answer of how to do it, nor a confirmation that it isn't possible at all.
Register another function to generate the complete string (e.g. {% extends "../../templates/de/index.volt" %}) and then compile it, e.g.
$compiler->addFunction('get_template',
function ($resolvedArgs, $exprArgs) use ($volt) {
return $volt->getCompiler()
->compileString('{% extends "../../templates/de/index.volt" %}');
});
and then use that function in the page, e.g.
{{ get_template() }}
{% block test %}
{{ content() }}
{% endblock %}
However, using that approach does not parse the page content (e.g. the content returned by the registered content() function is not shown). I'm also open to other solutions (using Twig instead of Volt is only a last resort, for performance issues), advices of what I'm doing wrong or pointers of useful articles on the topic. Thanks in advance!
Try using partials as documented in the Phalcon doc: Using Partials

twig: pass variables from view to controller

Setup:
Twig 1.13.1
PHP 5.4.3
Problem:
I have 10,000 articles in my DB. I need a way to only allow X amount of stories to display on the page. I know I can limit the number in the controller before i call the template but that number will be different depending on the template that is used. I will have one controller to handles all the articles. I need a way to pass the number from the template to the controller to limit the array. I don't want to pull down all 10,000 articles then use twig "slice" filter/func.
I know in django you can use the below. That will only load the top 3 stories.
{% get_latest_stories 3 sports as story_list %}
{% for story in story_list %}
{{ story.title }}
{% endfor %}
Here is my current files.
Controller
<?php
$stories = news_stories::getStories("sports",5); //getStories(section,limit);
?>
<?=$twig->render("storyList.html", array('stories' => $stories))?>
View/Template
{% for story in story_list %}
{{ story.title }}
{% endfor %}
Summary
I would like a way to pass a number from the template to the controller so that i can limit the about of rows returned from the DB
Logically speaking, it would be impossible for the view to pass something to controller since the view is being processed at the end of the stack, after everything else.
You can however, pass a function into the view. You would want to create some sort of getViewStories function that you can access from your twig template. Since you have this already in your controller:
<?php
$stories = news_stories::getStories("sports",5); //getStories(section,limit);
?>
<?=$twig->render("storyList.html", array('stories' => $stories))?>
All you would need to do is change it around a bit, like this:
<?php
$function = new Twig_SimpleFunction('getViewStories', function (section, limit) {
return news_stories::getStories(section,limit);
});
$twig->addFunction($function);
?>
<?=$twig->render("storyList.html")?>
Now, from inside your template, you can call this function, like so:
{% set story_list = getViewStories('sports',5) %}
{% for story in story_list %}
{{ story.title }}
{% endfor %}
And change the getViewStories parameters around in each template.
And while you can use the slice filter, I would recommend against it in your case, as it makes for unnecessarily long database calls. This is the most optimized method (that I'm aware of).
You want to use the slice filter I think this should work for you
http://twig.sensiolabs.org/doc/filters/slice.html
{% for story in story_list|slice(1,5) %}
{{ story.title }}
{% endfor %}
should only return the elements 1 - > 5 of the loop then break loop. You can also do it like this
{% for story in story_list|[start:5] %}
{{ story.title }}
{% endfor %}
Disclaimer: I've never actually used twig though this was just a quick browse through its docs
You can embedded controllers ( or render other urls ) from inside a twig template. This means you could have a main layout template for your site, and keep your storyList.html template very plain - just to iterate over the stories and any markup they might need.
In your main layout you would render the action for the news stories:
<div id="stories">
{% render url('...') with { section: 'sports', limit: 5}, {'standalone': 'js'} %}
</div>
This way requires hindclude.js included on your page. Check these docs. If you are also using symfony ( you mention MVC but not the framework ) - even better. Scroll up a bit and look at Embedded controllers.
Otherwise, I believe this way essentially uses ajax.

Categories