I have been testing sub-domain routing functionality in Laravel 5 and have had success with the following code as described in the docs. When the user visits {username}.mysite.com, the user profile view is shown as expected.
Route::group(['domain' => '{username}.{tld}'], function () {
Route::get('user/{id}', function ($username, $id) {
//
});
});
But, I was expecting a bit of different behavior from what I am experiencing. When the user visits the site through the sub-domain, all of the links in my views now retain the sub-domain within them. All of the other links like {username}.mysite.com/home and {username}.mysite.com/login etc... are fully functional, but I don't understand why Laravel is populating all of my links with the sub-domain and/or how I can get rid of this and only retain the sub-domain for just the single route. I want all of the other links in my views to be like mysite.com/home and mysite.com/login. I was hoping to just use {username}.mysite.com as a quick access point for site visitors, not to retain it in all views.
What can I do to change this behavior?
Move routes you don’t want prefixing with the subdomain outside of the route group:
// These routes won’t have the subdomain
$router->controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
// These routes WILL have the subdomain
$router->group(['domain' => '{username}.{tld}'], function ($router) {
$router->get('/', 'UserDashboard#index');
$router->controller('account', 'AccountController');
$router->resource('users', 'UserController');
});
You forgot to redirect user... So:
First, as Martin Bean suggested, exclude undesired controllers from sub-domained group.
Second, after user's successful login - redirect him to address without subdomain. You can do that by overriding auth middleware with yours implementation (which must implement TerminableMiddleware interface).
I. e.:
User was on page https://auth.example.com and logined.
Your's override of auth middleware checks for succesful login.
... and redirects user to https://example.com/home or whatever...
That should be enough.
I found the problem where all of my links/routes were being prefixed with the subdomain, even when they were outside of the route group. The issue is with the Illuminate HTML link builder. It renders relative links rather than full absolute links.
So instead of using: {!! HTML::link('contact', 'Contact Us') !!}
I should have used: {!! HTML::linkRoute('contact_route_name', 'Contact Us') !!}
The linkRoute() function takes into consideration the route group and applies the subdomain as required.
Related
Question
How can I set up Laravel routing so that:
navigating to mysite.com/login uses the LoginController
navigating to somecompany.mysite.com/login uses the TenantLoginController
What I'm doing
I'd have a Laravel 5.7 app that has a typical login page at say, mystite.com/login
I'd like to set up a subdomain for this app like somecompany.mysite.com that will have it's own authentication.
I'd like the somecompany users to log in at somecompany.mysite.com/login
What I've tried
The route definition for the main site login
Route::group(['namespace' => 'App\Http\Controllers\Auth', 'middleware' => ['web']], function () {
Route::get('login', 'LoginController#showLoginForm')->name('login');
});
The rout definition for the subsomain login
Route::domain('somecompany.mysite.com')->group(function ($router) {
$router->group(['namespace' => 'App\Http\Controllers\Tenant\Auth', 'middleware' => ['web']], function($router) {
$router->get('login', 'TenantLoginController#showLoginForm')->name('somecompany.login');
});
});
What Happened
I can navigate to somecompany.mysite.com/login and the URL bar says somecompany.mysite.com/login but when I do, the request is actually routed to the 'LoginController#showLoginForm' controller not the expected 'TenantLoginController#showLoginForm' and the typical login form is desplayed, not the subdomain's login form.
If I change the path to $router->get('tenant-login' and navigate to somecompany.mysite.com/tenant-login the subdomain login form is shown, and somecompany.mysite.com/login shows the main login form.
Since you did not specify a domain in the first route (handled by LoginController), it should also be valid for the somecompany.mysite.com subdomain.
To work around that, I would suggest trying to add more specificity to that first route, enclosing it with Route::domain('mysite.com').
The Laravel router always takes the first matching route, and that first one matches just fine in the end.
When I have this named route:
Route::get('/', 'IndexController#index')->name('home');
Then in any action method of any Controller; when I need to redirect to the named route home; any of these statements redirects properly to the intended route:
return redirect('/');
return redirect()->route('home');
return redirect()->home();
When to use each?
What are the differences?
Are there any benefits of using one over the others?
As the documentation mention :
When you call the redirect helper with no parameters, an instance of
Illuminate\Routing\Redirector is returned, allowing you to call any
method on the Redirector instance. For example, to generate a
RedirectResponse to a named route, you may use the route method
As you can see in the API methods(link below) there is a lot of methods that you can use and also there is one specific helper method home() it's just a shortcut for redirect()->route('home') as highlighted by #ceejayoz.
Now the we will talk about return redirect('/'); and return redirect()->route('home'); the two of them redirects properly to the intended route as you said BUT the second one is really useful if in the future.
Why ?
Because if you want to change the URL structure in the routes file all you would need to change is the route only for example :
Route::get('/', 'IndexController#index')->name('home');
Will be :
Route::get('/home_page', 'IndexController#index')->name('home');
and all the redirects would refer to that route and there is no other thing that you should change => all redirects will still work perfectly.
BUT
If you choose to use the first one (i mean return redirect('/');) then after the change in the route you will need to parse all your controllers to check if there is some redirects that uses then changed route and the change them :p
redirect()->home() is simply a shortcut for redirect()->route('home'). The source code can be seen here.
Named routes are generally better than raw URLs for maintainability purposes. The home route isn't all that likely to change location, but it is possible that you might host a Laravel app in a subfolder, or move the home page from / to /app to make room for a marketing landing page at the root.
redirect('/')
It redirects you to the base URL.
redirect()->route('home')
Redirects to the route named home.
See More about named routes here.
redirect()->home();
Alternative way to redirect to named route.Redirects to 'home' route as well. It does the same thing as above but with slightly different syntax.
I preferred named routes over raw URLs, because if you decide to change the URL later on, you have to make changes into your routes file only.
When you are passing a string it will redirect a user to the domain plus the string you pass.
http://localhost:3000 + string
It will also add / if you forget it, now if you name your routes like you did then you can call it by the name.
An advantage of using named routes is in case you want to change the URI you can do it without worrying about changing a bunch of ahref in your view, redirects in your controllers, etc.
home() is a method from Laravel's Redirector or redirect() so, I don't think you can just call a named route as a method.
I'm using a sub-domain in my application as a dashboard for a user account. In the views of the dashboard, I include a master template which contains links in the navigation that lead to the root of the website. When I visited the sub-domain I've noticed all links in the navigation were changed based on the sub-domain name.
In my master template, links are generated using the helper function route(). When a generated link is viewed on my sub-domain, the link changes from domain.com/about to sub.domain.com/about along with all other links.
Route::group(['domain' => 'dashboard.project.app', 'before' => 'auth'], function()
{
Route::controller('/', 'DashboardController');
});
Route::get('/', ['as' => 'home', 'uses' => 'HomeController#index']);
Route::resource('products', 'ProductsController');
Route::resource('support', 'SupportController');
So visiting dashboard.project.app would trigger getIndex() in the DashboardController while visiting project.app would trigger index() in the HomeController.
Great, we've got this far. Now I have a simple view I use as a template that will contain URLs to the named resource routes defined outside of the sub-domain.
Again, URLs are made using the helper function route(). In a template extended by a view called in DashboardController I would have a link in the navigation like:
Products
which would generate Products as desired however changes to Products when shown in a view under the sub-domain.
So I would like the desired output to always be project.app/products and never dashboard.project.app/products as visiting that link will result in a 404 as there is no getProducts() method in my DashboardController. It's the wrong links!
Get what I'm saying?
If the named route is not defined as being specific to a domain, then the url generator will use whatever domain you are currently on. If the route should be specific to a domain, specify it:
Route::group(['domain' => 'project.app'], function() {
Route::resource('products', 'ProductsController');
Route::resource('support', 'SupportController');
});
Now, even from your dashboard, route('products.index') should give you project.app/products.
I'm working with Laravel 4.2. I have an app that's routing to the wrong view, although the URL is correct. On a button click, it's supposed to route to users.create (UsersController#create), but is instead routing to UsersController#show. The resolved URL is correct, though, and the DOM element has the correct URL listed. Can anyone help me out?
Here is my Routes file:
// Home page
Route::get('/', 'BaseController#index');
// Define User model to pass through routes
Route::model('user', 'User');
// Create custom route for editing a user
Route::get('users/edit/{user}',
array('as' => 'users.edit', 'uses' => 'UsersController#edit'));
// Create custom route for showing a user
Route::get('users/{user}',
['as' => 'users.show', 'uses' => 'UsersController#show']);
// Remaining routes
Route::resource('users', 'UsersController',
array('except' => array('edit', 'show')));
Here is my UsersController with the two functions in question:
class UsersController extends \BaseController {
protected $user;
public function create()
{
return View::make('users/create');
}
public function show($user)
{
return View::make('users/show', ['user' => $user]);
}}
And here are the relevant results from php artisan routes:
GET:HEAD users/{user} users.show UsersController#show
GET:HEAD users/create users.create UsersController#create
Thanks for your help!
Edit:
The answer to the problem was to simply re-order the routes so that the resource is defined first. I was aware that Laravel grabs the first route that matches a URI, but I still don't understand why a route that isn't passed a user object would select a route defined as users/{user}. Furthermore, I was accessing the route via link_to_route(), that is to say, by name. Why would Laravel pick a different route from the one I explicitly named?
I suppose these questions are beyond the scope of the initial question, but I would greatly appreciate further explanation from someone. Problem solved!
The first thing that jumps out at me is there is no route for "create". There is the "restful" controller, but maybe you want to just try putting the route in. I'm slightly uncomfortable using restful routes when serving html. In the project i'm working on i've been trying to preserve those for data/json transmission, in order to support outside api action.
I think your routes setup recognizes create as {user} in user/{user}, thus redirect to user/show/create as your "custom route for showing a user" setup.
You may have to avoid getting string variable right after users/ route.
I would like to create an administrator interface for my Laravel project, which is completely separated from the user side.
For example, in Yii framework I can make a module and this will ensure full separation from the user side. Inside a module I can use separate folder structure etc.
This is really a broad question and one answer can't cover everything about best practice for admin controllers or back end management but there are some basic concepts for building an Admin Panel:
// Keep all of your admin routes inside something like this
Route::group(array('prefix'=> 'admin', 'before' => 'auth.admin'), function() {
// Show Dashboard (url: http://yoursite.com/admin)
Route::get('/', array('uses' => 'Admin\\DashBoardController#index', 'as' => 'admin.home'));
// Resource Controller for user management, nested so it needs to be relative
Route::resource('users', 'Admin\\UserController');
});
// Other routes (Non-Admin)
Route::get('login', array('uses' => 'AuthController#showLogin' 'as' => 'login'));
By using a prefix you may separate all admin routes whose url will be prefixed with admin so, if you have a users controller for user management in back end then it's url will be prefixed with admin, i.e. site.com/admin/users. Also using a before filter you may add an authentication for all admin controllers in one place, that means, to access all of your admin controllers user must be logged in and the filter could be something like this:
Route::filter('auth.admin', function($route, $request, $args){
// Check if the user is logged in, if not redirect to login url
if (Auth::guest()) return Redirect::guest('login');
// Check user type admin/general etc
if (Auth::user()->type != 'admin') return Redirect::to('/'); // home
});
For, CRUD (Create, Read, Update, Delete) use a resourceful controller, for example, the UserController in an example of resourceful route declaration.
Use repository classes (Repository Pattern) for decoupling of dependencies, read this article.
Always use a named route, i.e. array('as' => 'routename', 'uses' => 'SomeController#method'), this is an example of naming a route. Named routes are easy to refer, i.e. return Redirect::route('admin.home') will redirect to site.com/admin because we have used admin.home in as to assign the name for that route.
Keep admin controllers in a separate folder and use a namespace for example, Admin\\DashBoardController#index controller should be in app/controllers/admin and your DashBoardController controller should look like this:
<?php namespace Admin;
class DashBoardController extends \BaseController {
public function index()
{
//...
}
}
There are more but it's enough to start with, read articles online and must read the documentation.
If you are familiar with composer you can import in packages (aka modules)
There is a widely available module with multi level interface already called Sentry 2.0:
https://github.com/cartalyst/sentry
You could also make your own if needed if the one I propose is too complex.
There is even a "laravel-ready" version of sentry.
I use the same directory structure that you would like to use on most (if not all) my Laravel projects. Basically, I keep admin views and admin controllers separate from the front-end ones.
Examples:
Controllers:
app/controllers/admin/Admin*Name*Controller.php
app/controllers/site/*Name*Controller.php
Views:
app/views/admin/some_folder/index.blade.php
app/views/site/some_folder/index.blade.php
I would also suggest that you install this laravel project https://github.com/andrewelkins/Laravel-4-Bootstrap-Starter-Site which will give a very good starting on how to organise things in your laravel project. It also has the same folder structure you would like to use.
Good luck.