To build a sidebar that has a lot of dynamic data on it I learned about View composers in Laravel. The problem was that View Composers trigger when the view loads, overriding any data from the controller for variables with the same name. According to the Laravel 5.4 documentation though, I achieve what I want with view creators :
View creators are very similar to view composers; however, they are
executed immediately after the view is instantiated instead of waiting
until the view is about to render. To register a view creator, use the
creator method:
From what I understand, this means if I load variables with the same name in the controller and the creator, controller should override it. However this isn't happening with my code. The view composer:
public function boot()
{
view()->creator('*', function ($view) {
$userCompanies = auth()->user()->company()->get();
$currentCompany = auth()->user()->getCurrentCompany();
$view->with(compact('userCompanies', 'currentCompany'));
});
}
And here is one of the controllers, for example:
public function name()
{
$currentCompany = (object) ['name' => 'creating', 'id' => '0', 'account_balance' => 'N/A'];
return view('companies.name', compact('currentCompany'));
}
the $currentCompany variable in question, for example, always retains the value from the creator rather than being overridden by the one from the controller. Any idea what is wrong here?
After some research, I realized my initial assumption that the data from the view creator can be overwritten by the data from the controller was wrong. The creator still loads after the controller somehow. But I found a simple solution.
For those who want to use View composers/creators for default data that can get overwritten, do an array merge between the controller and composer/creator data at the end of the boot() method of the composer/creator, like so:
$with = array_merge(compact('userCompanies', 'currentCompany', 'currentUser'), $view->getData());
$view->with($with);
Related
I have a controller method that passes model names and class names to a view. These classes are then instantiated in another controller method. In this case I'm using the Laravel Excel package.
public function index()
{
$exports = [
'Model name 1' => TestExport::class,
'Model name 2' => AnotherExport::class
];
return view('export', compact('exports'));
}
public function download(string $collection)
{
return Excel::download(new $collection(), 'Export.xlsx');
}
My view file then redirects to the download controller method with the specific class name.
#foreach($exports as $name => $collection)
Download
#endforeach
Since I'm learning design patterns, and noticed it would break the DRY rule, I didn't want another controller method or every different Excel file that I downloaded.
Is this good practice or can this be done better?
You can make $exports common to both method and not accept class name from the request.
const EXPORTS = [
'export_name_1' => TestExport::class,
'export_name_2' => AnotherExport::class,
];
public function index()
{
return view('export', compact(self::EXPORTS));
}
public function download(string $collection)
{
if (!isset(self::EXPORTS[$collection]) {
return 'error';
}
$className = self::EXPORTS[$collection];
return Excel::download(new $className(), 'Export.xlsx');
}
Never let request manipulation break your code. For the export_name, you can simply use integers or the array simple index.
View
#foreach($exports as $name => $collection)
Download
#endforeach
To me it feels like your solution is mixing view logic with controller logic.
A controller should handle an incoming request, fetch the right data and then form a response.
A view should handle any logic based on the given data in order to render the output page.
In your case the data you pass to the view is not retrieved, it's just a static list which could be kept in the view just as well. A better solution would be to either:
Have a list of routes (not a list of classes) in your blade template and iterate over that instead.
If the list of routes is not that long, hardcode the whole list instead of making it dynamic.
The main reason for doing so would be to prevent a lot of 'magic' code. For instance in your blade template you expect exports to be an array which contains a list of classes which HAVE to be controllers and which HAVE to have a download function otherwise the code breaks.
An example for solution 1 would look something like:
<?php
// You could consider moving $routes to the controller
$routes = [
action('ExportController#download', TestExport::class),
action('ExportController#download', AnotherExport::class),
];
#foreach($routes as $route)
Download
#endforeach
I have 5 controllers and 5 models and they are all related to backend. I can easily output data in the backend but I need to that for the frontend as well. Not all of course but some of them.
For example I have controller called BooksController:
public function getBooks(Request $request)
{
$books = Books::all();
return view('backend.books.show', compact('images'));
}
So this will show it in backend without any problems but what I want is for example to loop through all the books and show their images in welcome.blade.php which doesn't have controller.
And also to pass other parameters to that same view from different controllers.
Is this is possible?
Thank you.
You are having an error because you did not declare the variable $image
public function getBooks(Request $request)
{
$books = Books::all();
$images = array_map(function($book) {
$book->image;
}, $books);
return view('backend.books.show', compact('images'));
}
It sounds like you are potentially caught up on some terminology. In this case, it sounds like backend is referring to your admin-facing interface, and frontend is referring to your user-facing interface.
You also seem to be locked on the idea of controllers. Unless the route is verrrrrry basic, create a controller for it.
Have a controller for your welcome view, for your admin view, basically (with some exceptions) a controller per resource or view is fine.
In this case, you would have one controller for your admin book view, and a seperate controller for your welcome view. Both of which would pull the books out of the db and render them in their own way
I'm learning Laravel, and for my first project I'd like to create my portfolio. However, the first task I have to do is confusing me.
So I created my templates, layout.blade.php and home.blade.php. That makes sense to me, but now how do I tell Laravel, or how do I route to home.blade.php?
I'm looking for an explanation rather then just code. I'm trying to learn.
Actually, a view in MVC application is just a part of the application and it's only for presentation logic, the UI and one doesn't call/load a view directly without the help of another part (controller/function) of the application. Basically, you make a request to a route and that route passes the control over to a controller/function and from there you show/load the view. So it's not a tutorial site and it's also not possible to explain about MVC here, you should read about it and for Laravel, it's best place to understand the basics on it's documentation, well explained with examples, anyways.
In case of Laravel, you should create a controller/class or an anonymous function in your apps/routes.php file and show the view from one of those. Just follow the given instruction step by step.
Using a Class:
To create a route to your Home Controller you should add this code in your app/routes.php
// This will call "showWelcome" method in your "HomeController" class
Route::any('/', array( 'as' => 'home', 'uses' => 'HomeController#showWelcome' ));
Then create the HomeController controller/class (create a file in your controllers folder and save this file using HomeController.php as it's name) then paste the code given below
class HomeController extends BaseController {
public function showWelcome()
{
// whatever you do, do it here
// prepare some data to use in the view (optional)
$data['page_title'] = 'Home Page';
// finally load the view
return View::make('home', $data);
}
}
If you have {{ $title }} in your home.blade.php then it'll print Home Page. So, to use a view you need a controller or an anonymous function and load the view from the controller/function.
Using an anonymous function:
Also, you can use an anonymous function instead of a controller/class to show the view from directly your route, i.e.
Route::any('/', function(){
// return View::make('home');
// or this
$data['page_title'] = 'Home Page'; // (optional)
return View::make('home', $data);
});
Using this approach, whenever you make a request to the home page, Laravel will call the anonymous function given in/as route's callback and from there you show your view.
Make sure to extend the the master/main layout in sub view (home):
Also, remember that, you have following at the first line of your home.blade.php file
#extends('layouts.layout')
It looks confusing, you may rename the main layout (layout.blade.php) to master.blade.php and use following in your home.blade.php instead
#extends('layouts.master')
Read the doc/understand basics:
You should read Laravel's documentation properly, (check templates to understand blade templating) and also read some MVC examples, that may help you too understand the basics of an MVC framework (you may find more by googling) and some good posts about MVC on SO.
Check it routing in Laravel.
You need to use route file and controllers
Create needed function in your Controller file and create a template file for example
class UserController extends BaseController {
/**
* Show the profile for the given user.
*/
public function showProfile($id)
{
$user = User::find($id);
return View::make('user.profile', array('user' => $user));
}
}
you need to create view file views/user/profile.blade.php
View::make('user.profile', array('user' => $user)) == views/user/profile.blade.php
And you should read it http://laravel.com/docs/responses and also this http://laravel.com/docs/quick#creating-a-view
In Laravel, we all pass data to our view in pretty much the same way
$data = array(
'thundercats' => 'Hoooooooooooh!'
);
return View::make('myawesomeview', $data);
But is there some way to add default variables to the view without having to declare it over and over in $data? This would be very helpful for repeating variables such as usernames, PHP logic, and even CSS styles if the site demands it.
Use View Composers
View composers are callbacks or class methods that are called when a
view is created. If you have data that you want bound to a given view
each time that view is created throughout your application, a view
composer can organize that code into a single location. Therefore,
view composers may function like "view models" or "presenters".
Defining A View Composer :
View::composer('profile', function($view)
{
$view->with('count', User::count());
});
Now each time the profile view is created, the count data will be bound to the view. In your case, it could be for id :
View::composer('myawesomeview', function($view)
{
$view->with('id', 'someId');
});
So the $id will be available to your myawesomeview view each time you create the view using :
View::make('myawesomeview', $data);
You may also attach a view composer to multiple views at once:
View::composer(array('profile','dashboard'), function($view)
{
$view->with('count', User::count());
});
If you would rather use a class based composer, which will provide the benefits of being resolved through the application IoC Container, you may do so:
View::composer('profile', 'ProfileComposer');
A view composer class should be defined like so:
class ProfileComposer {
public function compose($view)
{
$view->with('count', User::count());
}
}
Documentation and you can read this article too.
There are couple of ways, so far I have been experiment with some.
1.Use singleton, you can put it in routes.php
App::singleton('blog_tags', function() {
return array(
'Drupal' => 'success',
'Laravel' => 'danger',
'Symfony' => 'dark',
'Wordpress' => 'info'
);
});
2.Use Settings bundle, download here. https://github.com/Phil-F/Setting. You can put this in controller or template.
Setting::set('title', 'Scheduler | Mathnasium');
3.Use View share, pretty much use it in your template
Controller: Views::share('theme_path', 'views/admin/');
Template: <link href="{{ $theme_path }}/assets/bootstrap.min.css"/>
4.My current sample setup, I wrote a construct in HomeController.
public function __construct()
{
// Define a theme namespace folder under public
View::addLocation('../public/views/admin');
View::addNamespace('admin', '../public/views/admin');
View::share('theme_path', 'views/admin/');
// Set default page title
Setting::set('title', 'Scheduler | Mathnasium');
Setting::set('description', 'daily customer scheduler.');
Setting::set('keywords', ['Reservation', 'Planner']);
Setting::set('page-title', '');
}
#enchance, as an alternative to using '*', as mentioned in your comment, perhaps a View::share would help you too. From the Laravel documentation:
You may also share a piece of data across all views:
View::share('name', 'Steve');
Excerpt is from http://laravel.com/docs/responses
Yep there absolutely is a way - see here on view composers.
You can use that to add data to a view or set of views.
Can you tell me how to use a controller for home page because i'm trying to put a model's data in home.ctp (homepage view) with
<?php $this->user->find() ?>but it returns
Notice (8): Undefined property:
View::$user [APP\views\pages\home.ctp,
line 1]
You should check out the cookbook; it has some solid CakePHP tutorials, at http://book.cakephp.org/
You haven't really provided alot of information, but my guess is your Controller uses a model 'User', and you're putting $this->user->find() in your view, when it should be in your controller. In your controller's action you'll want/need to do something like this...
Users_Controller extends AppController {
function index() {
$arrayOfUsers = $this->User->find(...);
$this->set('users', $arrayOfUsers);
}
}
You can then - in your View - access 'users' like so:
pre($users);
... since you used the Controller method set() to send a variable $users to the view.
All you really need to do is create a new controller if that's the direction you want to go. If this is the only statement you have that requires data access, it might be worth faking it in only this method of the PagesController. For example, one of my projects' homepages is 99% static save for a list of featured events. Rather than move everything out to a new controller or even loading my Event model for the entire PagesController (where it's not needed), I just applied this solution in PagesController::home():
$attractions = ClassRegistry::init ( 'Attraction' )->featured ( 10 );
Works great. If your page is more dynamic than mine, though, it may well be worth routing your homepage through a different controller (one that is more closely related to the data being displayed).
The default controller for the home page i.e home.ctp view, is located in /cake/libs/controller/pages_controller.php. This controller can be overriden by placing a copy in the controllers directory of your application as /app/controllers/pages_controller.php. You can then modify that controller as deem fit i.e. use models, inject variables to be used in the home page etc