When I extends ApiBaseController in another class, response token denied is doesn't work. even though I put wrong app-token but still give response in another class.
class ApiBaseController extends Controller
{
protected $user;
public function __construct()
{
if (request()->header('app-token') != 'ofdsafalkhguddskjafl01JhBF9mGx2jay'){
return response()->json([
'success'=>false,
'status'=>'401',
'message'=>'Token Denied !',
'response'=>[
'total'=>0,
'data'=>[]
]
]);
}
else{
$this->user = Auth::guard('api')->user();
}
}
}
This class still work even though I put wrong app-token
class AttendeesApiController extends ApiBaseController
{
public function index(Request $request)
{
return Attendee::scope($this->account_id)->paginate($request->get('per_page', 25));
}
}
I want to make sure when app-token is wrong will give Token Denied ! response
please give me some advice
You will have to call parent constructor to make this work.
class AttendeesApiController extends ApiBaseController{
function __construct(){
parent::__construct();
}
public function index(Request $request){
return Attendee::scope($this->account_id)->paginate($request->get('per_page', 25));
}
}
If I am not mistaken, you will also have to put a kind of a die in the constructor to avoid further execution.
Update:
Best way to handle this is to group these routes inside a middleware and have the bearer token check in the middleware itself. This will make your approach more neat and you can easily add new routes that require bearer token check in this route middleware group.
While it is a good idea to keep the token validation concern separated, it is not a good practice to do such thing in the constructor, let alone hide it in the constructor of a base class.
In general, constructors should be used to construct the object, not to "do things".
Because you want to return early, it gets a bit complicated to extract this concern out of the controller. But that's what middleware is for.
Take a look at the Laravel documentation on creating your own middleware (altough what you are trying to do might be already built in)
An example middleware class could look like this:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckToken
{
/**
* Handle an incoming request and check the token.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (...) { //your token check
return ...; // your early-returned json.
}
return $next($request); //otherwise continue
}
}
Related
A question has arisen.
Suppose I have an api route that is
get: api/v1/conferences/{conference_id}/languages
The purpose would be to get the languages linked to this conference.
My program, almost always starts with conferences/{conference_id} so the conference_id must always be real. In case the conference_id does not exist, I should throw an exception.
I want to do this without putting any logic in the controllers or any class that has any particular logic of mine. I would like it to be validated by default from the laravel kernel, is that possible?
I mean, i want that every time somebody access to a route which starts with conferences/{conference_id} the program would be able to check if this id is real
Thanks
Use exists in validation rules like:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ConferencesRequest extends FormRequest
{
/**
* #inheritDoc
*/
public function all($keys = null)
{
$data = parent::all();
$data['conference_id'] = $this->route('conference_id');
return $data;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'conference_id' => ['required', 'integer', 'exists:' . App\Models\Conference::class . ',id'],
];
}
}
Or, if you dont want to use a FormRequest class, use this:
p.s. param should be changed to conference instead of conference_id:
use App\Models\Conference;
Route::get('conferences/{conference}/languages', function (Conference $conference) {
//...
});
See: https://laravel.com/docs/9.x/routing#route-model-binding
A lot of pieces to this so here's the meat. Code very slightly tweaked for brevity.
Extended class:
<?php
namespace App\Http;
use Illuminate\Http\Request as LaravelRequest;
class Request extends LaravelRequest
{
}
Middleware:
<?php
namespace App\Http\Middleware;
use App\Http\Request as CustomizedRequest;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\Request;
class CustomizeRequest
{
protected $app;
protected $customizedRequest;
public function __construct(Application $app, CustomizedRequest $customizedRequest){
$this->app = $app;
$this->customizedRequest = $customizedRequest;
}
public function handle(Request $request, Closure $next){
$this->app->instance(
'request',
Request::createFrom($request, $this->customizedRequest);
);
return $next($this->customizedRequest);
}
}
Routes:
Route::get('/books1/{id}',[BookController::class, 'frontend1']);
Route::get('/books2/{id}',[BookController::class, 'frontend2']);
Controller:
<?php
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
public function frontend1(\Illuminate\Http\Request $request){
dump($request);
dump($request->all());
dump($request->route('id'));
return Book::all();
}
public function frontend2(\App\Http\Request $request){
dump($request);
dump($request->all());
dump($request->route('id'));
return Book::all();
}
}
The /books1/5?foo=bar and frontend1() path works. $request is populated as expected.
The /books2/5?foo=bar and frontend2() path is broken. $request has vast amounts of missing data, like it was instantiated with nothing.
Evidently if I type-hint my subclass instead of the more generic parent, it's causing some kind of broken instantiation. From an OO perspective I think this should be perfectly fine and I do specifically need my subclass being provided so prefer that type-hint. Is something deep within Laravel tripping this up? Is this some obscure PHP behavior I haven't seen before?
This is kind of tricky.
First of all, you need to be familiar with the service container and dependency injection. Here is the full doc: https://laravel.com/docs/8.x/container
When you type hint a class inside a controller method, Laravel will try to understand what it should do with it.
If nothing is registered inside the service container, it will try to make a new instance of it.
\Illuminate\Http\Request is bound as a singleton (https://laravel.com/docs/8.x/container#binding-a-singleton).
While a simple bind will return a new instance at each call, a singleton will always return the exact same instance.
Here is a quick demo:
\App\Models\User::class is a class that is not explicitly bound.
When you try to resolve it using the service container, it will not find it and will try to make a new instance:
$u1 = app(\App\Models\User::class);
// Searching \App\Models\User::class...
// Cannot find \App\Models\User::class...
// returning new \App\Models\User();
$u2 = app(\App\Models\User::class);
// same process again
$u3 = app(\App\Models\User::class);
// and again
// You can check these instances are indeed different by checking their hash:
dd(
spl_object_hash($u1), // 000000004af5213500000000220f0bc0 (52135)
spl_object_hash($u2), // 000000004af5213400000000220f0bc0 (52134)
spl_object_hash($u3) // 000000004af5213700000000220f0bc0 (52137)
);
But since \Illuminate\Http\Request::class is bound by Laravel, it follows a different path:
$r1 = app(\Illuminate\Http\Request::class);
// Searching \Illuminate\Http\Request::class...
// Found it! Bound as a singleton.
// returning new \Illuminate\Http\Request() and storing the
// instance in case it is required again later;
$r2 = app(\Illuminate\Http\Request::class);
// Searching \Illuminate\Http\Request::class...
// Found it and already called! Returning the stored instance ($r1)
$r3 = app(\Illuminate\Http\Request::class);
// Searching \Illuminate\Http\Request::class...
// Found it and already called! Returning the stored instance ($r1)
// Their hash are the same
dd(
spl_object_hash($u1), // 0000000011f522cf0000000077704cd1
spl_object_hash($u2), // 0000000011f522cf0000000077704cd1
spl_object_hash($u3) // 0000000011f522cf0000000077704cd1
);
Now, what's happening?
Under the hood, when a new request is made to your app and before hitting the controller method, Laravel will do a lot of things to prepare the \Illuminate\Http\Request instance.
For instance, it will setup the route resolver inside Illuminate\Routing\Router:
/**
* Return the response for the given route.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Routing\Route $route
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function runRoute(Request $request, Route $route)
{
// here
$request->setRouteResolver(function () use ($route) {
return $route;
});
//
$this->events->dispatch(new RouteMatched($route, $request));
return $this->prepareResponse($request,
$this->runRouteWithinStack($route, $request)
);
}
Each time Laravel internally call a method like this:
protected function method(Request $request){
// do something to $request
}
$request is always the same instance, because it is bound as a singleton.
We are now in your controller.
public function frontend1(\Illuminate\Http\Request $request){
// Searching \Illuminate\Http\Request::class...
// Found it and already called!
// Returning the stored instance that has been prepared through all
// Laravel core classes
dump($request);
dump($request->all()); //well prepared
dump($request->route('id')); //well setup
return Book::all();
}
public function frontend2(\App\Http\Request $request){
// Searching \App\Http\Request::class...
// Cannot find \App\Http\Request::class...
// returning new \App\Http\Request();
dump($request);
dump($request->all()); //nothing
dump($request->route('id')); //empty
return Book::all();
}
If you are still here, how to solve this problem?
The easiest way is to use a FormRequest, initially designed to handle form validation, but if you return an empty rules array, you should be able to do everything you did with your custom \App\Http\Request instance:
<?php
namespace App\Http;
use Illuminate\Foundation\Http\FormRequest;
class Request extends FormRequest
{
public function rules()
{
return [];
}
}
Try again, everything should work fine, since this is a feature specially designed to replace the initial \Illuminate\Http\Request object.
The full doc is here: https://laravel.com/docs/8.x/validation#creating-form-requests
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.
In my application I use Laravel's authentication system and I use dependency injection (or the Facade) to access the logged in user. I tend to make the logged in user accessible through my base controller so I can access it easily in my child classes:
class Controller extends BaseController
{
protected $user;
public function __construct()
{
$this->user = \Auth::user();
}
}
My user has a number of different relationships, that I tend to eager load like this:
$this->user->load(['relationshipOne', 'relationshipTwo']);
As in this project I'm expecting to receive consistently high volumes of traffic, I want to make the application run as smoothly and efficiently as possible so I am looking to implement some caching.
I ideally, need to be able to avoid repeatedly querying the database, particularly for the user's related records. As such I need to look into caching the user object, after loading relationships.
I had the idea to do something like this:
public function __construct()
{
$userId = \Auth::id();
if (!is_null($userId)) {
$this->user = \Cache::remember("user-{$userId}", 60, function() use($userId) {
return User::with(['relationshipOne', 'relationshipTwo'])->find($userId);
});
}
}
However, I'm unsure whether or not it's safe to rely on whether or not \Auth::id() returning a non-null value to pass authentication. Has anyone faced any similar issues?
I would suggest you used a package like the following one. https://github.com/spatie/laravel-responsecache
It caches the response and you can use it for more than just the user object.
Well, after some messing about I've come up with kind of a solution for myself which I thought I would share.
I thought I would give up on caching the actual User object, and just let the authentication happen as normal and just focus on trying to cache the user's relations. This feels like quite a dirty way to do it, since my logic is in the model:
class User extends Model
{
// ..
/**
* This is the relationship I want to cache
*/
public function related()
{
return $this->hasMany(Related::class);
}
/**
* This method can be used when we want to utilise a cache
*/
public function getRelated()
{
return \Cache::remember("relatedByUser({$this->id})", 60, function() {
return $this->related;
});
}
/**
* Do something with the cached relationship
*/
public function totalRelated()
{
return $this->getRelated()->count();
}
}
In my case, I needed to be able to cache the related items inside the User model because I had some methods inside the user that would use that relationship. Like in the pretty trivial example of the totalRelated method above (My project is a bit more complex).
Of course, if I didn't have internal methods like that on my User model it would have been just as easy to call the relationship from outside my model and cache that (In a controller for example)
class MyController extends Controller
{
public function index()
{
$related = \Cache::remember("relatedByUser({$this->user->id})", 60, function() {
return $this->user->related;
});
// Do something with the $related items...
}
}
Again, this doesn't feel like the best solution to me and I am open to try other suggestions.
Cheers
Edit: I've went a step further and implemented a couple of methods on my parent Model class to help with caching relationships and implemented getter methods for all my relatonships that accept a $useCache parameter, to make things a bit more flexible:
Parent Model class:
class Model extends BaseModel
{
/**
* Helper method to get a value from the cache if it exists, or using the provided closure, caching the result for
* the default cache time.
*
* #param $key
* #param Closure|null $callback
* #return mixed
*/
protected function cacheRemember($key, Closure $callback = null)
{
return Cache::remember($key, Cache::getDefaultCacheTime(), $callback);
}
/**
* Another helper method to either run a closure to get a value, or if useCache is true, attempt to get the value
* from the cache, using the provided key and the closure as a means of getting the value if it doesn't exist.
*
* #param $useCache
* #param $key
* #param Closure $callback
* #return mixed
*/
protected function getOrCacheRemember($useCache, $key, Closure $callback)
{
return !$useCache ? $callback() : $this->cacheRemember($key, $callback);
}
}
My User class:
class User extends Model
{
public function related()
{
return $this->hasMany(Related::class);
}
public function getRelated($useCache = false)
{
return $this->getOrCacheRemember($useCache, "relatedByUser({$this->id})", function() {
return $this->related;
});
}
}
Usage:
$related = $user->getRelated(); // Gets related from the database
$relatedTwo = $user->getRelated(true); // Gets related from the cache if present (Or from database and caches result)
I have a BaseController that provides the foundation for most HTTP methods for my API server, e.g. the store method:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
I then extend on this BaseController in a more specific controller, such as the UserController, like so:
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
}
This works great. However, I now want to extend UserController to inject Laravel 5's new FormRequest class, which takes care of things like validation and authentication for the User resource. I would like to do this like so, by overwriting the store method and using Laravel's type hint dependency injection for its Form Request class.
UserController.php
public function store(UserFormRequest $request)
{
return parent::store($request);
}
Where the UserFormRequest extends from Request, which itself extends from FormRequest:
UserFormRequest.php
class UserFormRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required',
'email' => 'required'
];
}
}
The problem is that the BaseController requires a Illuminate\Http\Request object whereas I pass a UserFormRequest object. Therefore I get this error:
in UserController.php line 6
at HandleExceptions->handleError('2048', 'Declaration of Bloomon\Bloomapi3\Repositories\User\UserController::store() should be compatible with Bloomon\Bloomapi3\Http\Controllers\BaseController::store(Illuminate\Http\Request $request)', '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php', '6', array('file' => '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php')) in UserController.php line 6
So, how can I type hint inject the UserFormRequest while still adhering to the BaseController's Request requirement? I cannot force the BaseController to require a UserFormRequest, because it should work for any resource.
I could use an interface like RepositoryFormRequest in both the BaseController and the UserController, but then the problem is that Laravel no longer injects the UserFormController through its type hinting dependency injection.
In contrast to many 'real' object oriented languages, this kind of type hinting design in overridden methods is just not possible in PHP, see:
class X {}
class Y extends X {}
class A {
function a(X $x) {}
}
class B extends A {
function a(Y $y) {} // error! Methods with the same name must be compatible with the parent method, this includes the typehints
}
This produces the same kind of error as your code. I would just not put a store() method in your BaseController. If you feel that you are repeating code, consider introducing for example a service class or maybe a trait.
Using a service class
Below a solution that makes use of an extra service class. This might be overkill for your situation. But if you add more functionality to the StoringServices store() method (like validation), it could be useful. You can also add more methods to the StoringService like destroy(), update(), create(), but then you probably want to name the service differently.
class StoringService {
private $repo;
public function __construct(Repository $repo)
{
$this->repo = $repo;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
$service = new StoringService($this->repo); // Or put this in your BaseController's constructor and make $service a member variable
return $service->store($request);
}
}
Using a trait
You can also use a trait, but you have to rename the trait's store() method then:
trait StoringTrait {
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
use {
StoringTrait::store as baseStore;
}
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
return $this->baseStore($request);
}
}
The advantage of this solution is that if you do not have to add extra functionality to the store() method, you can just use the trait without renaming and you do not have to write an extra store() method.
Using inheritance
In my opinion, inheritance is not so suitable for the kind of code reuse that you need here, at least not in PHP. But if you want to only use inheritance for this code reuse problem, give the store() method in your BaseController another name, make sure that all classes have their own store() method and call the method in the BaseController. Something like this:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
protected function createResource(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
UserController.php
public function store(UserFormRequest $request)
{
return $this->createResource($request);
}
You can move your logic from BaseController to trait, service, facade.
You can not override existing function and force it to use different type of argument, it would break stuff. For example, if you later would write this:
function foo(BaseController $baseController, Request $request) {
$baseController->store($request);
}
It would break with your UserController and OtherRequest because UserController expects UserController, not OtherRequest (which extends Request and is valid argument from foo() perspective).
As others have mentioned, you cannot do what you want to do for a host of reasons. As mentioned, you can solve this problem with traits or similar. I am presenting an alternative approach.
At a guess, it sounds like you are trying to follow the naming convention put forth by Laravel's RESTful Resource Controllers, which is kind of forcing you to use a particular method on a controller, in this case, store.
Looking at the source of ResourceRegistrar.php we can see that in the getResourceMethods method, Laravel does either a diff or intersect with the options array you pass in and against the default values. However, the those defaults are protected, and include store.
What this means is that you can't pass anything to Route::resource to force some override of the route names. So let's rule that out.
A simpler approach would be to simply set up a different method just for this route. This can be achieved by doing:
Route::post('user/save', 'UserController#save');
Route::resource('users', 'UserController');
Note: As per the documentation, the custom routes must come prior to the Route::resource call.
The declaration of UserController::store() should be compatible with BaseController::store(), which means (among other things) that the given parameters for both the BaseController as well as UserController should be exactly the same.
You actually cán force the BaseController to require a UserFormRequest, it's not the prettiest solution, but it works.
By overwriting there is no way you can replace Request with UserFormRequest, so why not use both? Giving both methods an optional parameter for injecting the UserFormRequest object. Which would result in:
BaseController.php
class BaseController {
public function store(Request $request, UserFormRequest $userFormRequest = null)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
public function store(UserFormRequest $request, UserFormRequest $userFormRequest = null)
{
return parent::store($request);
}
}
This way you can ignore the parameter when using BaseController::store() and inject it when using UserController::store().
The easiest and cleanest way I found to circumvent that problem was to prefix the parent methods with an underscore. For example:
BaseController:
_store(Request $request) { ... }
_update(Request $request) { ... }
UserController:
store(UserFormRequest $request) { return parent::_store($request); }
update(UserFormRequest $request) { return parent::_update($request); }
I feel like creating service providers is an overkill. What we're trying to circumvent here is not the Liskov substitution principle, but simply the lack of proper PHP reflection. Type-hinting methods is, in itself, a hack after all.
This will force you to manually implement a store and update in every child controller. I don't know if that's bothersome for your design, but in mine, I use custom requests for each controller, so I had to do it anyway.