Laravel 4 Controller Templating / Blade - Correct method? [closed] - view

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I've been reading through the Laravel 4 documentation and have been making a demo application to help with learning.
I couldn't find much documentation on the templating of views with blade and controllers.
Which is the correct method or does it come down to personal preference?
E.g. 1
Controllers/HomeController.php
protected $layout = 'layouts.main';
public function showWelcome()
{
$this->layout->title = "Page Title";
$this->layout->content = View::make('welcome');
}
Views/layouts/main.blade.php
<html>
<head>
<title>{{ $title }}</title>
</head>
<body>
{{ $content }}
</body>
</html>
Views/welcome.blade.php
<p>Welcome.</p>
E.g. 2
Controllers/HomeController.php
protected $layout = 'layouts.main';
public function showWelcome()
{
$this->layout->content = View::make('welcome');
}
Views/layouts/main.blade.php
<html>
<head>
<title>#yield('title')</title>
</head>
<body>
#yield('content')
</body>
</html>
Views/welcome.blade.php
#section('title', 'Welcome')
#section('content')
// content
#stop
What is the best convention and/or advantages of the the above?

I don't store any layout information in the controller, I store it in the view via
#extends('layouts.master')
When I need to return a view in the controller I use:
return \View::make('examples.foo')->with('foo', $bar);
I prefer this approach as the view determines what layout to use and not the controller - which is subject to re-factoring.

I don't like either of them. Layouts are probably the weirdest part of Laravel. The controller version doesn't really make sense; all methods of the controller then require that view. The #yield version is a mess of boilerplate. I made up this "method specific layouts":
public function index()
{
return View::make('layouts.main', [
'layout_data' => 'sup'
])->nest('content', 'welcome', [
'view_data' => 'sup'
]);
}
I think it should be mentioned in the docs that this is an option.

I prefere the second one, because it shows a more clear separation between your view and controller code. It seems more logical to me that title would be a property of a content view instead of combining your welcome view with your welcome title each time.
In the end both are correct and will work, but the second alternative is more maintainable.

I prefer the 1st method since some sites have a dynamically generated title from the database. It's easy to pass the title in using the first method.

Related

How can i send array of data from laravel blade to controller

I have created a form and I used alpine js to add something to the array data, but I don't know how to pass it to the controller as a post method
It is super important that you read the official Laravel documentation, you are not a magician, so you must get this knowledge from somewhere.
You are not sharing a controller code, so I will assume you have this one:
Route::get('/', function () {
return view('greeting', ['name' => 'James']);
});
If you have this on your view, it will render that name:
<html>
<body>
<h1>Hello, {{ $name }}</h1>
</body>
</html>

Laravel Sub-menu Within View

