I'm new to Symfony2 and I've been thinking of the best way to generate navigation bar HTML that is used on every page request - especially with regards to caching.
So imagine if every page request shows the logged in user and a number indicating how many messages are unread (in fact like stackoverflow for that). I'm guessing that could be generated in every controller to ensure the info is up-to-date (using a function or something of course) - but I'm also looking at caching the whole controller output, and think it might be good to keep this dynamic part separate.
Would creating a controller extension for all this kind of stuff be a good way to go? So this way the controller only deals with that specific function (e.g. getting blog posts from a DB etc.) and the controller extension adds all the dynamic content. That way I can cache the controller result and speed up pages without caching the full page (which can't really be done due to lots of dynamic HTML content).
Something like this maybe:
class ControllerExtension extends Controller
{
public function render($view, array $parameters = array(), Response $response = null)
{
//get number of messages for this user
$parameters['messages'] =
//are they logged in
$parameters['logged_in'] =
// render as normal
return parent::render($view, $parameters, $response);
}
}
For this I want to ignore use of JS. I know some of these things could be populated with JS, but I would prefer not for this.
You can solve this by caching the navbar fragment separably from the page html with ESI or Hinclude and can be simply and elegantly solved with Symfony2.
Embed a controller inside a template
You can render a controller inside a template:
<div id="sidebar">
{% render url('latest_articles', { 'max': 3 }) %}
</div>
This will render the controller with the route "latest_articles", inside your html.
This can be done in you controller template, or in the global layout template (where you define the header, footer, js, css ecc of all your pages, see Template Inheritance)
Cache the embedded fragment separately from the page html:
You can use a reverse proxy (like Varnish, or the AppCache), to cache the two part of the html separately:
<div id="sidebar">
{% render url('latest_articles', { 'max': 3 }, {'standalone': true}) %}
</div>
That's it, just add {'standalone': true}
You'll need an external program on front of your web server (like Varnish, or Nginx with a mod), but this is the fastest way.
Load the fragment with javascript:
You can also tell symfony to asynchronously load the fragment in javascript:
<div id="sidebar">
{% render url('latest_articles', { 'max': 3 }, {'standalone': 'js'}) %}
</div>
This is a nice approach, since you can cache the entire html in a CDN (for example with Amazon CDN CloudFront), and still show user specific content.
For info see: http://symfony.com/doc/2.1/book/templating.html#asynchronous-content-with-hinclude-js
Related
Have recently started to re-develop an application with CodeIgniter and I am trying to implement my template into the system.
I am facing an issue that I can't yet see a way around
Basically, at the minute within the controller an example of what I am doing is:
/*
* Load view for header
*/
$this->load->view('header_view');
/*
* Load view for navigation
*/
$this->load->view('navigation_view');
/*
* Load view for content
*/
$this->load->view('content_view');
/*
* Load view for footer
*/
$this->load->view('footer_view');
However the problem I am facing is that the content area has pages dynamically loaded in to it.
So the navigation area links don't open up the HREF URL but they load that page data in to the content div via ajax.
I need to be able to go to my controllers directly via the URL such as 'http://www.website.co.uk/controller/method_name/parameters' which at the minute is fine.
The navigation however will load that link in to the content area which also includes the header/navigation/footer so I end up with nested layouts in the content area...
The simple way around it is 'well don't include the header/navigation/footer views in every page' however then you can't directly go to the URL such as above, it would only work when loaded in to the content area as it would need to have the header, navigation and footer.
The solution that I ideally need is that the header/navigation/footer are ALWAYS loaded around any controller so that if I go to a controller/method via the URL I don't have to load the header/navigation/footer in the controller but it is automatically loaded around it.
Any ideas appreciated, thanks
Why not extend the CI controller with a MY_Controller, then all your controllers will extend MY_Controller. In my_Controller have a function like:
public function loadView($file, $data=array()){
$this->load->view('header', $data);
$this->load->view($file, $data);
$this->load->view('footer', $data);
}
Then, from your controllers actions, just call it :
public function index(){
// code code code
$this->loadView('welcome', array('message'=>'Hello!'));
}
I think you get the point.
in ajax you can use views like this:
<div id="header"></div>
<div> //here all your stuffs</div>
</div id="footer"></div>
then just :
<script>
$(function(){
$('#footer').load('my_footer_view_url');
$('#header').load('my_header_view_url');
});
</script>
this is a poor example, you can define both a php method which serves the views and a js method which loads them into divs, so each view then must have only a <div> where to load partials
Important
if you can use Ajax, this solution is great, cause once you move the #footer div somewhere else in your views you moves an enteire footer view for example, php will be more static anyway, but it depends, i sad, if you can or not use ajax ;)
I have Yii template that structure is:
Include CSS files in
echo $content right after
Include Javascript files (like JQPlot) after $content
What I would like to do is add custom mix of Javascript/PHP code after including all Javascript files. I know this can be done just adding the code to the template.
But I have many pages with custom JS/PHP code and I would like to include only the specific code for that page to avoid long load times.
Can I create new element that works like $content, but it would include for example "js.php" from the same View folder as "index.php"? The "js.php" would be in every view folder I need custom code and contain the js/php mix of code for that specific page.
Thanks for the help!
Clips are your best bet, as #jfrej suggests by pointing you to a forum topic where clips are discussed.
Under conventional circumstances, every controller has associated a folder where all its views are placed, and it is the case that you want to include at the end of the layout (this is how they are refered, rather than templates) some PHP+Javascript content that is common to all the actions in the controller.
I would override CController::afterRender() method to capture the content for you clip; lets call your clip controller_content:
afterRender(string $view, string &$output)
$this->beginClip('controller_content');
// output here any content you want to capture into your clip
// e.g. renderPartial, echo, etc.
...
$this->endClip();
parent::afterRender($view, $output);
}
Then, in your layout, you would render your clip after all the stuff that is always there, e.g.:
// register your CSSs
// output $content variable
// register Javascript
...
<?= $this->clips['controller_content'] ?>
Of course, there are various ways for you to generate your clip content. Here I overrode the CController::afterRender() method, but you could also use filters, behaviors or any other approach that best suits your needs.
You can try :
Yii::app()->clientScript->registerCssFile(Yii::app()->baseUrl.'/css/example.css');
Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl.'/css/example.js');
You can call it from controller
I want to try create something like Zend's Server Pagecache. What I want to achieve is to store page output, so I guess it would be direct html, but with possibility to insert some dynamic data to page.
Output caching is a large subject. Do do it right, you need to think a bit on the design.
Here are 2 ways. The code sample is just for explanation, it's not a working solution.
Block cache and dynamic composition. Probably the best way. Divide your page into several bloks. Each block should be generated by separate function/class. You can use Zend_Cache_Frontend_* objects to cache those blocks. Once Your application know what to display, in the controller You can compose the output using cached blocks and the dynamic parts.
class CachedController extends Zend_Action_Controller
{
public function indexAction()
{
$this->_view->leftBlock = $this->leftBlock();
$this->_view->rightBlock = $this->rightBlock();
}
protected function leftBlock()
{
// prepare left block, can use Zend_View if you like
// use Zend_Cache to cache the block
}
protected function rightBlock()
{
// prepare left block, can use Zend_View if you like
// use Zend_Cache to cache the block
}
}
/* VIEW SCRIPT */
<html>
<body>
<div class="left">
Left cached block here
<?php echo $this->leftBlock; ?>
</div>
<div class="main">
Do Your dynamic part here
</div>
<div class="right">
Right cached block here
<?php echo $this->rightBlock; ?>
</div>
</body>
</html>
Whole page cache with substring substitution. If you do not wish do divide the page into blocks, You can cache whole page (Also using Zend_Cache_Frontend_*), and then use PHP str functions to substitute or insert the dynamic part. You will need to capture output of the View rather then having it sent by the framework automagically (refer to docs on how to change this).
You can always load dynamic data with ajax.
example:
if user logged and going to vote or something.
I'm working with a CakePHP application and jQuery Mobile. In the CakePHP application, RequestHandler is turned on, now, jQuery Mobile makes all of it's requests as ajax requests but requires a full page not just what is in my view but the full layout to.
I need requesthandler and I've tried to set a layout, force a render, turn off autolayout, nothing works, cake only returns the contents of my view.
I'd really love to know what's going on here.
If anyone is interested I found a solution to this, I found out that when you have RequestHandler on and make a Ajax request, it doesn't matter what you do, RequestHandler then decides that your layout is 'ajax' via call backs, this probably applies for all non-html request types, like json and text.
I had to set
$this->RequestHandler->enabled = false;
It really needs to be set in beforeFilter() as well, latter in the call chain and it appears to not work.
So my code ended up as:
class AppController extends Controller {
var $components = array('RequestHandler');
function beforeFilter() {
if ($this->RequestHandler->isMobile()) {
$this->RequestHandler->enabled = false
//set special mobile rules here
}
}
}
I was having the same problem with a CakePHP 1.3 app that is using jQueryMobile to build mobile-friendly views. I'll try to lay it out for future searches.
When I switched on $.mobile.ajaxEnabled = true; for jQM's nice Ajax-based navigation all the linked pages showed undefined instead of the page content. The Ajax nav requires that the linked page have the proper structure, like so:
<div data-role="page">
<div data-role="header">
<h1>Page Title</h1>
</div><!-- /header -->
<div data-role="content">
<p>Page content goes here.</p>
</div><!-- /content -->
<div data-role="footer">
<h4>Page Footer</h4>
</div><!-- /footer -->
</div><!-- /page -->
This markup was coming from two places for my app, though: my default mobile layout had the <html>, <body> and the page and footer divs. Each view had a header and content div.
The problem arose because Cake's RequestHandler wanted to set the layout to ajax for all Ajax requests. It does this somewhere after beforeFilter(), which caused it to ignore a declared layout there (which Luke mentions).
The ajax layout is empty, unlike my default layout--it doesn't have <html> and other markup because it assumes (rightly) that you only want partial markup that will be inserted into a page by Ajax. The jQuery Ajax-based navigation does want to see the full markup though, and when it didn't receive a well-formed page when making the Ajax request it had no page to display.
So the question became, "How do I make RequestHandler use my default mobile layout for mobile page requests?" I wasn't satisfied with Luke's solution above of disabling RequestHandler completely for mobile. Luckily I found a post from May '11 with the same problem and a better solution:
Finally sorted it! I was checking to see whether a mobile device was
requesting the page using $this->RequestHandler->isMobile(), but had
placed it in the beforeFilter() function. Moving this to the
beforeRender() function in the app_controller.php file fixed the
problem.
http://cakephp.1045679.n5.nabble.com/Jquery-Mobile-and-CakePHP-1-3-td4422871.html
So my code ended up something like this in AppController:
function beforeRender() {
if ($this->RequestHandler->isMobile()) {
$this->layout = 'm_default';
}
}
I hope that helps someone out there.
Just set $this->layout = 'default'; in your controller, and it will use the default layout.
Or maybe you could make a header and footer element to put in your ajax and default layouts.
I was new to CakePHP and started with Version 2 several weeks ago.
So far I also keep the beforeFilter untouched to identifier isMobile() and finally use mobile-views within a Themed-Folder. Therefore I dont use subfolders for mobile-views within the default desktop-views. After adding this->layout in the condition it seems that I got rid off the undefined which appeared only via some actions links.
public function beforeRender() {
if($this->RequestHandler->isMobile()) {
$this->theme = 'Mobile';
$this->layout = 'default';
}
}
The best sollution I think is to configure the request handler in before filter after the mobile browser was detected in your app controller:
public function beforeRender() {
if($this->RequestHandler->isMobile()) {
$this->theme = 'Mobile';//set your theme
$this->RequestHandler->ajaxLayout = 'public';//this does the trick, set your mobile layout, $this->layout is simply ignored or overwritten by the component
}
}
Whenever I start a new project I find myself remaking and rethinking my self made template library. I use some influences from dom manipulation, but don't want to make too much functions so that it still loads fast.
This is how my current template system looks like:
This is a layout file:
<body>
<div id="content">
<block:content>This is a default text</block:content>
</div>
<div id="sidebar">
<widget:advertisement type="wide" />
<block:sidebar this_param="is_passed_on" />
</div>
</body>
As you can see I made 2 sort of "extra" tags that will be replaced when eventually publishing the template. I load this layout like this:
$this->template->load("layout");
I then can manipulate the block tags like this:
$this->template->content = "I'm overwriting the default text";
$this->template->content->prepend("I forgot something");
$this->template->sidebar->view("viewfile_1", array(/*data*/));
$this->template->sidebar->view("viewfile_2", array(/*data*/));
I can set text manually, I can load multiple views into 1 block, I can use a few dom-like manipulating functions like prepend, append, ...
I can even extend the template with more layout options like:
$this->template->content->extend("2columns");
This layout file might look like:
<div><block:left/></div>
<div><block:right/></div>
So that instead of the content block I now have an extra left and right block to put content in.
I have also created a widget tag that loads the specific widget class (/widgets/advertisement in this case). The optional parameters added in the tags are passed on to the views files and/or widget display function together with the direct passed data array.
In short, this is how my system now works. I haven't really found other systems like this to get inspiration from. Could you guys give me advice on anything so I can put together one decent system that I can keep using?
My approach is:
Create main layouts for each page type on a layouts/ folder (think Wordpress layouts for home, archive, single post, single page)
Create common bits of interface in a common/ folder (think header, footer, sidebar, widget_XX, widget_YY)
Use Phil Sturgeon's Template Library (or Spark!) to handle the views.
On each controller I load all the data needed for rendering in $this->data and I pass that object to the view
Hope this helps! Good luck.