Our team is trying to convert an old multitenant architecture over to Laravel and having some difficulty staying DRY in our routes/views.
Our account section contains multiple page views: an account dashboard, edit profile, payment methods, registrations, etc. However, we provide multiple stand-alone websites for a client, and there are multiple URL structures for the user to access their account:
In addition to the standard...
https://clientone.ourdomain.com/account/
...they can also access their account via different standalone websites:
https://clientone.ourdomain.com/event-app-one/account/
https://clientone.ourdomain.com/event-app-two/account/
All of those URLs above give them the exact same information, but with a different layout/theme applied for each standalone website. Right now the routes I set up for those above look like this:
// #### CLIENT ONE - ROOT
Route::domain('{subdomain}.ourdomain.com')->group(function() {
Route::middleware(['setTheme:clientone-account-theme'])->group(function() {
Route::get('/', 'ClientController#index')->name('index');
Route::prefix('account')->group(function() {
Route::get('/', 'AccountController#index')->name('clientone.account.show');
Route::get('/edit', 'AccountController#edit')->name('clientone.account.edit');
...
});
});
// #### CLIENT ONE - EVENT APP ONE
Route::prefix('event-app-one')->group(function() {
Route::middleware(['setTheme:clientone-eventapp-one-theme'])->group(function() {
Route::get('/', 'EventAppController#index')->name('clientone.eventapp.one.index');
...
Route::prefix('account')->group(function() {
Route::get('/', 'AccountController#index')->name('clientone.eventapp.one.account.show');
Route::get('/edit', 'AccountController#edit')->name('clientone.eventapp.one.account.edit');
...
});
});
});
// #### CLIENT ONE - EVENT APP TWO
Route::prefix('event-app-2')->group(function() {
Route::middleware(['setTheme:clientone-eventapp-two-theme'])->group(function() {
Route::get('/', 'EventAppController#index')->name('clientone.eventapp.two.index');
...
Route::prefix('account')->group(function() {
Route::get('/', 'AccountController#index')->name('clientone.eventapp.two.account.show');
Route::get('/edit', 'AccountController#edit')->name('clientone.eventapp.two.account.edit');
...
});
});
});
Those routes all currently work. We don't like including the client or app name in all of our route names because they get rather long and can't be reused, but if we take those out we found out our route URLs will override each other.
However, if we do it this way, our account views that we share across all these URLs can't use named route links for href because we don't know which one to use. For example, we can't do this in blade...
Edit Profile
...because, while that will work while at clientone.ourdomain.com/account/, it won't work while at clientone.ourdomain.com/event-app-one/account/.
Should I just not use route names for links in our views and use a relative link instead? Or am I going about my route naming all wrong? The only other thing I can think of is to copy all the view files and have one version for .com/account/ and another for .com/all-event-apps/account/ and just use a different set of route names for each. Feels wrong to duplicate essentially the same functionality in those views though.
You can use a parameter for the prefix so you are not repeating the same exact routes and creating new names for them. You can then generate routes based on those route names.
You can set defaults for parameters for the UrlGenerator to help deal with this.
I would add a middleware to your group that checks the prefix then will assign that as a default parameter on the UrlGenerator:
// instead of app-one, app-two, etc etc
Route::prefix('{app}')->middleware('handleapp')...
// handleapp middleware
Url::defaults(['app' => $request->route()->parameter('app')]);
$request->route()->forgetParameter('app'); // if you dont want this passed into controller methods
// where you need to generate a route
route('eventapp.account.edit'); // {app} is handled by the default we set so doesn't have to be passed
I would also have a middleware that will add the default parameter for subdomain as well so you don't have to pass that yourself everywhere.
Basically it all becomes dynamic and based on the current request you can have defaults setup for all these parameters so you don't have to pass them into the route helpers yourself everywhere you need a URL from a route name.
This is just the basic idea in use.
Related
I have created an application like a portfolio, every user has a unique id or username, I need to generate a copy of this for each user by adding the ID in the URL as a parameter, I know how to do that surly, but I need to attach only with the one Route and then it will still across entire pages and requests for this user? is there any way to do that?
I think I can do it in this way, and then I will store it in the session? is it possible? and is it the best way, because I need the official or professional way
Route::get('/uid', [HomeController::class, 'home'])->name('home');
Let's assuse you want to have theese routes for example
/{uid}/profile
/{uid}/prot
Based on document you can use group to add prefix to all the routes
Route::prefix('/{uid}')->group(function () {
Route::get('/profile', [HomeController::class, 'profile'])->name('profile');
Route::get('/prot', [HomeController::class, 'prot'])->name('prot');
});
Using this you can pass uid across all routes that created in this groups
I can't seem to find an answer on the web or maybe I'm missing something.
Let's say my laravel application domain is my-laravel.com
Now I want to make requests at api.my-laravel.com which will work just same as my-laravel.com/api/
How do I do that?
Solution: Changed prefix('api') to domain('api.my-laravel.com') in RouteServiceProvider#mapApiRoutes
You may group your routes in a sub-domain, change your routes/api.php
Route::group(["domain" => "api.domain.key"], function() {
// your api routes.
});
Edit:
Check your RouteServiceProvider then remove the api prefix in mapApiRoutes method.
Alternatively(Except DNS and mod_rewrite)
Sub-Domain Routing
Route groups may also be used to handle sub-domain routing. 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 by calling the the domain method before defining the group:
Route::domain('{account}.myapp.com')->group(function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
Source: https://laravel.com/docs/5.4/routing#route-group-sub-domain-routing
Note:
Before this map your subdomain to point your server and point your domain (laravel.com)
I have an application in which you can create a service and a service can have its own partial view.
So I created a route group with {service} prefix:
Route::group(['prefix' => '{service}', ... ], ... ).
// http://.../my-service/my-url
However, in order to know in which service the user is I need to add the service in every single route I have in my application. So I have done a middleware that shares $service to every view:
view()->share(['service' => $service])
But I don't know how to add $service prefix to every route without explicitly adding it. I would like doing something like
route()->prefix(['service' => $service])
and then every route have the prefix $service:
url("myurl") // -> url("$service/my-url") or
route('my-route')
Any idea?
EDIT:
Finally I decided to create a ServiceType model, create a middleware with a parameter and set to my route groups. Then in view I offer the user to switch between services of the same type.
Not is what I was looking but it's OK for now.
Question is still open if anyone knows the answer.
Put all your routes you want prefixed in a group and then add the prefix
Laravel docs on route prefixes
in my routes file I use a route group with segment like:
Route::group(['prefix' => request()->segment(1) ], function(){
//routes
});
Normally, my routes are in the order they were written at, but when using the group with request()->segment(1) routes just get disarranged (not just inside the group itself, but all of them), I need to use segment so every customer will have their own slug as the first segment in the URL.
so how can I fix this issue?
Use middleware to validate the user and allow them based on their slug
Please Make own middleware like jwt.auth
I developed my own middleware to filter slug based
Say I have a controller, "Articles" but I want it to appear as a sub-folder (e.g. "blog/articles"), I can add a route like this:
$route['blog/articles'] = 'articles';
$route['blog/articles/(:any)'] = 'articles/$1';
This works fine, the only problem now is that example.com/articles and example.com/blog/articles both use the Articles controller and thus resolve to the same content. Is there a way to prevent this?
To add a little more clarity in case people aren't understanding:
In this example, I don't have a 'blog' controller, but I want 'articles' etc to appear to be in that subfolder (it's an organization thing).
I could have a blog controller with an 'articles' function, but I'm likely to have a bunch of 'subcontrollers' and want to separate the functionality (otherwise I could end up with 30+ functions for separate entities in the blog controller).
I want example.com/articles to return a 404 since that is not the correct URL, example.com/blog/articles is.
Shove this in your controller:
function __construct()
{
parent::Controller();
$this->uri->uri_segment(1) == 'blog' OR show_404();
}
You can use subfolders in Codeigniter controllers, so in CI, the following directory structure works:
application/controllers/blog/articles.php and is then accessed at
http://example.com/blog/articles/*.
If, for some reason, you're set on routing instead of accessing the controllers in folders (you want to have a blog controller, for example, and don't want to route to it), you can do as suggested above and add the test for 'blog' to the constructor.
If you're in PHP5, you can use the constructor function like this:
function __construct()
{
parent::Controller();
$this->uri->uri_segment(1) == 'blog' OR redirect('/blog/articles');
}
or, in PHP4, like this:
function Articles()
{
parent::Controller();
$this->uri->uri_segment(1) == 'blog' OR redirect('/blog/articles');
}
I would suggest using redirect('blog/articles') instead of show_404(), though, so that you're directing users who hit /articles to the correct location, instead of just showing them a 404 page.
Routing there does not mean it will use a different controller, it just creates alias url segment to same controller. The way will be to create another controller if you are looking to use a different controller for those url segments.
If both /blog/ and /articles/ use the same controller, you can reroute one of them to a different one by just adding a new rule in your routes file.