I would like to validate the get parameter where i passed throug the route to my controller.
api/route
get /order/{id} -> OrderController::order
public function order($id) {
// validation here (rules= require,between 1 and 1000)
return Order::find($id);
}
how can I validate inside my controller without creating a separate request class?
which validation class do i have to import? (this one: Illuminate\Support\Facades\Validator ? )
Is this a good or common solution?
As #lagbox already wrote, you can check all of your questions inside the Laravel documentation.
Validation inside the controller
use Illuminate\Http\Request;
class MyController extends Controller
{
public function order(Request $request, int $id)
{
$validated = $this->validate([
// .. put your fields and rules here
]);
}
}
If your controller extends the base controller, that is shipped with every Laravel installation you have direct access to the validator via $this->validate.
With injecting the $request you have access to the fields that are send (POSTed) to your server.
If this is a good solution heavily depends on the projects size and other factors. It is definitely a good solution to start with. If your project grows and you need to have the same validation logic in various places you can again think about additional Form Request Validation.
To apply certain rules to the route parameter, f. ex. id, you can use Regular Expression Constraints.
Futher processing of request data
I personally would leave the validation inside the controller (or a form request class).
If there is any problem with the request data, then it should fail there and not continue to the service class.
You could say this is a kind of fail fast approach. Why moving more and more inside your code, if your request items might have an error (are not valid)?
$id is always present so required validation always passes.
So you only need to check between 1 and 1000 condition.
I think using regex constraints in the route is a good idea here.
Route::get('/order/{id}','OrderController#order')
->where(['id'=> '1000|^[1-9]{0,2}[1-9]$']);
If id is less than 1 or more than 1000 or any other random string it won't match the route and if there isn't any other matching routes too, it gives 404 error.
If you really want to validate the route parameter in the controller, you can use this:
$validator = \Illuminate\Support\Facades\Validator::make(['id' => $id],
[
'id' => 'required|integer|between:1,1000'
]
);
$validator->validate();
Related
First question was solved with findOrFail method
Is there any way to prevent users from checking non-existing routes?
Example
I've got route to http://127.0.0.1:8000/event/9
but event with id 8 does not exist, if user would go to that id there is a massage:
Attempt to read property "photo_patch" on null (View: C:\xampp\htdocs\Laravel1\resources\views\frontend\eventView.blade.php)
Or any other error from db that record does not exist.
Second question
How to turn on preety URLs in laravel
So my page with display http://127.0.0.1:8000 not http://127.0.0.1:8000/events something...
I know that its somewere in config files but I cant find it.
Example class and route that uses it:
-----------------------------Class----------------
public function eventView($id)
{
$notDisplay = Auth::user();
$eventView = Event::findOrFail($id);
if(!$notDisplay){
$eventView->displayed = $eventView->displayed +1;
$eventView->save();
}
return view('frontend/eventView', ['eventView' => $eventView]);
}
----------------Route-----------------
Route::get('event/' . '{id}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
First off, use the container!
Laravel's service container is very powerful and your controller resolve use-case is one of the most common places you should be using it. The url argument and controller argument MUST match for this to work.
Your route:
Route::get('event/' . '{event}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
Your Controller:
public function eventView(Event $event)
{
return view('frontend/eventView', ['event' => $event]);
}
When leveraging Laravel's dependency injection and container, you get your findOrFail() for free. You should also remove your auth check, and handle that with route middleware.
In terms of "prettifying" urls, Laravel's route model binding feature allows you to control what property of a model is used to for container resolution. For example, let's imagine your event has a unique slug you'd like to use instead of the auto-increment id:
Route::get('event/' . '{event:slug}', [App\Http\Controllers\FrontendController::class, 'eventView'])->name('eventView');
Laravel's routing functionality offers a fallback feature that would allow you to fine-tune where the user is redirected if the route model binding failed.
https://laravel.com/docs/8.x/routing#fallback-routes
With regard to preventing an unauthorized individual from editing someone else's event. The first place I would put protections in place would be at the time of persistence (when saving to the database). While you can do this in every place in your codebase where persistence occurs, Laravel's Observer feature could be a great fit. That way, you can be confident that no matter what code is added to your app, the ownership check will always be run before making any changes to events.
https://laravel.com/docs/8.x/eloquent#observers
The second place that I would put protections in place would be with a route middleware on any routes that can mutate the event. That way, you can redirect the user away from an event they don't own before they even have a chance to attempt to edit it.
https://laravel.com/docs/8.x/middleware#assigning-middleware-to-routes
I faced a weird issue in Laravel today, the version am using is Laravel 5.5 and I have defined a route as below in the application.
Route::get('getplaylist/{playlistid}/{page}', 'Mycontroller#getplaylist');
And in my controller am trying to fetch the parameters, weirdly
dd($request->all()); // results in empty array []
whereas the below one works,
dd($request->playlistid);
Any help would be appreciated on what is happening behind the scenes. The issue am facing is am not able to validate the request since an empty '[]' array is resulted.
Route parameters, like playlistid and page in your example, can be used with the $request->route() method.
Example:
$request->route('playlistid')
You can also fetch all route parameters using $request->route()->parameters().
As #erikgaal already mentioned, these are route parameters, not request parameters.
But, as is written in the docs, it is one of the most basic and core parts of Laravel, that these route parameters get injected into the controller method. Therefore, with your route:
class Mycontroller
{
public function getplaylist(Request $request, $playlistid, $page)
{
// Do stuff
}
}
The parameter is posted to some_name like this:
{{ route('some_name', $id = '1'}}
How can I access it in the if condition?
Route::group(['prefix' => '/'], function()
{
if ( condition )
{
Route::get('/route/{id}', 'ControllerA#methodA')->name('some_name');
} else{
Route::get('/route{id}', 'ControllerB#methodB')->name('some_name');;
}
});
How can I use the {id} parameter in the if (condition)?
I tried
Route::group(['prefix' => '/'], function($id)
{
if ( $id == 1)
And it's not working.
I think the best thing you can do is a Middleware, for example:
public function handle($request, Closure $next)
{
if ($request->id == 'some_value') {
redirect action('ControllerA#methodA');
}
else {
redirect action('ControllerB#methodB');
}
return $next($request);
}
Check the docs, personally i've never done an if inside my routes folder, besides that, it's really dangerous to practice stuff like that, make everything happen in the views, if you are messing up with user logged in or not, do auth::check() or something like that, but never play with the routes web.php to ensure security in your app, everything else is made on the controllers and views.
I don't think it's a good practice to validate the id in the route file to redirect to different controllers, and heres why:
You'll send a request to that endpoint and send an ID.
Is that ID valid? How do you know?
Is the ID an integer or a string?
Does ID exists in the request?
and with these 3 questions, you'll end up having validations + redirect to different methods and if it's an ID of interest to a database query, you'll have database code in there aswell.
The normal procedure I like to think is when it hits the route, it should hit Authorization and Authentication (middleware as Bak87 said). In there, you can validate if he's authenticated, if he's a certain user, whatever you'd like.
Afterwards this initial validation, you can redirect it to a certain method in a certain controller depending on your needs, however, I wouldn't advise as a class should have a single purpose according to some standards (but in the end, you can build the application how you want it).
I believe a route or a group of routes should have an middleware (for whatever primary validation you require of the person making the request), and each route should point to a single method in a controller. Once it reaches the controller, instead of having (Request $request) as the parameters for the method, you can have your own custom FormRequest, where you can validate the ID if you'd like.
If FormRequest isn't of interest, you can use Eloquent (if the ID you're looking for is related to it) FindOrFail to validate if it exists (if it doesn't, returns a 404 error not found, if you have a 404.blade.php file). This way, by the time it reaches the controller's method, it has been validated by sections, where routes are then protected by the main Authorization and Authentication, FormRequest to do the input's validation and you can specifically return whatever you'd like from the controller's method.
Obviously we don't know what is the view your returning but if slighty differs from each other, consider refactoring it in order to return only 1 view, composed of other blades
So I find myself creating form request to validate the request that has been posted from a form. And sometimes it gets too complex that Laravel's Validation Rules can't help, so I make another validation in the data service (which is imported in the Controller).
I'll give an example to make it much clearer:
A writer posts and article. The data request is being processed at App\Http\Requests\Article\CreateArticleRequest. After the validation verifies it's valid, the request is being forwarded to the controller. In the controller, I send the request to the ArticleService for the business logic. So far so good.
But! what if I want to make some specific validation on my own that Laravel's validation rules can't help me. because then I'll have to load a repository for complex queries.
So the big issue here is that I "double check" the request instead of one time. So I thought about merging my 2 authorization (1 from the \Request and second from my Service). But to achieve that, I'll have to load repositories that are bound to their interface. So what's your solution?
When extending the validator factory with a new rule you can pass it a closure that inherits (see Example #3 here) any dependencies that are required.
So in your case it would be something like this:
public function boot()
{
$repository = $this->app->make(Repository::class);
Validator::extend('foo', function ($attribute, $value, $parameters, $validator) use ($repository) {
return /* your validation logic */
});
}
As for the authorization you can simply type-hint the necessary dependencies in your authorize method signature:
public function authorize(Repository $repository)
{
return /* your authorization logic */
}
I am writing route and controller rules for a web application. In a number of rules, a problem has emerged, which is that I need to match both GET and POST verbs, and send them to the controller, but different methods.
I considered using Route::controller('tracking', 'TrackingController') for this, but then it requires different names for each internal route, whereas I want to specify one name for both. Besides, I've read nothing but negativity regarding the usage, suggesting that it is not a good idea.
Here's what I have currently:
Route::match(['get', 'post'], '/tracking', [
'as' => 'tracking',
'uses' => 'TrackingController#index'
]);
While implementing this, I have discovered that I need to have two controller methods, index and track. How can I efficiently route GET to index and POST to track, while maintaining the same controller (TrackingController) and the same name (tracking)?
I considered using two separate routes, e.g. Route::get and Route::post, but that doesn't feel very eloquent.
you can use easily Route Controller,like this
Route::controller('tracking', 'TrackingController')
In here if you wanna use same method for both get and post,just use any prefix in method,like
//for both get and post
public function anyUrl();
//only get
public function getUrl();
//only post
public function postUrl();
Or use
Route::any('/url', function () {
return 'Hello World';
});