Setting the route parameter in the middleware in Laravel - laravel

I am developing a Laravel application. I am doing route model binding in the middleware.
I have the routes like this
Route::group([ 'prefix' => 'department/{department}', 'middleware' => [ 'auth.department' ] ], function () {
Route::post('employee/create', 'EmployeeController#store')->name('employees.store');
});
This is my auth.department middleware (AuthDepartment)
class AuthDepartment
{
public function handle($request, Closure $next)
{
$department = Department::find($request->department);
//do something with the department
//I want to set the $department (Department model) in the place of {department} in the route.
return $next($request);
}
}
This is EmployeeController
class EmployeeController extends Controller {
public function store($department)
{
}
}
As you can see in the code, I am using $department parameter to get the department id from the route. But instead of getting the integer as the parameter, I want to bind the model like this.
class EmployeeController extends Controller {
public function store(Department $department)
{
}
}
With my current code, it is not working. I tried to set the route parameter in the middleware as follow to match (bind model) the value in the action.
$request->route()->setParameter('department', $department)
But it is just not working. How can I set/ replace the route parameter with a model in the middleware which can be binded to the parameter in the action of the controller? Is it possible? What could be the better approach?
If I used
$request->route()->setParameter('department', $department)
to set the parameter, I cannot set type in the action of the controller like this.
store(Department $department)
But this is fine
store(Department $department)
But I want this
store(Department $department)

Laravel already has this built in. It is called Route Model Binding.
https://laravel.com/docs/5.7/routing#route-model-binding
Remove the middleware and instead keep your controller as it is. Laravel will automatically use the ID in the request to find the model and give you an instance of it. If the model cannot be found, Laravel will throw a 404 response for you.

Related

How can I redirect from a controller with a value to another controller in laravel?

