Get request from controller in model - Laravel - php

In my API I used "with" method to get parent's model relation and everything works fine.
I want to add an attribute in my relation and return it in my API but I should use request in my model.
Something like this :
Book.php
protected $appends = ['userState'];
public function getUserStateAttribute () {
return User::find($request->id); //request not exists here currently
}
I have $request in my controller (api controller)
Controller.php
public function get(Request $request) {
Post::with('books')->all();
}
I believe using static content to append in array of model is so easy but how about using request's based content ?

I guess you can use request() helper :
public function getUserStateAttribute () {
return User::find(request()->get('id'));
}
Sure this is not really MVC pattern, but it can work

You want to take request as a parameter here:
public function getUserStateAttribute (Request $request) {
return User::find($request->id);
}
That way you can access it in the function. You will just need to always pass the Request object whenever you call that function.
e.g. $book->getUserStateAttribute($request);
Alternatively, you could just pass the ID, that way you need not always pass a request, like so:
public function getUserStateAttribute ($id) {
return User::find($id);
}
Which you would call like:
e.g. $book->getUserStateAttribute($request->id);

Related

How is Model::find($id) secure in Laravel?

I have an app where I create entries based on who is signed in. If I use the find($id) method it returns json response. The function is like this:
public function edit($id)
{
$chore = Chore::find($id);
return response()->json($chore);
}
Now if I where to edit the id value I might be able to access other user's data which isn't secure at all.
So I added and extra column user_id that checks who is signed in:
public function edit($id)
{
$chore = Chore::find($id)
->where('user_id', Auth::id());
return response()->json($chore);
}
But of course laravel can't make it easy so it doesn't work. Adding ->get() returns an array instead of a json response.
First of all how is find($id) ever secure in any app that uses authentication and secondly how do I add another condition under the find($id) clause? I need data returned in JSON otherwise I will need to rewrite all my front-end which isn't ideal at this point.
I also tried:
public function edit($id)
{
$chore = Chore::where('id', $id)
->where('user_id', Auth::id());
return response()->json($chore);
}
but no luck
You just need to call the where method before the find method
$chore = Chore::where('user_id', Auth::id())
->find($id);
As an alternative, If you've set up relationships properly on user model
// In User.php
public function chores()
{
return $this->hasMany(Chore::class, 'user_id', 'id');
}
then you could also do
$chore = Auth::user()->chores()->find($id);
While this seems like extra work, it's more convenient and easier to maintain.
If Chore is in a one-to-one relationship with your User model, then you can create a relationship in your User.php model.
public function chore() {
return $this->hasOne(Chore::class);
}
Then in your controller, you could simply call auth()->user()->chore.
The Eloquent's find() method finds only one record by the primary key. If you use additional validation it's perfectly safe. You could use route model binding to simplify the code.
web.php
Route::get('/chore/{chore}/', 'ChoreController#edit');
Then in your controller
public function edit(Chore $chore)
{
if (! $chore->user_id === auth()->id()) {
// throw error or redirect, or whetever
}
return response()->json($chore);
}
To simplify the controller a little bit more, you could create a form request and inject it into controller's method as a regular request. (https://laravel.com/docs/7.x/validation#authorizing-form-requests)
Then you could move the validation into your form request. It should look something like this:
public function authorize() {
return $this->route('chore')->user_id === $this->user()->id
}

Laravel 5: PHP variable shared by all methods of controller

I have a resource controller. The route for that is like this-
Route::resource('branches/{branch_id}/employees', 'EmployeeController');
The problem is in every method I need to pass the branch variable to the view.
public function index($branch_id){
$branch = Branch::find($branch_id);
$employees = Employee::orderBy('created_at', 'desc')->get();
return view('employees.index', compact('branch', 'employees'));
}
Is there any way that I can pass the branch variable to each view returned through this controller?
#Sapnesh Naik Its not a duplicate as I need to manipulate the branch in each function.
In this case, you may try this:
public function __construct(Request $request)
{
view()->share(
'branch', Branch::find($request->route('branch_id'))
);
}
You may also use request()->route('branch_id') if you don't use method injection by type hinting the Request $request in your __construct method.
Add this to your controller constructor:
public function __construct()
{
view()->share('branch', Route::current()->getParameter('brach_id'););
}

How call specific function dynamically that we get in url/path in laravel

