Is there any easy way of retrieving the route binded model within a Request?
I want to update a model, but before I do, I want to perform some permissions checks using the Requests authorize() method. But I only want the owner of the model to be able to update it.
In the controller, I would simply do something like this:
public function update(Request $request, Booking $booking)
{
if($booking->owner->user_id === Auth::user()->user_id)
{
// Continue to update
}
}
But I'm looking to do this within the Request, rather than within the controller. If I do:
dd(Illuminate\Http\Request::all());
It only gives me the scalar form properties (such as _method and so on, but not the model).
Question
If I bind a model to a route, how can I retrieve that model from within a Request?
Many thanks in advance.
Absolutely! It’s an approach I even use myself.
You can get the current route in the request, and then any parameters, like so:
class UpdateRequest extends Request
{
public function authorize()
{
// Get bound Booking model from route
$booking = $this->route('booking');
// Check owner is the currently authenticated user
return $booking->owner->is($this->user());
}
}
Unlike smartman’s (now deleted) answer, this doesn’t incur another find query if you have already retrieved the model via route–model binding.
However, I’d also personally use a policy here instead of putting authorisation checks in form requests.
Once you did your explicit binding (https://laravel.com/docs/5.5/routing#route-model-binding) you actually can get your model directly with $this.
class UpdateRequest extends Request
{
public function authorize()
{
return $this->booking->owner->user_id == $this->booking->user()->id;
}
}
Even cleaner!
To add on to Martin Bean's answer, you can access the bound instance using just route($param):
class UpdateRequest extends Request
{
public function authorize()
{
$booking = $this->route('booking');
return $booking->owner->user_id == $this->user()->id;
}
}
Note: This works in Laravel 5.1. I have not tested this on older versions.
If you are not using the bindings middleware or if you want to access the bound $model anywhere else apart from FormRequest and Controller you can use the following:
$book = app(Book::class)->resolveRouteBinding(request()->route('book'));
Related
I wish to initialize a particular variable and reuse it within the class without needing to rewrite the entire code again and again within the class.
$profileInfo = Profile::with('address')->where('id', '=', '1')->get();
The variable above is what I want to reuse.
I tried using constructor
protected $profileInfo;
public function __construct(Profile $profileInfo){
$this->profileInfo = Profile::with('address')->where('id', '=', '1')->get();
}
public function index($profileInfo){
$this->profileInfo;
dd($profileInfo);
}
But I get Too few arguments to function App\Http\Controllers\ProfileController::index(), 0 passed in the display when I load the blade view in the browser.
Please any help?
You're having trouble because you are mixing concepts. Dependency Injection, Local instance variables, and possibly route model binding or route variable binding.
Dependency Injection is asking Laravel to provide an instance of a class for you. In cases where Laravel is loading something, it typically tries to use DI to fill the unknowns. In the case of your constructor, you're asking Laravel to provide the constructor with a fresh instance of the Profile class under the variable name $profileInfo. You do not end up using this variable in the constructor, so there is no point to requesting it here.
Next (still in the constructor) you set up and assign the local variable profileInfo to the controller class instance.
Moving on, when the route tries to trigger the index method there is a variable requirement of $profileInfo. Laravel doesn't know what this is here and it doesn't match anything from the route (See Route Model Binding in the docs). Because of this you get the "Too few arguments" message.
If this variable was not present, you should have the profileInfo you set up earlier.
If you want to keep the local variable, you can do something like this:
protected $profileInfo;
public function __construct(){
$this->profileInfo = Profile::with('address')->where('id', '=', '1')->get();
}
public function index(){
dd($this->profileInfo);
}
Here is another suggestion for you to consider...
Since this is called Profile, it seems like we should ask a user model for the appropriate profile record.
// in your user model, set up a relationship
public function profile(){
return $this->hasOne(Profile::class);
}
// In controller, if you're getting a profile for the logged in user
public function index(){
$profile = Auth::user()->profile;
dd($profile);
}
// In controller, if you're getting profile for another user via route model binding
public function index(User $user){
$profile = $user->profile;
dd($profile);
}
I am newbie with Laravel. I have just fork laravel 5 boilerplate from https://github.com/rappasoft/laravel-5-boilerplate.
In route files, i see that there is a line like that :
Route::group(['prefix' => 'user/{deletedUser}'], function () {
Route::get('delete', 'UserStatusController#delete')->name('user.delete-permanently');
Route::get('restore', 'UserStatusController#restore')->name('user.restore');
});
I understand it means that, when url catch 'restore' it will use function restore in UserStatusController.
And here it is:
public function restore(User $deletedUser, ManageUserRequest $request)
Can anybody can help me to find out that, how can it send object $deletedUser to restore function. Tks you!
If your look at the route definition:
user/{deletedUser}
That {deletedUser} represents the id of the user to be deleted/restored. Variables are declared between {} in routes as the docs states.
Now in your controller:
public function restore(User $deletedUser, ManageUserRequest $request)
You can see that a User object is declared as an argument. This object is being injected by Laravel, that automatically will look for an User object that has that id. This is called Route Model Binding.
The documentation explains it better:
When injecting a model ID to a route or controller action, you will often query to retrieve the model that corresponds to that ID. Laravel route model binding provides a convenient way to automatically inject the model instances directly into your routes. For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.
The same way, the Request class injected in this case is a ManageUserRequest that should be an instance of a FormRequest.
So, returning to your question, you will just have to specify the user id that you want to delete/restore, like this:
someurl.dev/users/5 // <-- for the user of id=5
Now your controller will interact with that specific object to do what you want:
public function restore(User $deletedUser, ManageUserRequest $request)
{
$deletedUser->delete(); // for example
}
There are two things happening here: parameters (docs) and model binding (docs)
First of all, in ['prefix' => 'user/{deletedUser}'] you can see that you are parsing a parameter from the url. This way, when someone navigates to api/user/3, laravel will pass the 3 to your route handler.
Second, it would be very nice to get the User model instance instead of just getting an id number. That's possible and it's called "model binding". Model binding can be
Explicit
You add your bindings to boot method in your RouteServiceProvider class, telling laravel what is the expected type of parameter.
public function boot()
{
parent::boot();
Route::model('deletedUser', App\User::class);
// in older docs I've seen 'App\User' passed as a string instead of as a class
}
Implicit
Laravel automatically figures out what model you need based on type hints.
public function restore(User $deletedUser, ManageUserRequest $request) {}
Here, $deletedUser has is type hinted as User. Laravel sees this, so it will go ahead and convert the id to the Eloquent model for you.
You seem to be using implicit binding, but feel free to check your RouteServiceProvider class.
Check the documentation links for more details, it's pretty well written. (If you are not using version 5.6, then just change the version number in the links).
You Just need to pass ID of the user as a parameter.
And this function
public function restore(User $deletedUser, ManageUserRequest $request)
you can see $deletedUser is of type User Laravel will search for that id ($deletedUser) in Users table and return an object of that user.
If you don't want User object and just need ID that you are passing in URL update restore() function to
public function restore($deletedUser, ManageUserRequest $request)
I have following method in my controller:
public function store()
{
$data=Input::all();
User::create($data);
}
The above code works perfectly. My question is can we run the above method in model without writing in controller? And which is the best approach?
you can try following way
in your model
public function insetUser()
{
$input = Input::all();
User::create($input);
//here instead of User,you can use self like self::create($input);
}
in controller you can
public function store()
{
User::insetUser();
}
If it is in model, how you are going to trigger it?
It is in fact only one line of code
User::create(Input::all());
What it is here is the instance of model User and method create with injected model Input. Of couse you may set (model) User.php:
public function storedata()
{
return $this->create(Input::all());
}
And then run it in your controller:
User::storedata();
But is it better? ;-)
In this circumstance, I don't think you gain much from moving things arounds.
If this is your Controller method:
public function store()
{
$data=Input::all();
User::create($data);
}
then this makes sense. The input data is being handled by the controller method, and is being passed to the create method on your User Model.
If however, there was more logic required before creating a User record, then it'd be perfectly valid to abstract that logic to your User model. But as it stands though, I think your current implementation is appropriate.
I have a Session table where I create sessions for users by authenticating in a REST API.
A Session has a relation with a User, so I can retrieve the User object from the Session.
I want to create a Eloquent scope to retrieve the currentUser logged in like Session::currentUser()->id where the Authorization header equals the token in the Session table.
It seems I can't use the Request instance in my scopeCurrentUser(). Can't inject it via the Controller as well like this:
public function __construct(Request $request) {
$this->request = $request;
}
Also tried to inject it into the method (which is possible since Laravel 5)
public function scopeCurrentUser($query, Request $request)
{
return $query->where('token', $request->header('Authorization'));
}
Any way I can get the $request->header('Authorization')?
You can get the request instance by using app('Illuminate\Http\Request') or Request:: (make sure you have use Request; at the top of you file).
So either you have
use Request;
public function scopeCurrentUser($query)
{
return $query->where('token', Request::header('Authorization'));
}
or
public function scopeCurrentUser($query)
{
return $query->where('token', app('Illuminate\Http\Request')->header('Authorization'));
}
You can't do dependency injection in Eloquent methods as far as I know (at least it didn't work when I tried it).
Is it possible to automatically bind an instance of a model to a parameter in a controller action? Is their any workaround for doing this if it does not already exist within Yii itself?
I know this is possible in Laravel and ASP.NET MVC. Here is what I want to achieve:
class PostController extends Controller {
public function actionEdit(Post $post) {
if($_POST['Post']){
$post->attributes = $_POST['Post'];
$post->save();
}
$this->render('edit', array('post'=>$post));
}
}
Given a url like localhost/?r=post/edit&post=1
[eg Yii::app()->createUrl('post/edit',array('post'=>$mypost->id))] the id 1 is converted to an instance of CActiveRecord [i.e. Post::model()->findByPk(1) is called automatically]
from your snippet it seems you are trying to pass both a $_POST and $_GET variable to your action.
Values in $_POST,$_GET are stored as associated arrays not objects themselves.
In Yii you can't do that directly load the model, however you can achieve your objective like this.
class PostController extends Controller {
public function actionEdit($post) {
$postModel = Post::model()->findByPk($post)
$this->render('edit', array('post'=>$postModel));
}
}
This is slightly longer but more useful if you want to bind additional conditions for example allow editing only is post is not deleted, or is active etc. You can use then findByAttributes, or findAll with additional conditions which may be implicit and not necessarily passed as parameter
Alternatively if you really required such a functionality you can write a component class with a custom action, where you can load a model as you wish
<?php
class LoadModel extends CComponent {
public function loadModel($type,$id){
if(!in_array($type,$arrayofpossibleModels || !is_numeric($id)){ //Validation if $type is a model name is required.
throw new CHttpException("400","Bad Request")
}
return $type::model()->findByPk($id);
}
}