OrderController.php
if (request('payment_method') == 'online') {
return redirect(route('payments.pay', $order->id));
}
web.php
Route::POST('/pay/{orderId}', 'PublicSslCommerzPaymentController#index')->name('payments.pay');
PublicSslCommerzPaymentController.php
session_start();
class PublicSslCommerzPaymentController extends Controller
{
public function index(Request $request, $ordId)
{
//code...
}
}
Here in index function I need the order id from `OrderController.
But the Error I am getting
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: POST.
if you want to redirect to named route you can use this:
return redirect()->route('payments.pay', ['orderId' => $order->id]);
if you want generate redirect to controller action you can try this:
return redirect()->action(
'PublicSslCommerzPaymentController#index', ['ordId' => $order->id]]
);
Just change in your web.php from POST method to GET method
Something like this
Route::GET('/pay/{orderId}', 'PublicSslCommerzPaymentController#index')->name('payments.pay');

How to get result in Laravel with where clause while passing model in controller method

I have a route to get a single post item by slug.
Route
Route::get('post/{post}', 'PostController#details')->name('post.details');
While I want to pass the model in the controller method for the route.
Controller
public function details(Post $post)
{
// how to get the post by slug
}
My question is how can I get the post by slug passing in the route
instead of post ID?
I am aware that I can pass the slug and get the post using where clause.
//Route
Route::get('post/{slug}', 'PostController#details')->name('post.details');
//Controller method
public function details($slug)
{
$post = Post::with('slug', $slug)->first();
}
But I want to learn to do the same by passing the Model in the method.
set route key name to your model class
//Post.php
public function getRouteKeyName()
{
return 'slug';
}
This will inform Laravel injector/resolver to look the variable passed in slug column while fetching the object instance.
What you want to do is implicit route model binding
What you can do is in your Post model define getRouteKeyName like below
<?php
class Post extends Model
{
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug';
}
}
and define your route like this:
Route::get('post/{post:slug}', 'PostController#details')->name('post.details');
and then in your controller
public function details(Post $post)
{
// it will return post with slug name
return $post;
}
Hope it helps.
Thanks

Access session data from parent controller w/o passing it in

Can I access session data from Controller, without passing the request from MyController?
class Controller extends BaseController
{
public function __construct()
{
// ** next line throws error:
// "Session store not set on request."
$userdata = request()->session()->get('userdata');
// I want to inject `userdata` into every template without
// passing data from child controllers.
view()->share(['userdata' => $userdata);
}
}
class MyController extends Controller
{
public function __construct(Request $request)
{
// This works, so the data is in fact in the session.
// I don't want to pass it, or `$request` to the parent from here.
$userdata = $request->session()->get('userdata');
...
}
}
The reason it won't be working in your __construct() method is because the StartSession middleware won't have been run yet.
To get around this you can simply use the middleware() method on the controller:
public function __construct()
{
$this->middleware(function ($request, $next) {
$userdata = $request->session()->get('userdata');
view()->share(compact('userdata'));
return $next($request);
});
}
Laravel 5.3 Upgrade guide (Scroll down the Controllers section)
In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly in your controller's constructor.

Laravel - forgetParameter

My project is multi language, so in most route I add parameter "locale". But latter I do not need to pass "locale" parameter to my controllers. I try to remove this parameter using forgetParameter() method but when I use this method then I have error "Route not bound".
So what I'm doing wrong.
How to remove "locale" from all routes?
Laravel 5.8
My route file.
Route::group(['prefix' => '{locale}','where' => ['locale' => '[a-zA-Z]{2}']], function() {
Auth::routes([app()->getLocale()]);
Route::get('/', 'HomeController#index')->name('mainPage');
Route::get('/user/{id}','UserController#showUser')->name('getUserProfile')->forgetParameter('locale');
Route::get('/user/{id}/edit','UserController#editUser')->name('editUserProfile')
->middleware('can:editUser,App\User,id')->forgetParameter('locale');
Route::patch('/user/{id}/edit','UserController#updateUser')->name('updateUserProfile')
->middleware('can:updateUser,App\User,id')->forgetParameter('locale');
});
It's not duplicate with this question - Laravel 5.4: How to DO NOT use route parameter in controller
Solution in that question doesn't work in my case. And my question is why "forgotParamter()" throw an error?
Other question is, why I can't use this construction in middleware:
$request->route()->forgetParameter('locale')
I have following error:
Call to a member function is() on null
Thank you.
Try to forget the parameter in an inline middleware in the controller's constructor. Actually, I would use a base controller: 1, get the route value; 2, set to a protected property, so inherited controllers can access to the value; 3, forget the parameter.
<?php
// Localized/Controller.php
namespace App\Http\Controllers\Localized;
use App\Http\Controllers\Controller as ControllerBase;
use Illuminate\Http\Request;
class Controller extends ControllerBase
{
protected string $currentLocale;
public function __construct()
{
$this->middleware(function ($request, $next) {
// If you need to access this later on inherited controllers
$this->currentLocale = $request->route('locale') ?? 'en';
// Other operations, like setting the locale or check if lang is available, etc
$request->route()->forgetParameter('locale');
return $next($request);
});
}
}
<?php
// Localized/UserController.php
namespace App\Http\Controllers\Localized;
use Illuminate\Http\Request;
// Extends the our base controller instead of App\Http\Controllers\Controller
class UserController extends Controller
{
public function show(Request $request)
{
// No need of string $locale arg
dd($this->currentLocale);
}
}

Laravel 5 : passing a Model parameter to the middleware

I would like to pass a model parameter to a middleware. According to this link (laravel 5 middleware parameters) , I can just include an extra parameter in the handle() function like so :
public function handle($request, Closure $next, $model)
{
//perform actions
}
How would you pass it in the constructor of the Controller? This isn't working :
public function __construct(){
$model = new Model();
$this->middleware('myCustomMW', $model);
}
**NOTE : ** it is important that I could pass different Models (ex. ModelX, ModelY, ModelZ)
First of all make sure that you're using Laravel 5.1. Middleware parameters weren't available in prior versions.
Now I don't believe you can pass an instantiated object as a parameter to your middleware, but (if you really need this) you can pass a model's class name and i.e. primary key if you need a specific instance.
In your middleware:
public function handle($request, Closure $next, $model, $id)
{
// Instantiate the model off of IoC and find a specific one by id
$model = app($model)->find($id);
// Do whatever you need with your model
return $next($request);
}
In your controller:
use App\User;
public function __construct()
{
$id = 1;
// Use middleware and pass a model's class name and an id
$this->middleware('myCustomMW:'.User::class.",$id");
}
With this approach you can pass whatever models you want to your middleware.
A more eloquent way of resolving this problem is to create a constructor method in the middleware, inject the model(s) as dependencies, pass them to class variables, and then utilize the class variables in the handle method.
For authority to validate my response, see app/Http/Middleware/Authenticate.php in a Laravel 5.1 installation.
For middleware MyMiddleware, model $myModel, of class MyModel, do as follows:
use App\MyModel;
class MyMiddleware
{
protected $myModel;
public function __construct(MyModel $myModel)
{
$this->myModel = $myModel;
}
public function handle($request, Closure $next)
{
$this->myModel->insert_model_method_here()
// and write your code to manipulate the model methods
return $next($request);
}
}
You don't need to pass the model to middleware, Because you already have access to model instance inside the middleware!
Lets say we have a route like this:
example.test/api/post/{post}
now in our middleware if we want to have access to that post dynamically we go like this
$post = $request->route()->parameter('post');
now we can use this $post, for example $post->id will give us the id of the post, or $post->replies will give us the replies belong to the post.

Resources