Nest templates inside of templates in Laravel 4? - php

I was wondering if it is possible to nest templates inside of templates in Laravel 4. I would like to have a master wrapper template that would include things like the doctype, header and footer and then be able to load in templates into the master templates body section.
This would give me the flexibility of having a nested template for my application pages without having to duplicate code while giving me the ability to use the master template for non-application pages.
Can someone please provide an example as to how this would be done using blades templating engine? Is it possible to pass in a value from the router and then have that value be pushed down to your nested template?
EDIT:
Here is my code for index.blade.php
#extends('layouts.master')
#section('title')
Some page
#endsection
#section('content')
#include('layouts.app')
#endsection

You can try this for multiple level nesting
//index.blade.php
#extends('layouts.master')
#section('title')
#parent
:: new title
#stop
#section('content')
<p>some static contents here</p>
#stop
//app.blade.php
#section('content')
#parent
<p>Add here what ever you want to add</p>
#stop
now either from your Route or Controller you can nest the index and app, ex-
return View::make('index')->nest('content','layouts.app');
And in case you want to pass data to child view you can do this by passing the data as third parameter to nest()
return View::make('index')->nest('content','layouts.app',$data);

Related

Laravel view stored in subfolder can not extend layout template stored

I am trying to extend a layout stored in the layout folder into a subfolder. When i extend the layout into a file directly under the view folder it extends as usual, but when i try extending it into a subfolder under view. It just gives me the raw extend text. So in the page it will show as "#extend ('layouts.pickAlbum')" instead of the layout it self.
#extends('layouts.pickAlbum')
#section('sidebar')
#parent
#endsection
#section('content')
<h1>homePhotos page</h1>
#endsection
thanks forgot to add .blade into the file name, brombeer solved it.

Not having to extend and section in every file Laravel/Blade

Is it possible to make every view extend my main layout without having to put #extends('layout') and #section('content') in every file at the top? For example by using a provider? I'm new to Laravel and Blade.
Actually its better to use extend but in case you want to get rid of #extends or #section, you can always return your main layout from controller and pass views as parameters to the main layout.
mainlayout.blade.php:
//replace yield directive with content variable
// #yield('content')
{!!$content!!}
in controller:
//return view('test');
$content=view('test')->render();
return view('mainlayout',['content'=>$content]);
Now in test view you don't need #extends and #section.

Laravel Blade: Something similar to Jinja2 block

Is there an existing way in Blade to mimic Jinja2's block construct?
I understand it is possible to use #yield('content', '<h1>Default content</h1>'), but this is clunky and ugly if you want the default content to be much bigger than a small string.
Yes, you can use #section/#show instead of #yield:
#section('content')
default content
#show
and override as usual:
#section('content')
override
#stop

Bind data to specific blade templates in Laravel 5.1 (Issue with view composer being called twice on nested template)

I am trying to implement themes in a site so that certain pages have a different look and feel (and features) than other pages.
I have master.blade.php which contains the full HTML structure and has #section and #yield to get the content:
<html>
<body>
#section('master-template')
<?php
view()->share('masterTemplate', 'general');
$masterTemplate = "general";
?>
#stop
<header>
#section('nav-1')
#include('layout.navs.house-links')
#show
#section('nav-2')
#include ('layout.navs.contact-links')
#show
#section('nav-3')
#include('layout.navs.product-links')
#show
#section('nav-4')
#show
</header>
#yield('content')
</body>
</html>
I then have ford.blade.php which extends master, and overwrites some things:
#extends('templates.master')
#section('master-template')
<?php
view()->share('masterTemplate', 'ford');
$masterTemplate = "ford";
?>
#overwrite
#section('nav-1')
#include('layout.navs.house-links')
#overwrite
#section('nav-2')
#include ('layout.navs.contact-links')
#overwrite
#section('nav-3')
#include('layout.navs.product-links')
#overwrite
Then a regular page on the website will extend master.blade, and anything that requires the Ford navigation bar and colour scheme will extend the ford.blade template.
I then bound a view composer to layout.navs.product-links which gets the $masterTemplate variable from the view data and checks if it is one of our themes (like "ford"). If it is a regular page, it will bind vehicle ranges for all manufacturers to the view, and if it is a ford page, it will only bind the ford ranges to the page. The pseudo-code of the view composer is below:
public function compose(View $view)
{
// Get the view data
$viewData = $view->getData();
// Check for $masterTemplate view variable
if (isset($viewData['masterTemplate'])) {
$template = $viewData['masterTemplate'];
if ($template == "ford") {
$ranges = $this->getRangesForManufacturer($template);
}else {
$ranges = $this->getRangesForAllManufacturers();
}
$view->with('ranges', $ranges);
}
}
The problem is, because ford.blade extends master.blade, BOTH views get rendered behind the scenes. So the above view composer logic is useless because it runs twice on the "ford" page, once for master.blade and then again for ford.blade.
I proved this by checking the queries that are running using Laravel DebugBar.
How can I bind a variable $masterTemplate for use in all views (and partials) to check logic inside a view whether it is a "ford" or "general" page? I did it with #section and #overwrite in ford.blade but this seems to not work everywhere.
How can I use a view composer to bind data to a partial view that is #included by ford.blade and master.blade, because ford.blade extends master.blade and so the view composer gets executed twice.
At first glance, #overwrite might do what you told it to (except for #stop being enough, see how to extend a layout).
As a workaround: How about a redesign where you have a meta-master-template which contains the general stuff and is extended by both the ford and the master (i.e. general) template, depending on which part you need? (You could then rename master to general and meta-master to master)
That would skip your problem, because only one would be defined in each instance.

