Laravel :: Routes Vs. Controller - php

As I am new to the laravel 4 after spending some few months in Codeigniter, I went through the lots of tutorials about laravel and one thing I want to be clear is what is the actual difference between Routes and Controller in laravel, because we can create and generate view in both controller and routes too. Will anyone explain me in brief when to use routes and Controller in laravel? Because in other Framework we need routes to specify some particular URL's within the apps and Controller were used to do some real tasks but in laravel I didnt get the main concept of Routes except the routing mechanism?

In Laravel, you can totally skip controllers and do the task of performing business logic and generating the view in the routes.
E.g I have a link b2.com/getUsers so in routes.php I can write:
Route::get('/getUsers',function()
{
$users=User::all(); //select * from users
return View::make('allUsers')->with('users',$users);
}
So, here to serve the request b2.com/getUsers, we didn't use controller at all and you can very well do this for handling all requests in your application, both get and post.
But then, if your application is large and have 500+ url's with complex business logic then imagine putting everything in one routes.php. It will totally make it criminally messy and whole purpose of architecture will be defeated. Hence what we usually do is, reserve routes.php for routing only and write all business logic (along with generation of views inside controllers)
So the same example can be solved as:
To handle link: b2.com/getUsers, in routes.php
Route::get('/getUsers', array('before' => 'auth', 'uses' => 'MyController#getUsers'));
MyController has the method getUsers defined like this:
public function getUsers()
{
$users=User::all(); //select * from users
return View::make('allUsers')->with('users',$users);
}
I usually create a controller for related activities e.g for login/signup/logout. I create AuthController and all the links related to those activities are routed to AuthController through routes.php.

The fact that you can get views or do a lot of things in Routes::any() is against MVC and separation of logic.
In Route::get("admin", function(){}), you indeed have a fast access to your route callback, which otherwise in a standard fashion must just be bound to controller. But Laravel allows you to do your job there in a closure (function(){}), instead of binding it to a controller. Anyway, it lets you, but you'd better avoid it. In Route::get() you only should go with your 'routing' and nothing more.
There is no reason for you to use callbacks in Route unless for testing or some trivial requests. So, better to avoid this:
Route::get("admin", function(){
return View::make("admin_index");
});
And rather go with this:
Route::controller("admin", "AdminController");
And in your AdminController.php :
// I mean create a file named AdminController.php in controllers directory under app.
class AdminController extends Controller
{
function getIndex()
{
return View::make("admin_index");
}
}
Read more about Route::controller and restful controllers.
Some Notes:
Having the ability to add closures in your Routes allows you to make
complex decisions on Routes and have a powerful routing system.
These callbacks let you add conditions to your route.
Having Controller separated from you Routes makes you application
more extensible, less confusing and makes other coders more
comfortable in future.
It allows you to focus better on your problem and finding solution,
this physical separation is very important. Having View::make()
inside your Route stirs all problems into each other and makes up a
confusion for the coder.

Let's see what we have in both cases:
In CodeIgniter, a route is just pointing your request to a specific method of your controller:
$route['blog/joe'] = "blogs/users/34";
Here when you visit application.com/blog/joe, you will invoke the controller BlogsController, call the method users() and pass 34 as the first parameter. Nothing else. As they put it, a route in CI is just a one-to-one relationship between a URL string and its corresponding controller class/method.
Now, in Laravel, you have a lot of possibilities:
You can directly return a simple response
You can return a view
You can point the request to a specific controller and a method
You can write some logic in a closure and then decide what you want to do
You can add some additional functionality to them, like attaching filters, checking parameters upon a regex, give them separate names, etc., but this is the main functionality.
What's the reason for being able to do so much stuff? It gives you the power to use them in any way you need. Examples:
Need a small website, rendering static HTML? Use them like this:
Route::get('/', function()
{
return View::make('greeting');
});
Need a bigger application using the traditional MVC pattern? Use like this:
Route::get('user/{id}', 'UserController#showProfile');
Need a RESTful approach? No problem. This will generate routes for all the CRUD methods:
Route::resource('photo', 'PhotoController');
Need something quick and dirty to handle a specific Ajax request? Keep it simple:
Route::post('foo/bar', function()
{
return 'Hello World';
});
TL;DR: For very simple things without or with very little logic, use them instead of controllers. Otherwise, always stick to the MVC principles and route to your controllers, so that they're the ones who do the actual work.

Related

Should I use function create or Route::view to show blade form

I've been trying to learn Laravel by myself and some features are bugging my mind. I'm trying to display a simple login form with just inputs and a button, so should I use function create and return a view or just declare Route::view on web.php to show a blade form?
OBS: I noticed that if the blade expect any data, I have to go through a controller, but since this won't use it that would not be a problem.
Keep in mind I'm just trying to learning the best and professional way possible.
This is how I would code on the AuthController:
public function create() {
return view('auth.login');
}
VS
And this is how I would declared on web.php:
Route::controller(AuthController::class)->group(function () {
Route::view('/login', 'auth.login')->name('login')->middleware('guest');
}
If your route only needs to return a view, you may use the Route::view method.
So that's dependent on
1-what you do.
2- the purpose of the view.
3-complexity of your app.
4- data that you can pass to the view.
5- the best practice that seniors wrote.
you will capture the best practices by practice, build projects and see what professional engineers do
In your case you should use controller to hanle the authentication
see
https://laravel.com/docs/9.x/routing
https://laravel.com/docs/9.x/views
https://laravel.com/docs/9.x/blade
As Abdallah said, it depends.
You can start this way, if your controller have no other logic than returning the view, delete the controller and simply return the view in your route file:
Route::view('/login', 'auth.login')->name('login')->middleware('guest');
If then you need a parameter, but no other logic than passing it into your view, you can use a function:
Route::get('/login/{someParameter}', function () {
return view('auth.login', compact('someParameter');
})->name('login')->middleware('guest');
Then you might need some more logic, at this point you may want to create a controller to handle it:
Route::get('/login/{someParameter}', [AuthController::class, 'myMethod'])->name('login')->middleware('guest');
Then you might need some extra "guest only" pages as register. You may want to do some grouping:
Route::middleware('guest')->group(function () {
Route::get('/login', [AuthController::class, 'myMethod'])->name('login');
Route::get('/register', [RegisterController::class, 'myMethod'])->name('register');
});
Those are only some example of what you can do with routes.
You can refactor your route file as your project grows and try to learn more function of the routing of Laravel that may or may not help you as the time goes.
I would also suggest to follow the naming convention for the routes. Then you won't have to much trouble refactoring the route file if you don't have to change the name.

How to map shorthanded routes to the same controller in Slim 3

I am working on a REST API project using Slim 3, and I was wondering if there is an easy way to implement the following routing without creating separate routes for the shorthands.
The shorthand is ../me for ../users/{id} where the id is the current users ID.
So far its easy, I just create the two routes, and map them to the same controller method; but there are many more endpoints which use the same logic for example:
../users/{id}/posts should use the same as ../me/posts,
../users/{id}/groups/{gid} should use the as ../me/groups/{gid}, etc.
I used the double dots to indicate that there are preceding URI parts (version, language etc.).
I hope you get the idea now.
So my question is this: is there a way to reroute these kind of requests, or maybe is there a route pattern that would fit my needs and i missed it, maybe even I have to fiddle in a middleware to achieve this?
Thanks
There is a way to take advantage of Slim's FastRoute router. Put a regular expression into the variable part of your route and do the extra parsing inside the controller:
$app->get('/whatever/{id:users/\d+|me}', function ($request, $response, $args) {
if (preg_match('%^users/(\d+)$%', $args['id'], $parsed)) {
// This is /users/{id} route:
$user = $parsed[1];
} else {
// This is /me route:
$user = 'automagically recognized user';
}
return $response->withStatus(200)->write('Hello '.$user);
});
However I'd find that strange and would recommend mapping the same controller to two individual routes, as you do now. Two reasons come to my mind:
You can put the user ID lookup for 'me' route only into the one where it's needed (by having another controller adding this logic on top of the main one).
It's easier to comprehend for other developers on the team.
Hope it helps!
Try it
$app->get('/users[/{id}/groups[/{msgid}]]', function ($request, $response, $args) {
}
and see the oficial documentation in http://www.slimframework.com/docs/objects/router.html

PHP - Use database in MVC view layer (Laravel Blade)

Is it generally a good idea to interact with database in view layer in MVC frameworks?
I'm using Laravel 4.
I want to show some data from database at top of all website's pages.
I have a main.blade.php and: #include("inc.header")
If I should, how can I connect to database from inc/header.php in right way?
I don't want make multiple connections one here at header.php and one in my pages controllers.
I'm more familiar with PDO than Laravel's database methods and ORMs.
Any advice is appreciated!
Edit
Friends gave nice advice and answers about MVC and Laravel workflow, but my main concern is still here.
Ok I have used controllers and models to get required data, then as I said, it should be present for the view layer in every page, So should I repeat the same task to get the same data in All my controllers' actions? (I guess that's why we have Filters here! then again, is it ok to use db in Laravel filters? using a model?)
Thanks in advance :)
Never do anything more than loop thru your data in your view layer. Basically a normal MVC pattern in laravel could be something like this:
It all begins with the routing layer (which btw. is fantastic in laravel)
Using closures
Route::get('/home', function()
{
//Here data is an array, normally you would fetch data
//from your database here and pass it to the View.
$data = array('this', 'is', 'my', 'data-array');
return View::make('my.view')->with(compact('data');
});
Using controllers (and a controller method)
//the same route is bound to a controller method
Route::get('/home','HomeController#myFunction');
The controller for the above could look something like this:
<?php
class HomeController extends BaseController {
//The function you call from your route
public function myFunction()
{
$data = array('this', 'is', 'my', 'data-array');
return View::make('my.view')->with(compact('data');
}
}
The above example just shows the VC in MVC, but generally you pass data from your models in the same way.
Heres a quick one:
Model usage in controllers
public function myFunction($user)
{
$userdata = User::find($user)->orderBy('firstname', 'desc');
$infodata = Event::find(1)->course;
return View::make('my.view')->with(compact('data', 'infodata');
}
So the idea is that laravel lets you do things in multiple ways. If you have a minor app, and are sure that you can manage without controllers you could skip the controller and keep everything in your routing layer.
For most apps however the controllers are needed for controlling the dataflow in the application.
If you are totally new to MVC you should check out some tuts on the subject.
EDIT:
Ahaa! So you wanted to share some piece of data in all your views! Well thats simple. Because all your controllers extend the BaseController you can simply pass in the data in there. Like so:
class BaseController extends Controller {
public function __construct()
{
$data = array('alot', 'of', 'data');
return View::share('data', $data);
}
}
Now the data variable is available in all the views.
PS. Filters are meant to filter stuff, like to check if certain things are "ok". This could include to check authed users, or form submissions etc.
There is another solution that is especially handy for cases like yours. If you have 15 routes that all ultimately include the inc.header view... well you can see where this is going. You'll have data logic repeated in several places. Patrik suggested using the BaseController which is a good solution but I prefer to use a composer.
View::composer('inc.header', function ($view)
{
$view->some_data = MyModel::where(...)->get();
});
It doesn't get much easier then that. :)
Is it generally a good idea to interact with database in view layer in MVC frameworks?
No, you don't interact with your database (model) in your view layer. MVC stands for "Model View Controller" to separate logic, application data and business rules of your application.
If I should, how can I connect to database from inc/header.php in right way?
I don't want make multiple connections one here at header.php and one in my pages controllers.
You could create a main layout (containing your header) and use this layout for all the pages of your application:
app/views/
layouts/main.blade.php
page1.blade.php
layouts/main.blade.php:
<html>
<head>...</head>
<body>
<div>Your header that will be included in each of your pages</div>
<!-- The content of the current page: -->
#yield('body')
</body>
</html>
page1.blade.php:
#extends('layouts.main')
#section('body')
<div>The content of your page</div>
#stop
Learn more about the Blade templating engine.
Now where do you fetch your data?
The fastest/easiest way to develop is to use the routes as Controllers and as your application evolves you start refactoring your code and create Controllers.
app/routes.php:
Route::get('page1', function(){
//Fetch your data the way you prefer: SQL, Fluent or Eloquent (Laravel ORM)
//$data = DB::select('select * from yourtable');
$data = YourModel::all();
//Pass your data to the view
return View::make('page1')
->with('data', $data);
});
The data can be fetch using Fluent class or Eloquent ORM.
I suggest you to learn the basics of Laravel to properly create the base of your application so it will be really easy to maintain in the future.
The answer and issue here is not so simple. First have just been introduced to MVC and trying to understand the basic concept, the flow for MVC is Controller->Model->View. Here we see that the data is passed from the model directly to the View. The current examples of how to use Laravel MVC is to have the Model return data to controller making the flow as follows: Conntroller->Model-Controller->View. Here we see that the model and view are not aware of each other. A qucik look on wikipedia show that this type of modeling and controlling is know has MVA. The A is for adapter and also known as mediating controller, so now we are back to the answer here. Should we be using MVA or MVC? How would one achieve true MVC in Laravel? Answer is to use {{Form:model}} facade for true MVC. But then what happens if the view changes? should we be calling model from the view to get the new data? Answer is no, if the view has to change then user should have caused a controller to react kicking off a new cycle of MVC. So what we can see here a mix of MVC and MVA using the Laravel framework, can achieve a highly customized flow based on the needs of the site.

Implementating Dependency Injection for better routing in mvc or mvvm in php using DICE

I've trying to expand my knowledge on separation of concerns in php. I've been practicing for over a year I think and trying to write my own mvc framework for practice. Now I am stuck again in routing and on how to initiate MVC triad.
I have this uri that I want to map so I can identify which controller and which view to use
$uri = filter_var(rtrim(filter_input(INPUT_GET, 'url', FILTER_SANITIZE_STRING), '/'), FILTER_SANITIZE_URL);
lets say this piece of code resides in my bootstrap.php file that acts as the entry point.
While reading Tom Butler's Blog I realize many things, like views should have access to models, but not quite, using a viewmodel is better, or just a model.
I've come across his IOC or his Dependency Injection Container and fell interested on trying it.
What lacks in that article is the dispatching part, which I am very interested to learn, I've tried a couple things to make it work but with no avail.
I wanted to implement this because I want a single call of controller can have shared dependency across its views, something like
$route = $router->getRoutes(); // maybe something that return a Route object with Controller, and View object that has already shared dependecies.
I do not know if my understanding on the above paragraphs where correct and if really can be of use on my routing. Correct me if I am wrong.
The real question is how would the dispatcher looks like? and if I were to use the convention over configuration stuff of mr. tom, should I declare the routes individually in my bootstrap? Like these
$dice->addRule('$route_user/edit', $rule);
$dice->addRule('$route_user/delete', $rule);
...
I wonder if I could just do:
$controller->method($params)
After I have settled on what view and controller I needed.
I'm not sure that this answer will be what you want, but if I where you I'll do it this way, it will be more restFull, and more "convention over configuration":
Use http verbs for edit , delete...
Use a standard way to handle your urls: "article/", "article/2"
Where article is your controller name AND your view name
Use a simple array tree for simple routes:
$bootstrap=[
...
"routes"=>[
"myFirstCategory"=>[
"art"=>"article",
...
],
"mySecondCategory"=>[
...
This way, myFirstCategory/art can be redirected to article controller and view, with a recursive function that handle the routes tree
In this tree you can use a callback rule for complex rules (that callback have to be handeled by your recursive function for the routes tree:
... "art"=>function($myContainer){...return ['view'=>$view,'controller'=>$controller,'id'=>$id...];}...
It's just some ideas to make it more easy to use...

PHP Laravel extending resource routing

Laravel routing functionality allows you to name a resource and name a controller to go with it. I am new to Laravel and would like to know if anyone knows how to extend the resources method in the route class provided.
Basically say I have: (which works fine)
/invoices
But say I want:
/invoices/status/unpaid
How is this achievable?
To see the basics of what I am doing check:
http://laravel.com/docs/controllers#resource-controllers
Resource controllers tie you into a specific URLs, such as:
GET|POST /invoices
GET|PUT /invoices/{$id}
GET /invoices/create
and so on as documented.
Since, by convention, GET /invoices is used to list all invoices, you may want to add some filtering on that:
/invoices?status=unpaid - which you can then use in code
<?php
class InvoiceController extends BaseController {
public function index()
{
$status = Input::get('status');
// Continue with logic, pagination, etc
}
}
If you don't want to use filtering via a query string, in your case, you may be able to do something like:
// routes.php
Route::group(array('prefix' => 'invoice'), function()
{
Route::get('status/unpaid', 'InvoiceController#filter');
});
Route::resource('invoice', 'InvoiceController');
That might work as the order routes are created matter. The first route that matches will be the one used to fulfill the request.

Categories