Laravel routes with unlimited parameters for RESTful API - php

I am building a RESTful API using Laravel 5.1.
The default route would be api. The user is allowed to create a url service using as many as parameters as she wants let's say .../api/p1/p2/.../pn.
How do I make a single route to point to a single Controller, so the service will be handled in a single controller?
Note : At first the application just needs to know whether the service exists or not by comparing the url with stored service in the database. As for the service itself, it can be queried into the database later.
I read that we can use * in Laravel 4, how about Laravel 5.1 ?
I have tried :
Route::resource('/api/*', 'APIServiceController'); but it does not work for unlimited parameters
or is it possible to do it like this
Route::group(['prefix' => 'api'], function () {
//what should I put in the closure, how can I redirect it to a single controller
});

Write your route as below:-
Route::group(['prefix' => 'api'], function () {
// this route will basically catch everything that starts with api/routeName
Route::get('routeName/{params?}', function($params= null){
return $params;
})->where('params', '(.*)');
});
Redirect to controller,
Route::group(['prefix' => 'api'], function () {
Route::get('routeName/{params?}', 'YourController#action')->where('params', '(.*)');
});
If you want to make routeName dynamic then just write it in curly bracket as below:-
Route::get('{routeName}/{params?}', 'YourController#action')->where('params', '(.*)');
Hope it will help you :-)

You can try this trick
Route::get('{pageLink}/{otherParams?}', 'IndexController#get')->where('otherParams', '(.*)');
You should put it on the end of routes.php file as it is like a 'catch all' route.
class IndexController extends BaseController {
public function get($pageLink, $otherParams = null)
{
if($otherParams)
{
$otherParams = explode('/', $otherParams);
}
}
}

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 get route current domain in multiple domains route

I want to prevent access to some of my app routes from other domain except listed. It success using below code:
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
But the problem is when I call {{route('home')}}, the URL always becomes the domain at the last line of the routes.php(at above case is http://localhost ). How to make it to current domain?
My current solution:
if (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain1.com") {
Route::domain('domain1.com')->group($loginRoutes);
}elseif (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain2.com") {
Route::domain('domain2.com')->group($loginRoutes);
}
It's work but I think it's dirty. I have a lot of domains/subdomain and also the routes too.
I need solution on route directly, because I have a lot of routes, if I update each controller it's will take a long time. Maybe edit route provider or laravel vendor code is also no problem.
I am also using PHP 7.3 and Laravel 5.7
I actually use this routing for my domains.
Maybe this is not exactly what you asked, but you can try something like this
// To get the routes from other domains
// Always add the new domains here
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(array('domain' => 'domain1.com'), $loginRoutes);
Route::group(array('domain' => 'domain2.com'), $loginRoutes);
Route::group(array('domain' => 'domain3.com'), $loginRoutes);
If you want to handle something at the domain level. In your controller (HomeController#index), you can get the current domain and do whatever you want. To get exact domain I have used like this:
class HomeController extends Controller
{
public function index()
{
$domain = parse_url(request()->root())['host'];
if ($domain == 'domain1.com'){
// do something
}
...
}
...
}
That way I can handle different things for each domain.
Just to make it more complete, we can take the domains from a table/query and dynamically create the routes.
$domains = Cache::get('partners')->where('status', '=', 'A')->where('domain', '<>', '')->all();
$loginRoutes = function() {
Route::get('/', 'HomeController# index')->name('home');
};
foreach ($domains as $domain) {
Route::group(array('domain' => $domain->dominio_externo), $loginRoutes);
}
It has been working for me. I hope to help you.
You can maybe try something like this :
Route::pattern('domainPattern', '(domain1.com|domain2.com|localhost)');
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(['domain' => '{domainPattern}'], $loginRoutes);
If I understand your issue, you just want to filter domains. Using regex, you can do it. You could try the following code:
Route::domain('{domain}')->group($loginRoutes)->where('domain', '\b(domain1\.com|domain2\.com|localhost)\b');
Details:
\b: we get exactly the string.
\.: in regex, the character . means any character. So, we have to escape . using backslash.
Note:
You might get an error, because I can not check the results. Let me know any errors you encounter.
I want to prevent access to some of my app routes from other domain
except listed. It success using below code:
I think you are right with your thoughts about a better, more laravel-core based solution for this problem.
Every route handling method you define in a controller file recieves a request. In standard laravel this is an object of type Illuminate\Http\Request.
You can extend this class with a custom class - let's say "AdminRequest". This extended class than offers authorization methods which will check if the Auth:user has the correct role, session values or whatever you want in your app.
I guess this is more flexible and clean - in your controller you only have to change the definition of the request you recieve in that controller method. Validation messages and everything else can be wrapped in the custom request class.
See this also:
How to Use custom request (make:request)? (laravel) Method App\Http\Requests\Custom::doesExistI does not exist
Extend Request class in Laravel 5
for preventing access to a certain route, its a bad design to inject a Route into these structure:
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
since it defines route multiple time, and only the last will be override the others.
you can check this by php artisan route:list .
the laravel way to handle this situation (access management ) is to use middleware
class DomainValid
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$domain = $request->server->get('SERVER_NAME');
if (in_array($domain , ['domain1.com','domain2.com','localhost'] )) {
return $next($request);
}
abort(403);
}
}
and use it like this:
use App\Http\Middleware\DomainValid;
Route::get('/', 'HomeController#index')->name('home')->middleware(DomainValid::class);
so it will be only ONE home route.

