So basically my app has two types of dynamic url..
app.com/{page}
app.com/{user}
Both having their own controllers
PageController#index
User\ProfileController#index
But I'm struggling to get this working.
I have tried a few different methods. Here are two I have tried..
Route::get('{slug}', function($slug) {
if (App\Page::where('slug', $slug)->count()) {
// return redirect()->action('PageController#index', [$slug]);
// return App::make('App\Http\Controllers\PageController', [$slug])->index();
return 'Page found';
} else if (App\User::where('username', $slug)->count()) {
// return redirect()->action('User\ProfileController#index', [$slug]);
// return App::make('App\Http\Controllers\User\ProfileController', [$slug])->index();
return 'User found';
} else {
return abort(404);
}
});
I feel I should be doing this with middleware/filters. Any help would be great. Thanks.
I think you could achieve what you after with Route::group using a middleware to filter if it is a page or a user.
Route::group(['middleware' => 'isPage'], function () {
Route::get('{slug}', ['as'=> 'pages.show', 'uses' => 'PageController#show']);
});
Route::group(['middleware' => 'isUser'], function () {
Route::get('{slug}', ['as'=> 'users.show', 'uses' => 'User\ProfileController#show']);
});
If you were using slugs for the Pages and ids for the Users, your idea of handling the issue might make more sense, but since you are using slugs for both the pages and the users, I strongly suggest you try a different approach. Why not declare two routes? Why not use the "show" methods of the respective controllers while you are at it, and keep in line with conventions for resources?
Route::get('pages/{slug}', ['as'=> 'pages.show', 'uses' => 'PageController#show']);
Route::get('users/{slug}', ['as'=> 'users.show', 'uses' => 'User\ProfileController#show']);
And if you really want to keep your "root-slug-respective-redirect" functionality you could write afterwards:
Route::get('{slug}', function($slug) {
if (App\Page::where('slug', $slug)->count()) {
return redirect(route('pages.show', $slug));
} else if (App\User::where('username', $slug)->count()) {
return redirect(route('users.show', $slug));
}
return abort(404);
});
I do advise against it though, as it seems like a waste of queries.
Here are the docs on Laravel RESTful resource controllers for good measure.
Related
I have a project which have multiple subdomains.
for example I have a subdomain for Students which goes to a student controller and it looks like this:
Route::domain('students.domain.test')->group(function () {
Route::get('/', function () {
return "done reaching the students page";
});
});
The second type of domains is "domain.test" and any subdomain which I'm checking in the request level and that's fine too.
Route::get('/', [HomeController::class, 'index'])->name('index');
But before the second type of domains I want to make subdomain for specific types of Entities which I have in the database.
Route::domain('{someTypes}.domain.test')
->group(function () {
Route::get('/', function () {
return "done reaching SomeTypes Page";
});
});
My Entity table have these attributes: Id, Title, Type "which I want to check if the type is 5".
I tried to use the middleware:
public function handle($request, Closure $next, ...$types)
{
$currentEntity = app('current_entity');
if ($currentEntity->entityType()->whereIn('title->en', $types)->exists()) {
return $next($request);
}
abort(404, 'Sorry, Request Not Found');
}
and I applied it to my routes like this:
Route::group([
'middleware' => ['type:journal']
],function () {
Route::get('/', function(){
return 'journals logic goes here';
});
});
and I have another middleware to ignore types like this:
public function handle($request, Closure $next, ...$types)
{
$currentEntity = app('current_entity');
if ($currentEntity->entityType()->whereIn('title->en', $types)->exists()) {
abort(404, 'Sorry, Request Not Found');
}
return $next($request);
}
and applied it to the other routes like this:
Route::group([
'middleware' => ['except_entity:journal']
], function(){
Route::get('/', function(){
return 'default pages when journals fails';
})->name('index');
I hope its clear what I'm trying to achieve.
First, you need a check what version laravel that you used?
You need to use Middleware. And I think, method to code with laravel 6, 7, or 8, is a little bit different.
Can you give us more information about your code, so we can help it easier?
Lets assume I have a site with cars: cars.com on Laravel 5.
I want to set up my routes.php so a user could type in a browser ford.cars.com/somethingOrnothing and get to the controller responsible for Ford™ cars (FordController).
Of course I could use something like this code:
Route::group(['middleware' => 'web'], function () {
Route::group(['domain' => 'ford.cars.com'], function(\Illuminate\Routing\Router $router) {
return $router->resource('/', 'FordController');
});
});
But I am not happy about writing and maintaining routes for hundreds of car brands.
I would like to write something like this:
Route::group(['domain' => '{brand}.cars.com'], function(\Illuminate\Routing\Router $router) {
return $router->get('/', function($brand) {
return Route::resource('/', $brand.'Controller');
});
});
So the question is: Is it possible to dynamically set routes for sub-domains and how to achieve this?
upd:
the desirable outcome is to have subdomains that completely repeat controllers structure. Like Route::controller() did (but it is now deprecated)
To emulate Route::controller() behaviour you could do this:
Route::group(['domain' => '{carbrand}.your.domain'], function () {
foreach (['get', 'post'] as $request_method) {
Route::$request_method(
'{action}/{one?}/{two?}/{three?}/{four?}',
function ($carbrand, $action, $one = null, $two = null, $three = null, $four = null) use ($request_method) {
$controller_classname = '\\App\\Http\\Controllers\\' . Str::title($carbrand).'Controller';
$action_name = $request_method . Str::title($action);
if ( ! class_exists($controller_classname) || ! method_exists($controller_classname, $action_name)) {
abort(404);
}
return App::make($controller_classname)->{$action_name}($one, $two, $three, $four);
}
);
}
});
This route group should go after all other routes, as it raises 404 Not found exception.
Probably this is what you need:
Sub-Domain Routing
Route groups may also be used to route wildcard sub-domains.
Sub-domains may be assigned route parameters just like route URIs,
allowing you to capture a portion of the sub-domain for usage in your
route or controller. The sub-domain may be specified using the domain
key on the group attribute array:
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
From:
Laravel 5.2 Documentation
upd.
If you want to call your controller method you can do it like this:
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
$controllerName = $account . 'Controller' //...or any other Controller Name resolving logic goes here
app('App\Http\Controllers\\' . $controllerName)->controllerMethod($id);
});
});
I am using Hashid to hide the id of a resource in Laravel 5.
Here is the route bind in the routes file:
Route::bind('schedule', function($value, $route)
{
$hashids = new Hashids\Hashids(env('APP_KEY'),8);
if( isset($hashids->decode($value)[0]) )
{
$id = $hashids->decode($value)[0];
return App\Schedule::findOrFail($id);
}
App::abort(404);
});
And in the model:
public function getRouteKey()
{
$hashids = new \Hashids\Hashids(env('APP_KEY'),8);
return $hashids->encode($this->getKey());
}
Now this works fine the resource displays perfectly and the ID is hashed.
BUT when I go to my create route, it 404's - if I remove App::abort(404) the create route goes to the resource 'show' view without any data...
Here is the Create route:
Route::get('schedules/create', [
'uses' => 'SchedulesController#create',
'as' => 'schedules.create'
]);
The Show route:
Route::get('schedules/{schedule}', [
'uses' => 'Schedules Controller#show',
'as' => 'schedules.show'
]);
I am also binding the model to the route:
Route::model('schedule', 'App\Schedule');
Any ideas why my create view is not showing correctly? The index view displays fine.
Turns out to solve this, I had to rearrange my crud routes.
Create needed to come before the Show route...
There's a package that does exactly what you want to do: https://github.com/balping/laravel-hashslug
Also note, that it's not a good idea to use APP_KEY as salt because it can be exposed.
Using the above package all you need to do is add a trait and typehint in controller:
class Post extends Model {
use HasHashSlug;
}
// routes/web.php
Route::resource('/posts', 'PostController');
// app/Http/Controllers/PostController.php
public function show(Post $post){
return view('post.show', compact('post'));
}
Goal: I want to make route filter in Laravel 4 using Route::group and Route::filter
Description
I have 2 types of user :
Internal
Distributor
For, Internal, I have 2 groups:
admin
regular
For Distributor, I have 4 groups:
gold
silver
bronze
oem
Eligible Route
OEM Distributor are eligible for only 5 routes.
Route::get('distributors/{id}', array('before' =>'profile', 'uses'=>'DistributorController#show'));
Route::get('distributors/{id}/edit', 'DistributorController#edit');
Route::put('distributors/{id}/update', array('as'=>'distributors.update', 'uses'=>'DistributorController#update'));
Route::get('catalog_downloads','CatalogDownloadController#index');
Route::get('catalog_downloads/{id}/download','CatalogDownloadController#file_download');
Regular Distributor are eligible for 8 routes.
Route::get('distributors/{id}', array('before' =>'profile', 'uses'=>'DistributorController#show'));
Route::get('distributors/{id}/edit', 'DistributorController#edit');
Route::put('distributors/{id}/update', array('as'=>'distributors.update', 'uses'=>'DistributorController#update'));
Route::get('catalog_downloads','CatalogDownloadController#index');
Route::get('catalog_downloads/{id}/download','CatalogDownloadController#file_download');
Route::get('marketing_materials','MarketingMaterialController#index');
Route::get('marketing_materials/{id}/download/thumb_path','MarketingMaterialController#thumb_download');
Route::get('marketing_materials/{id}/download/media_path','MarketingMaterialController#media_download');
Code
filters.php
routes.php.
Questions
Can someone please help me or at least direct me to the right direction ?
First off: It's not possibble to declare a route that results in the same URL twice. Whether it's in a group or not. (Well if you have a group with prefix it's possible because a prefix changes to URL of the route)
You have to solve this problem by intelligent filtering
This is the simplest solution I've come up with:
Route::filter('distributor', function(){
$user = Auth::user();
if($user->type == "Distributor"){
return true;
}
if (Request::ajax()){
return Response::make('Unauthorized', 404);
}
return View::make('errors.404_auth');
});
Route::filter('distributor.regular', function(){
$user = Auth::user();
if($user->type == "Distributor"){
if($user->distributor()->type != 'OEM'){
return true;
}
}
if (Request::ajax()){
return Response::make('Unauthorized', 404);
}
return View::make('errors.404_auth');
});
The distributor filter checks just if the user is of type Distributor. The second filter, distributor.regular, checks if the distributor is not an OEM. (If you're wondering, the dot in distributor.regular has no special function or deeper meaning. I just like to write it like that)
Route::group(['before' => 'distributor'], function(){
Route::get('distributors/{id}', array('before' =>'profile', 'uses'=>'DistributorController#show'));
Route::get('distributors/{id}/edit', 'DistributorController#edit');
Route::put('distributors/{id}/update', array('as'=>'distributors.update', 'uses'=>'DistributorController#update'));
Route::get('catalog_downloads','CatalogDownloadController#index');
Route::get('catalog_downloads/{id}/download','CatalogDownloadController#file_download');
Route::group(['before' => 'distributor.regular'], function(){
Route::get('catalog_downloads', 'CatalogDownloadController#index');
Route::get('catalog_downloads/{id}/download', 'CatalogDownloadController#file_download');
Route::get('marketing_materials', 'MarketingMaterialController#index');
Route::get('marketing_materials/{id}/download/thumb_path', 'MarketingMaterialController#thumb_download');
Route::get('marketing_materials/{id}/download/media_path', 'MarketingMaterialController#media_download');
});
});
This should already work with the use-cases you posted. However we can make the filters more flexible and also reduce redundant code.
function makeError404(){
if (Request::ajax()){
return Response::make('Unauthorized', 404);
}
return View::make('errors.404_auth');
}
Route::filter('distributor', function(){
$user = Auth::user();
if($user->type == "Distributor"){
return true;
}
return makeError404();
});
Route::filter('distributor.group', function($route, $request, $value){
$groups = explode(';', $value);
$user = Auth::user();
if($user->type == "Distributor"){
if(in_array($user->distributor()->type, $groups)){
return true;
}
}
return makeError404();
});
Now we can dynamically specify in which group the user has to be...
Route::group(['before' => 'distributor'], function(){
// distributor routes
Route::group(['before' => 'distributor.group:gold;silver;bronze'], function(){
// regular routes
});
});
You can follow a path like this
class UserController extends BaseController {
/**
* Instantiate a new UserController instance.
*/
public function __construct()
{
$this->beforeFilter('employee', array('only' => 'index'));
}
}
I am using Laravel 4. When "logged_in_id" is found in session I want it to continue to parse through bellow written Routes.
Route::get('/{anything}', function($anything)
{
if(!Session::has('logged_in_id')) {
return View::make('user.login');
} else {
//continue to check Route::get written bellow this Routing
}
})->where('anything', '[A-Za-z0-9\/?=]+');
If I write Redirect::to('/'.$anything) then it enters the same Route and keeps on redirecting in loop. Is there any way to solve this problem?
I would create a filter and then apply it to any routes that need it.
Remember to put first the routes that are more restrictive and last the more generic ones.
See the example:
Route::filter('logged_in', function()
{
if(!Session::has('logged_in_id')) {
return View::make('user.login');
}
});
Route::get('testing', array(
'before' => 'logged_in',
function()
{
return View::make('user.testing');
}
));
Route::get('/{anything}', array(
'before' => 'logged_in',
function($anything)
{
return View::make('user.anything');
}
))->where('anything', '[A-Za-z0-9\/?=]+');