Hi I am very new to Laravel and MVC frameworks in general and am looking to create a list of links (in a view within a template) that links to some content. I am using this to display a list of nine people and to display their profile description when the link is clicked on. I have created a model of what the page looks like at http://i.imgur.com/8XhI2Ba.png. The portion that I am concerned with is in blue. Is there a way to route these links to something like /about/link2 or /about?link2 while maintaining the same exact page structure but modifying the ‘link content’ section (on the right of the link menu) to show the specific link's content? I would greatly appreciate it if someone could point me in the right direction, as I have literally no clue where to go with this!
There are a couple ways you can go about doing this.
Templates
Create your route.
Im assuming a lot about your app here but hopefully you get the picture. If you need help with anything in particular, be sure to update your question with the code youve tried so it will be easier to help you.
Route::get('about/{page}', function($page)
{
$profile = Profile::where('name', $page)->first();
return View::make('about')->with('profile', $profile);
});
Modify Template.blade.php
Put this line where you wish for About.blade.php to appear.
#yield('content')
Create your view which will extend your template
#extends('Template')
#section('content')
<h2>User Profile</h2>
<ul>
<li>Name: {{ $profile->name }}</li>
<li>Last Updated: {{ $profile->updated_at }}</li>
</ul>
#stop
AJAX
This solution will utilize AJAX to grab the data from the server and output it on the page.
Route for initial page view
Route::get('about', function($page)
{
$profiles = Profile::all();
return View::make('about')->with('profiles', $profiles);
});
Feel free to follow the same templating structure as before but this time we need to add some javascript into the template to handle the AJAX. Will also need to id everything which needs to be dynamically set so we can easily set it with jquery.
#extends('Template')
#section('content')
<h2>Links</h2>
#foreach($profiles as $profile)
{{ $profile->name }}
#endforeach
<h2>User Profile</h2>
<ul>
<li>Name: <span id="profile_name">{{ $profile->name }}</span></li>
<li>Last Updated: <span id="profile_updated_at">{{ $profile->updated_at }}</span></li>
</ul>
<script>
function setProfile(a)
{
$.ajax({
method: 'get',
url: 'getProfile',
dataType: 'json',
data: {
profile: $(a).data('id')
},
success: function(profile) {
$('#profile_name').html(profile.name);
$('#profile_updated_at').html(profile.updated_at);
},
error: function() {
alert('Error loading data.');
}
});
}
</script>
#stop
Route to handle the AJAX request
Route::get('getProfile', function()
{
$profile_id = Input::get('profile');
$profile = Profile::find($profile_id);
return $profile->toJson();
});
Now, the page should not have to reload and only the profile information should be updated.
Making some assumptions here as no code posted and assuming you're using the latest version of Laravel, Laravel 5.
Lets say you have a table in your database named users and you have a Model named Users (Laravel 5 comes with the Users model as default, see app/Users.php). The users will be the base of our data for the links.
Firstly, you want to register a route so you can access the page to view some information. You can do this in the routes file. The routes file can be found here: app/Http/routes.php.
To register a route add the following code:
Route::get('users', ['uses' => 'UserController#index']);
Now what this route does is whenever we hit the URL http://your-app-name/public/users (URL might be different depending on how you have your app set up, i.e. you may not have to include public) in our web browser it will respond by running the index method on the UserController.
To respond to that route you can set up your UserController as so:
<?php namespace App\Http\Controllers;
class UserController extends Controller {
public function index()
{
}
}
Controllers should be stored in app/Http/Controllers/.
Now lets flesh out the index method:
public function index()
{
// grab our users
$users = App\Users::all();
// return a view with the users data
return view('users.index')->with('users');
}
This grabs the users from the database and loads up a view passing the users data.
Here's what your view could look like:
<!DOCTYPE html>
<html>
<head>
<title>Users Page</title>
</head>
<body>
#foreach($users as $user)
<a href="{{ URL::route('user', ['id' => $user->id]) }}">
{{ $user->username }}
</a>
#endforeach
</body>
</html>
The view code will loop through each user from the $users data we passed to the view and create a link to their user page which is different for each user based on their id (their unique identifier in the DB)
Due to the way I've named it, this would be found in app/views/users/index.blade.php - if you save files ending in blade.php you can use Laravel's templating language, blade.
Now you need to finally set up another route to respond to a user page, for example http://your-app-name/public/user/22.
Route::get('user/{id}', ['uses' => 'UserController#show']);
Then add the show method to UserController
public function show($id)
{
// this will dump out the user information
return \App\User::find($id);
}
Hope that helps a little! Wrote most of it off the top of my head so let me know if you get any errors via comment.
This question is very bare, and it is difficult to actually help your situation without you showing any code. Just to point you in the right direction though, here is what you would need.
A Model called People, this is how you will access your data.
A controller. In this controller you will do the following
Get the ID of the profile you want from the functions parameters
Find that persons information e.g. People::find($person_id);
return the profile view with that persons data e.g. return view('profile')->with('person', $person);
In your view you can then use that data on that page e.g. {{ $person->name }}
For the page that needs to display the links to the people you would have a method in your controller which..
Get all the people data e.g. People::all();
Return a view with that data return view('all-people')->with('people', $people);
You will then need a route to access an individual person. The route will need to pass the persons ID into a controller method e.g.
Route::get('get-person/{id}',
[ 'as' => 'get-person',
'uses' => 'PeopleController#getPerson' ]);
You can then use this route in your view to get the links to each person
#foreach($people as $person)
{{$person->name}}
#endforeach
This would produce the list of links you want.

Laravel 4 content yielded before layout

I am using a fresh build today of Laravel 4.
I have a dashboardController
class DashboardController extends BaseController {
protected $layout = 'layouts.dashboard';
public function index()
{
$this->layout->content = View::make('dashboard.default');
}
}
I have a simple route
Route::get('/', 'DashboardController#index');
I have a blade layout in views/layouts/dashboard.blade.php
For the sake of saving everyone from all of the actual HTML ill use a mock up.
<html>
<head>
<title></title>
</head>
<body>
#yield('content')
</body>
</html>
I have a default blade file in views/dashboard/ that has the following (edited for simplicity)
#section('content')
<p>This is not rocket science</p>
#stop
For some reason the content gets generated before the layout.
I am using a different approach to set the layouts globally to routes using a custom filter. Put the following filter into the app/filters.php
Route::filter('theme', function($route, $request, $response, $layout='layouts.default')
{
// Redirects have no content and errors should handle their own layout.
if ($response->getStatusCode() > 300) return;
//get original view object
$view = $response->getOriginalContent();
//we will render the view nested to the layout
$content = View::make($layout)->nest('_content',$view->getName(), $view->getData())->render();
$response->setContent($content);
});
and now instead of setting layout property in the controller class, you can group the routes and apply the filter as shown below.
Route::group(array('after' => 'theme:layouts.dashboard'), function()
{
Route::get('/admin', 'DashboardController#getIndex');
Route::get('/admin/dashboard', function(){ return View::make('dashboard.default'); });
});
When creating the views, make sure to use the #section('sectionName') in all the sub views and use #yield('sectionName') in the layout views.
I find it easier to do my layout like this for example. I would create my master blade file like so
<html>
<body>
#yield('content');
</body>
</html
And in the blade files that I want to use the master at the top i would put
#extends('master')
then content like so
#section('content')
// content
#stop
Hope this helps.
When you use controller layouts, i.e. $this->layout->..., then you get access to data as variables, not sections. So to access content in your layout you should use...
<html>
<head>
<title></title>
</head>
<body>
<?php echo $content; ?>
</body>
</html>
And in your partial, you would not use #section or #stop...
<p>This is not rocket science</p>