Laravel 5.2: Creating a custom route function - extending route

So basically, I want to create my own Route::custom function.
This is because I've been using the same groups and middleware for several routes throughout the site (I'm also using modules with subdomains, so we're talking about saving 5-6 lines of code per route)
All I want is for Route::custom to just call two or three other Route functions. For example:
Route::Module('forum') to be replaced with
Route::group(['middleware' => ['web','auth'], 'domain' => 'forum.' . env('SITE_DOMAIN', 'example.com')], function () {
Route::group(['middleware' => 'permission:access.forum'], function () {
Route::get('/', function () {
return view('forum::forum.index');
})->name("forum.index");
});
});
You can extends laravel default facade then add static method as you want.
Notice: You must replace route facade config in config/app.php to your custom facade class.
Example here
I do not understand properly question 1. But for question 2, try this:
Go to app/Providers/RouteServiceProvider.php. Look for the function mapWebRoutes(). The line
require base_path('routes/web.php');
Duplicate it and change so you now have :
require base_path('routes/web.php');
require base_path('app/User/route.user.php');
require base_path('app/Whatever/route.whatever.php');
I guess this will resolve for your problem

How to use wild card in admin routing in laravel 5

I am using code that is below for admin routing in laravel.
Route::group(['prefix' => 'admin'], function() {
Route::get('/', 'Admin\AdminController#home');
Route::get('/users/userList', 'Admin\UserController#userList');
Route::get('/users/detail', 'Admin\UserController#detail');
Route::get('/posts/view', 'Admin\PostController#view');
Route::get('/posts/edit', 'Admin\PostController#edit');
Route::get('/posts/add', 'Admin\PostController#add');
});
This is working fine for me. But when I add new functions in code for that I have to write routing in routes file. For example: If I want to add edit functionality in users controller, for that I have to add new route like .
Route::get('/users/edit', 'Admin\UserController#edit');
So I have to add routing for each function.
I want to know How to use wild card for admin routing so that I have to write routing only for controller not for each function for example.
Route::group(['prefix' => 'admin'], function() {
Route::get('/', 'Admin\AdminController#home');
Route::get('/users/:any', 'Admin\UserController#:any');
Route::get('/posts/:any', 'Admin\PostsController#:any');
});
wild card replace the function name, and auto ridirect to that function.
You could use implicit controllers that will do what you need.
First declare a route for your implicit controller
Route::controller('users', 'UserController');
Then, on your controller, you have to follow a convention for naming your routes with HTTP verbs used to access them (get for GET, post for POST, any for both)
class UserController extends Controller {
public function getIndex()
{
//
}
public function postProfile()
{
//
}
public function anyLogin()
{
//
}
}
A note about composed method name from documentation
If your controller action contains multiple words, you may access the action using "dash" syntax in the URI. For example, the following controller action on our UserController would respond to the users/admin-profile URI:
public function getAdminProfile() {}

How can I use "where" in a Laravel controller

If I have the next route:
Route::get('/user/{id}', function($id) {
return View::make(users.profile, array('id' => $id));
})->where(array('id' => '[0-9]+'));`
How could I do the same in a Restful controller?
Route::controller('/user', 'UserController');
My UserController:
class UserController extends BaseController {
public function getProfile($id) {
return View::make('users.profile', array('id' => $id));
}
}
Thanks for your attention.
where doesn't appear to work when chained onto a Route::controller call, but you can achieve the same functionality with the Route::pattern declaration. So, for example, this code for a Route::controller (called "implicit routing") would work, limiting id to numeric:
Route::pattern('id', '\d+');
Route::controller('/user/{id}', 'UserController');
Then, in UserController, the getIndex method would be called from a GET request:
class UserController extends BaseController {
public function getIndex($id) {
return View::make('users.profile', array('id' => $id));
}
}
Note, however, that this only works for the index method, i.e. for calls to http:://example.com/user/99. If you want to use other controller methods using "implicit routing", for example http:://example.com/user/profile/99 and the controller method getProfile($id), you need to declare your route without the {id} parameter, like so:
Route::controller('/user', 'UserController');
...in which case, you aren't able to use ->where or Route::pattern to constrain the {id}, since there is no {id} parameter to constrain.
In the end, you're better off going with "explicit routing," as you do at the beginning of your answer, or using RESTful Resource Controllers (see the docs) and specifying your route as:
Route::resource('user', 'UserController');.
If you subscribe to Laracasts, Jeffrey Way has a great, clear tutorial about some of the perils of "implicit routing" here.
To make sure about the methods and URL run the php artisan routes from your terminal so you'll get the list of all routes you have access to with their URL. In this case for the following route and controller you may find a URL like user/profile/10
// Route
Route::controller('/user', 'UserController');
// Controller
class UserController extends BaseController {
public function getProfile($id) {
return View::make('users.profile', array('id' => $id));
}
}
So use http://domain.com/user/profile/10, here 10 will be passed to the $id variable in your profile method. Also remember that, in RESTfull controller each method should be prefixed with the HTTP verb they responds to so in this case this method will respond to a GET request.
In order to do it you want to wrap your Route::controller statements in a group and apply where pattern for the group, since setting it globally might not be accurate for other routes:
Route::group('where' => ['id' => '\d+'], function () {
Route::controller('users', 'UsersController');
// other restful controller definitions with this pattern go here
}

Categories