How does Laravel call a method in a controller via Route? - laravel

I cannot understand how Routing works. I tried to read Illuminate\Routing\Route but cannot grasp, how Laravel calls a method in a controller.
Lets say we have:
Route::get('/', 'WelcomeController#index');
I found out that you could call it like this
Route::get('/', function () {
App::call('App\Http\Controllers\WelcomeController#index, []);
});
But I cannot read anything near that in the Route.php file.

With out going into all the details, somethings to look at:
Router#dispatch -> dispatchRoute
Router#runRoute
Router#runWithinStack
Route#run
Route#runController
ControllerDispatcher#dispatch
Route#runCallable
Illuminate\Routing\Router Illuminate\Routing\Route Illuminate\Routing\ControllerDispatcher
This will lead you from dispatching to the actual call on the controller itself.

If you see App\Providers\RouteServiceProvider you can find protected $namespace = 'App\Http\Controllers'; and
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
Which means all your web.php routes, for example Route::get('/', 'WelcomeController#index'); visit https:://your-domain/ send action in App\Http\Controllers\WelcomeController method index

It's a bit of a beast but in a nutshell you have:
index.php will capture the request and send it through the Http Kernel.
Http Kernel will try and dispatch the Request to the Router.
The Router will then find the applicable Route (if there is one), get all of the middleware for the Route, pipe the Request through them and finally run the Route.
This should then return a Response which ultimately gets returned in the index.php file which will send it on.
NB
"Running" the route will get the controller method or closure, resolve any dependencies and then call the closure/method.

Related

Route is not defined Inertia.js

In my controller, I have a method to redirect to a route:
return Redirect::route('/management');
This is my routes/web.php:
Route::get('/management', function () {
return Inertia::render('Management');
});
However, it throws an error saying Route [management] not defined.
The Redirect::route() method expects a named route, which you have not defined in your routes/web.php file.
In order to use a named route, you need to change your route to:
Route::get('/management', function () {
return Inertia::render('Management');
})->name('management'); // added
After that, you can redirect to named routes in Inertia like so:
return Redirect::route('management');
Since we're using named routes, and not URLs, you should not include the leading / in your route() call.

Customise the "method is not supported for this route" error handling in Laravel

Route::post('order', 'OrderController#store')->name('order');
When I browse to the URL http://127.0.0.1:8000/order it shows the error:
The GET method is not supported for this route. Supported methods: POST.
Which is the correct.
But I want to redirect user to home page instead of showing this error.
First of all note that what you are trying to do seems like an anti-pattern for Laravel. Accessing a route with the wrong method should be denied!
I currently do not know about altering the default way of handling the wrong method error and I wouldn't advise to do that. But you can work around it:
Patching
Method 1
Keep your routes file clean but alter the original route line and add some lines to the beggining of the controller method
Route::match(['get', 'post'], 'order', [OrderController::class, 'store'])->name('order');
public function store(Request $request)
{
if ($request->isMethod('get')) {
return to_route('home');
}
// ...
Method 2
Keep your controller clean, but add a line to your routes file
Route::get('order', fn () => to_route('home'));

Laravel router problem when I use Route::any

I would appreciate any help you could give me with the following problem I have.
I am trying to implement a Single Page Application (SPA) with Laravel and Vue, at the moment I am defining the routes, I have something like the following:
routes/app.php
Route::group(['middleware' => 'web'], function () {
Route::any('{path}', function () {
return view('index');
});
});
What I'm trying to do here is the following:
For the middleware web you will only have one view available, in this case in the file index in (resources/views/index.php)
I want that regardless of the method used, Laravel returns the view I want. But I have not succeeded.
Output of php artisan route:list
And when I try to verify with Postman I receive a 404 in response regardless of the method or the URL that I requested .. what could I be doing wrong? Thank you very much in advance.
EDIT 1
I have modified my router and added a conditional as follows:
Route::group(['middleware' => 'web'], function () {
Route::any('{path}', function () {
return view('index');
})->where('path', '.*');
});
Now the GET and HEAD method work properly, however ... when I try the POST method, I get the following error ..
EDIT 2
As mentioned #lagbox in the comments below, error 419 refers to missing CSRF token. I could disable this check in the file Middleware/VerifyCsrfToken.php.
However I wish I could just return the desired view .. without first checking the CSRF token. Another detail that I find is the following:
When in Postman I make a request by a method different from the typical methods ... let's say UNLINK method, I get the following result:
Which leaves me a bit confused, when I define in my routes file web.php Route::any, does it mean any route or not?

Creating a route to custom function in controller in Laravel

I still can't understand why I can't point my blade to the custom function I made in my controller. I create a route like this,
Route::get('/orders/storeInitialItems', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');
and in my controller I have this,
public function storeInitialItems()
{
return view('orders.storeInitialItems');
}
but when I run the page, storeInitialItems.blade.php, the error seems calling the show() function of my controller.
Why is that happening?
update
Complete routes for ORDERS
Route::get('/orders','OrdersController#index')->name('orders.index');
Route::get('/orders/create', 'OrdersController#create')->name('orders.create');
Route::post('/orders', 'OrdersController#store')->name('orders.store');
Route::get('/orders/{order}/edit', 'OrdersController#edit')->name('orders.edit');
Route::post('/orders/{order}', 'OrdersController#update')->name('orders.update');
Route::delete('/orders/{order}', 'OrdersController#destroy')->name('orders.delete');
Route::resource('orders', 'OrdersController');
Route::put('orders/{order}/pub', 'OrdersController#publish')->name('orders.publish');
Route::put('orders/{order}/cancel', 'OrdersController#cancel')->name('orders.cancel');
Route::put('orders/{order}/delivered', 'OrdersController#delivered')->name('orders.delivered');
Route::get('/orders/storeInitialItems', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');
Route::get('/orders/{order}/delivery', 'OrdersController#viewdeliveryItems')->name('orders.delivery');
Route::get('/orders/acceptDelivery', 'OrdersController#acceptDelivery')->name('orders.acceptDelivery');
Add your orders.storeInitialItems route
Route::get('/orders/storeInitialItems', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');
before,
Route::resource('orders', 'OrdersController');
or add some extra path with your storeInitialItems
Route::get('/orders/storeInitialItems/add-some-extra-path', 'OrdersController#storeInitialItems')->name('orders.storeInitialItems');

Use Policies with apiResource Routes

Currently I'm writing a Laravel 5.6 REST api. Now I want to secure my endpoints:
Each user in my application has a role. Based on that the user should be able to access some endpoints and otherwise should get a 403 error. For this I would like to use Policies because, when used as middleware, they can authorize actions before the incoming request even reaches my route or controller.
I declare my endpoints like this:
Route::apiResource('me', 'UserController');
My problem now is that if I want to use Policies as middleware I have to specify the (HTTP) method like this middleware('can:update,post'). How should I do this when I use apiResource in my route declaration?
BTW: Currently I have written a FormRequest for each method (which is a pain) and do the authorization there. Can I simply return true in the authorize method after switching to Policies middleware?
Since you are using FormRequest::class to validate the request data, it is best practice to first check is the user is authorized to make the request. For Laravel 5.6 the cleanest solution would be to specify each policy manually in the __construct() method of your resource controller.
public function __construct()
{
$this->middleware('can:viewAny,App\Post')->only('index');
$this->middleware('can:create,App\Post')->only('store');
$this->middleware('can:view,post')->only('show');
$this->middleware('can:update,post')->only('update');
$this->middleware('can:delete,post')->only('delete');
}
If your were validating form data inside your controller instead of using FormRequest::class, a cleaner solution would be to also authorize the user inside the controller.
public function store(Request $request)
{
$this->authorize('create', Post::class);
// The user is authorized to make this request...
$request->validate([
//Validation Rules
});
// The form data has been successfully validated...
// Controller logic...
}
Since Laravel 5.7 you can do all of this using one line of code on your controller's __construct() method.
public function __construct()
{
$this->authorizeResource(Post::class, 'post');
}
You can define route groups, routes that have a common behaviour (middleware, prefix etc. ).
The following should work:
Route::middleware('can:update,post')->group(function () {
Route::apiResource('me', 'UserController');
//more routes
});
You can prefix routes as well:
Route::middleware('can:update,post')->group(function () {
Route::prefix('users')->group(function () {
Route::apiResource('me', 'UserController'); //Translated to ex: /users/me
Route::prefix('books')->group(function () {
Route::apiResource('{book}', 'UserController'); //Translated to ex: /users/me/book_1
});
});
});
P.S: I haven't used resources before but it should do the job

Resources