Override section in a laravel blade template throwing undefined variable errors

I am using Laravel 4 and blade templates, and running into an issue when I try and extend a template.
In my layout I have
#yield('content')
and in my page I have
#section('content')
Welcome {{ $name }}
#stop
which works fine, I've created another page very similar to my first, and just want to change override the admin content section. The other sections in the template are fine.
so I extend my page, and do
#section('content')
Hello!
#stop
I get an undefined notice with the $name variable.
I tried
#section('content')
Hello!
#overwrite
and same deal, I get the notice error.
I checked my controller and it IS using the correct template. I am not calling #parent so I don't understand, how can I overwrite a section in a template with out notice errors?
Blade layouts work their way up the tree to the route or master view before rendering any of the children. Thus, nested views that extend others must always have their parent rendered before they are. As a result, parent views that contain sections will always be rendered prior to the child.
To overcome the problem you are experiencing it is a good idea to only ever nest pages that don't overwrite parents sections that contain variables, as the parents content will always be rendered before the child.
As the above ideal can't always be adhered to or a parents section content is still required, a good alternative method would be to use view composers. View composers give you an opportunity to declare variables for any specific view whenever they are rendered.
View::composer(array('pages.admin'), function($view)
{
$view->with('name', Auth::user()->username);
});
Another alternative would be to use a view creator. Creators are fired the moment a view is instantiated rather than rendered. This method allows you to overwrite the variable should you so wish prior to the view being rendered.
View::creator(array('pages.admin'), function($view)
{
$view->with('name', Auth::user()->username);
});
You can read up more about these methods in the documentation here. (Or here for the Laravel 5 documentation.)
I can't guarantee support for Laravel 4 but for those looking for a solution that works in Laravel 5.5 (and probably a fair bit further back – hard to check) is to define the variables you need when #extending.
E.g. in the example in the question:
#extend('my.parent.view', ['name' => ''])
This approach can be especially useful if the data needed is available to the child-view, but under a different name.
E.g. if a parent-view needed a $parent variable, but the child view only has a $child variable which has a property referencing the parent, you might do:
#extend('my.parent.view', ['parent' => $child->parent])
I am not sure if this is a bug or intentional, but it seems like Laravel renders the variables before interpreting blade instructions.
The workaround would be this:
views/layouts/testlayout.blade.php:
<html>
<body>
#yield('sidebar', 'This is the master sidebar. {{ $name }}' )
<div class="container">
#yield('content')
</div>
</body>
</html>
actual view: views/test.blade.php
#extends('layouts.testlayout')
#section('sidebar')
No variable in the child
#stop
#section('content')
This is a child content
#stop
This prevents the variable $name to get rendered if there is a section with that name in the actual view. It seems like this is the approach if the content in the layout file contains a variable
In your page try this :
#section('content')
#hasSection('content')
#yield('content')
#else
Welcome {{ $name }}
#endif
#stop
And read this post for more inspiration https://laracasts.com/discuss/channels/laravel/laravel-blade-layouts-inheritence-problem
I found an easy solution for this problem :)
Just add the # symbol before the variable/method you call in the parent view. If there is an error (notice/warning) PHP will ignore that and continue the execution.
It's not perfect, but save us to code view/composers.
Hope this help!

Categories