Laravel 4 blade templates causing FatalErrorException?

I'm getting an unexplained FatalErrorException when trying to implement a simple page layout using blade templating. I'm not sure if it's something I'm doing wrong or Laravel is. I'm following the tutorial on L4's documentation about Templating and my code seems to follow it. Here's my code.
app/routes.php:
<?php
Route::get('/', 'HomeController#showWelcome');
app/views/home/welcome.blade.php:
#extends('layouts.default')
#section('content')
<h1>Hello World!</h1>
#stop
app/views/layouts/default.blade.php:
<!doctype html>
<html>
<head>
<title>The Big Bad Barn (2013)</title>
</head>
<body>
<div>
#yield('content')
</div>
</body>
</html>
app/controllers/HomeController.php:
<?php
class HomeController extends BaseController {
protected $layout = 'layouts.default';
public function showWelcome()
{
$this->layout->content = View::make('home.welcome');
}
}
Laravel just throws a FatalErrorException. The output error page says "syntax error, unexpected '?'". The file blade is generating inside the storage/views directory has PHP where the
<?php echo $__env->make('layouts.default')
<?php $__env->startSection('content', array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>; ?>
<h1>Hello World!</h1>
<?php $__env->stopSection(); ?>
Yesterday I encountered the same problem you did, however the other answers didn't fix my problem. You've probably figured it out already, but maybe this post prevents others from spending as much time as I did figuring out what is wrong while it's such a small (but frustrating!) thing.
I'm using Notepad++ as text-editor and for some strange reason it had decided to use "MAC format" as the End-Of-Line (EOL) format. Apparently the Blade framework can't cope with that. Use the conversion function (in notepad++ : Edit -> EOL Conversion) to convert to Windows Format and it will work just fine..
Your controller should be just returning View::make("home.welcome") instead of attaching it to the layout.
The welcome view then calls the layout so the controller is only concerned about the body template in this case.
Edit for showing example controller:
class HomeController extends BaseController {
public function showWelcome()
{
return View::make('home.welcome');
}
}
Eric already gave the right answer, I just wanted to add that if you want to use
protected $layout = 'layouts.default';
in your HomeController, then leave showWelcome action intact and remove this line
#extends('layouts.default')
from welcome.blade.php file. That should work too.
Regards,
Vlad

Laravel 4: if statement in blade layout works strange

Could someone explain me why I get blank screen with printed string "#extends('layouts.default')" if I request page normally (not ajax)?
#if(!Request::ajax())
#extends('layouts.default')
#section('content')
#endif
Test
#if(!Request::ajax())
#stop
#endif
I'm trying to solve problem with Ajax, I don't want to create 2 templates for each request type and also I do want to use blade templates, so using controller layouts doesn't work for me. How can I do it in blade template? I was looking at this Laravel: how to render only one section of a template?
By the way. If I request it with ajax it works like it should.
Yes #extends has to be on line 1.
And I found solution for PJAX. At the beginning I was not sure this could solve my problem but it did. Don't know why I was afraid to lose blade functionality if you actually can't lose it this way. If someone is using PJAX and needs to use one template with and without layout this could be your solution:
protected $layout = 'layouts.default';
public function index()
{
if(Request::header('X-PJAX'))
{
return $view = View::make('home.index')
->with('title', 'index');
}
else
{
$this->layout->title = 'index';
$this->layout->content = View::make('home.index');
}
}
Try moving #extends to line 1 and you will see the blade template will render properly.
As for solving the ajax problem, I think it's better if you move the logic back to your controller.
Example:
…
if ( Request::ajax() )
{
return Response::eloquent($books);
} else {
return View::make('book.index')->with('books', $books);
}
…
Take a look at this thread for more info: http://forums.laravel.io/viewtopic.php?id=2508
You can still run your condition short handed in the fist line like so
#extends((Request::ajax())?"layout1":"layout2")

Resources