Detect if action is show or edit in Laravel 5.2 - php

I have a middleware that detects if a user owns a tournament.
So, if user want to edit a tournament he doesn't own, he will get a 403.
Thing is I can't make difference between laravel.dev/tournament/1/edit, and laravel.devl/tournament/1
Off course, I could check the "edit" word in URL, but I would prefer other better param...
I tried method param in Request Object, but it is giving me GET for both, so I can't make difference...
Any Idea???

In your case, you can do like this:
$request->route()->getName();
Now you can do your logic based on this.

What about using a different HTTP method for edit, e.g PATCH or PUT and declaring two different routes, something like:
Route::get('laravel.devl/tournament/1', 'TournamentController#showTournament');
Route::put('laravel.dev/tournament/1/edit', 'TournamentController#editTournament');
Then in the TournamentController you can check if the user has rights to edit.

It sounds like you should just use route specific middleware instead of global middleware. See https://laravel.com/docs/master/middleware#assigning-middleware-to-routes. Then you can just do:
Route::get('/tournament/{id}/edit', ['middleware' => ['tournamentOwner'], function () {
//
}]);
Route::get('/tournament/{id}', ['middleware' => [], function () {
//
}]);
If it's a RESTful route, you can just do:
public function __construct()
{
$this->middleware('tournamentOwner', ['only' => ['edit']]);
}

Related

Declare same route twice but expect different behaviour according to a middleware

I started creating a REST API using the lumen framework and wanted to set up a particular behaviour for my GET /user route. Behaviour is the following:
If the request come from an authenticated user (using auth middleware), the method getAllFields from UserController is called and return all the data from the user
If it's not the case, the method get from UserController is called and return some of the data from the user
It seems logic to me to just write it like that in my web.php using a simple middleware:
<?php
$router->group(['middleware' => 'auth'], function () use ($router) {
$router->get('/user/{id}', [
'uses' => 'UserController#getAllFields'
]);
});
$router->get('/user/{id}', [
'uses' => 'UserController#get'
]);
But for some reason, even if the middleware is correct, I always get the response of the second route declaration (that call get()). I precise that if I remove the second route declaration, the one in the middleware work as expected.
Have someone an idea how I can achieve something similar that work?
Router will check if your request matches to any declared route. Middleware will run AFTER that match, so You cannot just return to router and try to find another match.
To fallow Laravel and Routes pattern - You should have single route that will point to method inside controller. Then inside that You can check if user is logged or not and execute getAllFields() from that controller. It will be not much to rewrite since You are currently using UserController in both routes anyway.
web.php
$router->get('/user/{id}', 'UserController#get');
UserController.php
public function get()
{
return auth()->check() ? YourMethodForLogged() : YourMethodForNotLogged();
}
Or if there is not much logic You can keep this in single method.
Also it is good idea to fallow Laravels REST standards (so use show instead of get, "users" instead of "user" etc - read more https://laravel.com/docs/7.x/controllers)
web.php
$router->get('/users/{user}', 'UserController#show');
UserController.php
public function show(User $user)
{
if (auth()->check()) {
//
} else {
//
}
}
To summary - for your needs use Auth inside controller instead of middleware.
To check if user is logged You can use Facade Auth::check() or helper auth()->check(), or opposite Auth::guest() or auth()->guest().
If you are actually using Lumen instead of full Laravel then there is not auth helper by default (You can make own or use package like lumen-helpers) or just keep it simple and use just Facades instead (if You have then enabled in Lumen).
Read more https://laravel.com/docs/7.x/authentication and https://lumen.laravel.com/docs/7.x/authentication
This pattern is against the idea of Laravel's routing. Each route should be defined once.
You can define your route without auth middleware enabled and then define your logic in the controller.

Laravel - How to restrict certain functionality based on user authentication?

Laravel newbie here (obviously :D ). I've set up a new model & controller for a model named Pages.
Every User has many Pages.
Each Page has a single User.
I've created the following functioning controller actions (& views):
PagesController::index
PagesController::create
PagesController::store
PagesController::show
PagesController::edit
PagesController::delete
So you can edit a Page by going to url.dev/pages/{id}/edit.
The problem is, you can access all of these routes regardless of your session status. So random users can edit any given Page. Which, obviously, is terrible.
Can anyone point me in the direction of what I should read up on, to limit the ability to access my model's controller actions based on whether or not the user is logged in (and if it's the correct user, at all)?
To force a specific route to be only accessible by authenticated users you can specify middleware auth in the controller constructor, like so:
public function __construct()
{
$this->middleware('auth');
}
Also you can restrict which methods you want auth to be applied to in the controller, using the only or except parameters. Using only you could do:
public function __construct()
{
$this->middleware('auth', ['only' => ['create', 'store', 'edit', 'delete']]);
}
You´re looking for middleware..
You can read more here
public function __construct()
{
$this->middleware('auth')->only('index');
$this->middleware('admin')->except('store');
}
Other answers are good, but I prefer to use middleware on route groups.
So when I have several routes like this:
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
I would add them inside Laravel Route group. Like this:
Route::group(['middleware' => 'auth'], function() {
Route::get('pages', 'PagesController#index')->name('pages.index');
Route::get('pages/{id}/edit', 'PagesController#edit')->name('pages.edit');
});

Using a different controller for GET and POST in Laravel but using the same name

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';
});

Laravel sub-domain routing without passing variable to URL::route()

In a Laravel 4 app, i'm using subdomain routing around a bunch of Route::resource's like this:
Route::group(['domain' => '{account}.my.app'], function()
{
Route::group(['before' => 'auth'], function($account)
{
Route::resource('organisations', 'OrganisationsController');
Route::resource('clients', 'ClientsController');
Route::resource('domains', 'DomainsController');
});
});
In my auth filter i'm doing the following:
Route::filter('auth', function($route)
{
// you could now access $account in a controller if it was passed as an argument to the method
$account = $route->getParameter('account');
// share account variable with all views
View::share('account', $account);
// Auth::guest Returns true if the current user is not logged in (a guest).
if (Auth::guest()) return Redirect::guest('login');
});
Within my views I can now access $account, but if I want a call to URL::route() to be correct I have to manually pass the account variable, like URL::route('clients.show',['account' => $account]) otherwise it generates URLs like %7Baccount%7D.my.app.
This is a bit of a pain and doesn't seem that elegant, is there any other or better way to achieve this? I guess I could create my own route helper to use instead of the built-in one.
However, I also do redirects with Redirect::route() within controllers so I would also need to make updates here.
EDIT
As suggested in the comments it may be that extending the Route API is the best approach here. Does anyone have any suggestions how this should be done?
Thanks.

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

Categories