Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Route using either a prefix or a domain

I am working on a platform that allows users to run their own site in either a sub folder of the main website domain, or map a custom domain for their site.

When using a custom domain the URL structure for each route is slightly different in that it is prefixed with the username, but when using a custom domain then this prefix is not used.

Is there a clever way to achieve this in my Route::group to handle both request types in one route and successfully use reverse routing to produce the appropriate URL based on the parameters passed to it.

Below is an example of using the prefix

Route::group(array( 'prefix' => 'sites/{username}'), function() {
    Route::get('/photos/{album_id}.html', array('uses' => 'Media\PhotosController@album_view', 'as' => 'photo_album'));
});

And here is an example of using a custom domain

Route::group(array('domain' => '{users_domain}'), function() {
    Route::get('/photos/{album_id}.html', array('uses' => 'Media\PhotosController@album_view', 'as' => 'photo_album'));
});

Ideally I would like to be in a position where I could use either

route('photo_album', ['username' => 'johnboy', 'album_id' => 123] )

and be returned

http://www.mainwebsitedomain.com/sites/johnboy/photos/123.html

or call the same route with different parameters

route('photo_album', ['users_domain' => 'www.johnboy.com', 'album_id' => 123] )

and be returned

http://www.johnboy.com/photos/123.html
like image 288
Johnboy Avatar asked Feb 12 '15 16:02

Johnboy


People also ask

What is prefix in Laravel route?

Route PrefixesThe prefix method may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin : Route::prefix('admin')->group(function () { Route::get('/users', function () { // Matches The "/admin/users" URL.

What is mean by route in Cloud Foundry?

The Cloud Foundry Gorouter routes requests to apps by associating an app with an address, known as a route. This is known as a mapping. Use the cf CLI cf map-route command to associate an app and route.

What is php routing?

Routing is what happens when an application determines which controllers and actions are executed based on the URL requested. Simply put, it is how the framework gets from http://localhost/users/list.html to the Users controller and the list() action.

What is reverse routing in Laravel?

Laravel reverse routing is generating URL's based on route declarations. Reverse routing makes your application so much more flexible. For example the below route declaration tells Laravel to execute the action “login” in the users controller when the request's URI is 'login'.


1 Answers

This is a pretty tricky question so expect a few not so perfect workarounds in my answer...

I recommend you read everything first and try it out afterwards. This answer includes several simplification steps but I wrote down whole process to help with understanding

The first problem here is that you can't have multiple routes with the same name if you want to call them by name.

Let's fix that by adding a "route name prefix":

Route::group(array( 'prefix' => 'sites/{username}'), function() {
    Route::get('/photos/{album_id}.html', array('uses' => 'Media\PhotosController@album_view',
                                                'as' => 'photo_album'));
});

Route::group(array('domain' => '{users_domain}'), function() {
    Route::get('/photos/{album_id}.html', array('uses' => 'Media\PhotosController@album_view',
                                                'as' => 'domain.photo_album'));
});

So now we can use this to generate urls:

route('photo_album', ['username' => 'johnboy', 'album_id' => 123] )
route('domain.photo_album', ['users_domain' => 'www.johnboy.com', 'album_id' => 123])

(No worries we will get rid of domain. in the URL generation later...)


The next problem is that Laravel doesn't allow a full wildcard domain like 'domain' => '{users_domain}'. It works fine for generating URLs but if you try to actually access it you get a 404. What's the solution for this you ask? You have to create an additional group that listens to the domain you're currently on. But only if it isn't the root domain of your site.

For simplicity reasons let's first add the application domain to your config. I suggest this in config/app.php:

'domain' => env('APP_DOMAIN', 'www.mainwebsitedomain.com')

This way it is also configurable via the environment file for development.

After that we can add this conditional route group:

$currentDomain = Request::server('HTTP_HOST');
if($currentDomain != config('app.domain')){
    Route::group(array('domain' => $currentDomain), function() {
        Route::get('/photos/{album_id}.html', array('uses' => 'Media\PhotosController@album_view',
                                                    'as' => 'current_domain.photo_album'));
    });
}

Soooo... we got our routes. However this is pretty messy, even with just one single route. To reduce the code duplication your can move the actual routes to one (or more) external files. Like this:

app/Http/routes/photos.php:

if(!empty($routeNamePrefix)){
    $routeNamePrefix = $routeNamePrefix . '.';
}
else {
    $routeNamePrefix = '';
}

Route::get('/photos/{album_id}.html', ['uses' => 'Media\PhotosController@album_view',
                                            'as' => $routeNamePrefix.'photo_album']);

And then the new routes.php:

// routes for application domain routes
Route::group(['domain' => config('app.domain'), 'prefix' => 'sites/{username}'], function($group){
    include __DIR__.'/routes/photos.php';
});

// routes to LISTEN to custom domain requests
$currentDomain = Request::server('HTTP_HOST');
if($currentDomain != config('app.domain')){
    Route::group(['domain' => $currentDomain], function(){
        $routeNamePrefix = 'current_domain';
        include __DIR__.'/routes/photos.php';
    });
}

// routes to GENERATE custom domain URLs
Route::group(['domain' => '{users_domain}'], function(){
    $routeNamePrefix = 'domain';
    include __DIR__.'/routes/photos.php';
});

Now the only thing missing is a custom URL generation function. Unfortunately Laravel's route() won't be able to handle this logic so you have to override it. Create a file with custom helper functions, for example app/helpers.php and require it in bootstrap/autoload.php before vendor/autoload.php is loaded:

require __DIR__.'/../app/helpers.php';

require __DIR__.'/../vendor/autoload.php';

Then add this function to the helpers.php:

function route($name, $parameters = array(), $absolute = true, $route = null){
    $currentDomain = Request::server('HTTP_HOST');
    $usersDomain = array_get($parameters, 'users_domain');
    if($usersDomain){
        if($currentDomain == $usersDomain){
            $name = 'current_domain.'.$name;
            array_forget($parameters, 'users_domain');
        }
        else {
            $name = 'domain.'.$name;
        }
    }
    return app('url')->route($name, $parameters, $absolute, $route);
}

You can call this function exactly like you asked for and it will behave like the normal route() in terms of options and passing parameters.

like image 192
lukasgeiter Avatar answered Sep 29 '22 15:09

lukasgeiter