My Code in route.php:-
Route::get('/register/{id}',array('uses'=>'UserRegistration#id));
I want to call function id (that can be any function of controller) in UserRegistration controller.
Url is like this:- http://localhost:8000/register/test,
http://localhost:8000/register/login
here test and login are function in controller.
{id} is the parameter you're passing to route. So, for your routes go with something like this:
Route::get('/register/id/{id}',array('uses'=>'UserRegistration#id));
//this route requires an id parameter
Route::get('/register/test',['uses'=>'UserRegistration#test]);
Doing this you can call your functions but it is not the recomended way of doing it. Having separete routes foreach function allows for a more in depth control.
public function id(Request $request)
{
return $this->{$request->id}($request);
}
public function test(Request $request)
{
return $request->all();
}

Passing variable from middleware to controllers __construct to prevent repeating myself

I'm doing an existence check within a middleware, by checking a route-parameter.
If the check succeeds, I'm attaching it's model to the request to make it available throughout the rest of the request-cycle, application.
// App\Http\Middleware\CheckForExistence.php:
...
public function handle($request, Closure $next)
{
// some checks...
// success
$request->attributes->add([
'company' => $someModel
]);
}
I now have a controller which 'needs' this information in a couple of methods. So my thought was to add it to the construct of the controller and add it as a protected var in the whole controller:
// App\Http\Controllers\MyController.php
<?php
use Illuminate\Http\Request;
class MyController extends Controller
{
protected $company;
public function __construct(Request $request)
{
$this->company = $request->attributes->get('company');
}
public function index()
{
dd($this->company); // returns null
}
}
This controllers index() returns null instead of the give model.
If I change the index() method to:
public function index(Request $request)
{
return $request->attributes->get('company');
}
This returns the model; as expected.
Why is this happening? It looks like the middleware is not run when the controller is constructed.... Is there a way to circumvent it?
Or am I missing the obvious here.....
I could off course repeat myself in each method; but that is not very DRY ;)
You can't access the session or authenticated user in your controller's constructor because the middleware has not run yet, So you can do it like this :
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->company = $request->attributes->get('company');
return $next($request);
});
}
For reasons currently unclear to me, the controller object is constructed before the request changes are reflected in the request object. In short the request is not considered properly constructed when a controller is constructed. This post seems to imply that.
There's two ways to work around this (if for a second we ignore what you're trying to do).
Use request dependency injection
public function index(Request $request)
{
$compary = $request->attributes->get('company');
}
This is not really WET because you're just swapping $this->company with $request->attributes->get('company') it's just a refactor. You should be injecting the request in the controller action anyway and if you don't want to do that you can use the request() helper.
Use a callback middleware in the constructor (Maraboc's answer explains how)
Now if you want a more case specific solution though you can use case specific dependency injection:
If you need to bind a model to a specific route parameter you can use route model binding and add the following in your RouteServiceProvider (or any provider).
Route::bind("companyAsARouteVarName", function () {
// this is why more details in the question are invaluable. I don't know if this is the right way for you.
//checks
// success
return $someModel;
});
Then you will register your route as:
Route::get("/something/{companyAsARouteVarName}", "SomeController#index");
and your controller will be:
public function index(Company $companyAsARouteVarName) {
//Magic
}
Controller constructor will be initialized before middleware execution.
You can get data from Injected $request object in controller functions.

How to pass POST request to constructor in Laravel?

I'd like to access a request variable from the constructor of my controller in Laravel. How can I do this?
this is my route:
Route::post('bookGetById', ['uses' => 'v1\BookController#getBookById']);
and here is my controller:
public function __construct(Request $request = null)
{
parent::__construct();
$this->bookStructure = new bookStructure($request->imageHeight);
}
but the request variable is always null. How can I pass the request into the constructor?
Don't know what version of laravel you're using but I think in 5.2+ the controller is created before the request is bound. There's a workaround:
public function __construct()
{
parent::__construct();
$this->middleware(function (Request $r, $next) {
$this->bookStructure = new bookStructure($request->imageHeight);
return $next($r);
});
}
However I would recommend doing this is via the service container:
File AppServiceProvider
$this->app->bind("bookstructure.withheight", function ($app) {
return new bookStructure($app->make("request")->get("imageHeight",0));
});
You can instantiate your bookStructure wherever you need it via:
app()->make("bookstructure.withheight");
I know you are asking how to pass a request to a class constructor but you may want to rethink it and pass it to a controller that validates the request and instantiates your class with validated request variables.

Categories