Laravel access route parameter in route web.php file - php

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

Related

How to prevent users from checking not existing urls, how to hide routes in Laravel 8.*

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

Laravel - Validate inside Controller without make CustomRequest

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();

Change target controller/action from middleware

I have two resources /test and /blabla.
For the /test I have registered middleware. In the middleware, based on certain condition, I would like to redirect current call to the controller/action which serve /blabla resource, transparently for user (no any extra client request, no 302 status response code etc..). How can I achieve it ?
It seems like your solution might be better suited in the routes file. You are suggesting serving a different route given a certain condition.
So in your routes.php file:
Route::get('test', function(){
if($condition){
return App::make('App\Http\Controllers\TestController')->index();
} else {
return App::make('App\Http\Controllers\BlaBlaController')->index();
}
});
If you still want to handle it in the middleware you should be able to do the same thing as above:
return App::make($controller)->index(); // or whatever controller method you want to call.
If you need both sets of middlewares to be called, then inside the constructor (after calling your middlewares) check your condition and call the other controller like above.
If you want to change the users url, I don't think there's any way other than returning a redirect. Most users don't notice the redirect so it will probably seem "transparent."
In that case, your middleware function looks like:
public function handle($request, Closure $next)
{
if (something == thingy) {
return redirect('/blabla');
}
return $next($request);
}

Laravel and routes with multiple "nested" tasks and parameters

I have just started tinkering with Laravel (PHP newbie alert) and I have a doubt on how to implement my REST JSON APIs on this framework.
The desired URL path should be:
http://api.mysite.com/APIKEY/FUNCTION/(ARGUMENTS)*
But what is the best way to do it?
I did not find any enough explanatory guide, because it is assumed that each feature (authentication, search, and so on) are on different routes.
At the moment I managed to create migrations and models for the needed data and I also set a route this way:
Route::get('/{apikey}/{function}/{arg1}/{arg2}/{arg3?}',
//The first two arguments are mandatory, the 3rd optional
function($apikey,$function,$arg1,$arg2)
{
return Response::json(array(
'status'=>'200'),
200);
})
->where(array('function'=>'[A-Za-z]+'));
This should be the correct action flow, but I have some doubts.
Check that apikey is valid, if not return a 404 json response. How do I call such function, where do I define it?
If key check is successful, understand which function is needed (should I use a switch construct or is there a better way to implement this, like a route group? In all examples with Route::group there is a static prefix, but here the prefix is apikey, a variable)
Return data - if available - getting it from the database. I suppose that for each function i should code a specific controller that gets data from the database using the Models.
#1. I think I'd probably use a route filter for this, like so:
Route::filter('apikey', function(){
if (Shared\API::checkIfKeyIsValid(Input::get('key') != true){
return Shared\Errors::handleError("bad-api-key");
}
});
You can see this filter checks to make sure some session variables are set and match, and if it returns ANYTHING, that's failing, and it won't send the user where the route normally goes. You'd call it in the route like so:
Route::get('play/{id}', array('before' => 'loggedin', 'uses' => 'ThingController#doThing'));
#2. I think a route group is how I'd structure this (if I understand what you're asking).
Edit: You've changed the question since I answered, regarding the prefix being a variable. I'd reorder your arguments so the API key is at the end, or you could take a look at this, which might do what you want: https://github.com/jasonlewis/enhanced-router
Route::group(array('prefix' => 'api'), function()
{
//the routes for all API calls
Route::group(array('prefix' => '/v1'), function()
{
//for version 1
Route::group(array('prefix' => '/thing', 'before' => 'loggedin'), function()
{
//for things only
Route::get('/getThing/{id}', 'APIController#getThing');
Route::get('/getDifferentThing/{id}/{aux}/{optional?}', 'APIController#getDifferentThing');
});
});
});
#3. The returning of the data should be done via your controller with the data coming from the model. Either return it as a view, or just return it as JSON like so:
return Response::json($data);

How can I set a specific method of aLaravel 4 resource route in my ACL

I have a fairly simple ACL system set up. The filter checks if the user is part of a user group which has access to the route or if the user has access to the route. It works for individual routes and works for resources in general. However I want some users to have access to a specific method of a resource route, but not all of it. for example, user1 is part of the admin group and always has access to the admin resource route but user2 is not a part of the admin user group and I want to give him access to teh resource admin/create. How can I go about this in Laravel 4 with my setup
Database
Routes:
id
route
created_by
last_editted_by
created
updated
deleted_at
acl (table it looks at to see if user has access)
id
routes_id
user_id
group_id
created
updated
deleted_at
Filter
if (Auth::check()){
$route = Request::segment(1);
$user_id = Auth::user()->id;
$acl_count = Acls::join('routes','routes.id','=','acl.routes_id')
->where('routes.route','=',$route)
->Where(function($in_parenthesis) use($user_id){
$in_parenthesis->whereIn('acl.group_id',function($where_in) use($user_id){
$where_in->select('group_id')
->from('user_group_junction')
->where('user_id','=',$user_id);
})
->orWhere('acl.user_id','=',$user_id);
})
->count();
if($acl_count < 1){
return Redirect::to('/');
}
}else{
return Redirect::to('/');
}
Routes
Route::get('/','HomeController#index');
Route::get('login','AuthorizationController#loginForm');
Route::post('authenticate','HomeController#authenticate');
Route::get('logout','HomeController#logout');
Route::group(array('before'=>'auth'),function(){
Route::group(array('before'=>'user_permission'),function(){
Route::get('protected','HomeController#protectedPage');
Route::resource('sources', 'SourcesController');
Route::resource('admins', 'AdminsController');
});
});
You can use beforeFilter inside the __construct method of the AdminBaseController like this (create a different one for admin controllers only)
class AdminController extends AdminBaseController {
function __construct() {
// Use filter on all methods but not on create
$this->beforeFilter('admin', array('except' => array('create')));
}
}
Also, you may directly use this beforeFilter inside your resource controller and use except or you can use only (only works reverse of except, allows access to all but filters only mentioned methods in the array). You can also check conditions inside the constructor method s well.
I figured out that part of my problem is with the filter. I am only looking at the first segment of the url which doesn't work correctly if my route (whether it is a resource or just a route with a "/" in it) won't work. Thus I asked another question located here

Categories