I come from the symfony (v1) world, where we had two types of partial rendering: partials, which loaded the same as:
#include('some.view')
and controllers, which would act the exact same way but would be ran with some controller logic behind hit. So, calling the same as above would first go to the matching some.view controller, and operate with the logic it had.
I'm trying to do the same with Laravel. Basically, I have this:
#foreach($array as $thing)
#include('controller.like.view', array('thing' => $thing))
#endforeach
... and I'd like my included view to run something like this (this is just an example, the actual code is a lot more complicated, otherwise I'd just write it with an if clause in Blade):
...
if ($thing%2) {
return 'a';
}
return 'b';
... so that only 'a' or 'b' would be printed in my loop. What's the best way to achieve this without having a bunch of PHP code in a Blade template?
Why not just like that?
#foreach($array as $thing)
#if($thing%2)
a
#else
b
#endif
#endforeach
In general though, it's mostly a good way to prepare the data in your controller before passing it to the view. This way the view is just for presenting the data.
You could also write a little helper function or even a full class (with optional Facade for easy access) But it really depends on your needs
Update
I'm not sure this is the best solution for you but it's the only one I can think of.
Put as much of the logic as you can in your "thing" class and then use that in the included view. Here's an example:
class Thing {
public function isA(){
// do the magic
return true;
}
}
View
#if($this->isA())
a
#else
b
#endif
Update 2
Or to make a bit more like the controller from symfony you described:
class Thing {
public function getVars(){
// do stuff
return array(
'all' => 'the',
'vars' => 'you',
'need' => 'in',
'the' => 'view'
);
}
}
And then when you include the view
#include('item', $thing->getVars())
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 want to use templating in a Codeigniter-4 project.
If my controller looked like this
class Blog extends \CodeIgniter\Controller
{
public function index()
{
$data = [
'todo_list' => ['Clean House', 'Call Mom', 'Run Errands'],
'title' => "My Real Title",
'heading' => "My Real Heading"
];
echo view('blogview', $data);
}
}
I want to use a templating system which works as below:
// Create new Plates instance
$templates = League\Plates\Engine::create('/path/to/templates');
// Render a template with the given data
echo $templates->render('profile', ['name' => 'Jonathan']);
My question is - what is the best place to instantiate that $templates object?
I can repeat it in every method which I know for sure is bad practice.
.... OR ...
I could do it in the __contstructor() and assign to $this->templates and I do so for every controller where I want to use that templating. I feel like there is still a better way.
I have little experience of Laravel where I don't specifically set this $templates variable to use Blade templates instead it simply calls View(). I want to achieve something like that, if possible. I tagged laravel so that anyone with more experience in Laravel may have better understanding on how to achieve it?
What would you suggest?
PS: I am using composer autoload to load all the files.
I'm afraid I am really only a CI3 user but have read a little about CI4 and there is still a base controller you can use. You should take a look at https://codeigniter4.github.io/userguide/extending/basecontroller.html?highlight=basecontroller. You would then make your $this->templates available through all extended controllers.
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);
I am trying to return values from a MySQL database into a datatable using Chumper. I am trying to obtain values from 2 tables; but I am having what I believe to be a routing issue as I do not know how to include 2 controller functions to the same view ie in my search view I want to render the result of 2 functions like : for a deeper understanding of what I am trying to accomplish see question : Laravel Chumper Datatable getting data into one datatable from multiple MySQL tables
Route::get('search', array('as' => 'instance.search', 'uses' => 'CRUDController#instances'));
Route::get('search', array('as' => 'accid.search', 'uses' => 'CRUDController#account_ids'));
any ideas ?
Only a single route will ever match any given URL requested of the system. I believe what Laravel will do is choose the second one (as in the second definition will overwrite the first).
You have some options here. All I can really tell from your question is that you want two methods to be executed when that route gets hit. This is indirectly possible, consider:
Route::get('search', 'MyController#instances');
class MyController extends Controller
{
public function instances()
{
$mydata = $this->account_ids();
$myotherdata = $this->getOtherData();
return View::make('myview')
->with('mydata', $mydata)
->with('myotherdata', $myotherdata);
}
private function getOtherData() { /* ... */ }
}
This isn't really clean, though, and eventually will lead to convoluted controller logic which is an anti-pattern in MVC. Fortunately, Laravel let's you use View Composers, which can greatly clean up your controller logic:
public function instances()
{
return View::make('myview');
}
Wow. Nice and simple. Now the view composer part:
// Inside of a service provider...
View::composer('search', 'App\Http\ViewComposers\AViewComposer');
use View;
class AViewComposer
{
public function compose(View $view)
{
$view->with('instances', $this->instances());
$view->with('accountIds', $this->accountIds());
}
public function instances()
{
// generate your instance data here and return it...
return $instances;
}
public function accountIds()
{
// generate your account id data here and return it...
return $accountIds;
}
}
You could take this a step further and inject another class into the constructor of this view composer to completely off-load the responsibility of determining what 'instances' and 'account ids' actually mean, should you need that same functionality elsewhere. This will help you keep your code extremely DRY.
You are having those 2 routes but with same domain so the second one is running over the second one.
Or you change the URL of on of them or you change the method for post instead of get
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