Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

BartHuis's avatar

One app multiple view routes (and domains assigned to it)

Hi, I'm going to build an application thats using multiple domains. Each domain has its own views, but its all the same application.

  • Api gets json
  • site1 gets his views
  • site2 gets other views

I've tried this setup in my routes.php:

<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::auth();

//**********************
//********Home**********
//**********************

Route::get('home', 'HomeController@index');
Route::get('api', 'HomeController@index');
Route::get('site1', 'HomeController@index');
Route::get('site2', 'HomeController@index');

//**********************
//*******Product********
//**********************
//
// API
//
Route::get('product', 'HomeController@index'); //returns login or welcome text
Route::get('api/product', 'ProductController@index'); // returns all products as json
Route::get('api/product/{id}', function ($id) { // returns given product as json
    $product = \App\Product::with('category','supplier')->find($id);
    return response(view('product',array('product'=>$product)),200, ['Content-Type' => 'application/json']);
});
//
// SITE1
//
Route::get('site1/product', function () { // returns all products as view, styled as site1
    $products = \App\Product::all();
    return response(view('site1/products',array('products'=>$products)),200);
});
Route::get('site1/product/{id}', function ($id) { // returns given product as view, styled as site1
    $product = \App\Product::with('category','supplier')->find($id);
    return response(view('site1/product',array('product'=>$product)),200);
});
//
// SITE2
//
Route::get('site2/product', function () { // returns all products as view, styled as site2
    $products = \App\Product::all();
    return response(view('site2/products',array('products'=>$products)),200);
});
Route::get('site2/product/{id}', function ($id) { // returns given product as view, styled as site2
    $product = \App\Product::with('category','supplier')->find($id);
    return response(view('site2/product',array('product'=>$product)),200);
});


//**********************
//*******Category*******
//**********************
//
// API
//
Route::get('category', 'HomeController@index'); //returns login or welcome text
Route::get('api/category', 'ProductController@index'); // returns all categories as json
Route::get('api/category/{id}', function ($id) { // returns given category as json
    $category = \App\Category::with('product')->find($id);
    return response(view('category',array('category'=>$category)),200, ['Content-Type' => 'application/json']);
});
//
// SITE1
//
Route::get('site1/category', function ($id) { // returns all categories as view, styled as site1
    $categories = \App\Category::all();
    return response(view('site1/categories',array('categories'=>$categories)),200);
});
Route::get('site1/category/{id}', function ($id) { // returns given category as view, styled as site1
    $product = \App\Category::with('product')->find($id);
    return response(view('site1/category',array('category'=>$category)),200);
});
//
// SITE2
//
Route::get('site2/category', function ($id) { // returns all categories as view, styled as site2
    $categories = \App\Category::all();
    return response(view('site2/categories',array('categories'=>$categories)),200);
});
Route::get('site1/category/{id}', function ($id) { // returns given category as view, styled as site2
    $product = \App\Category::with('product')->find($id);
    return response(view('site2/category',array('category'=>$category)),200);
});


//**********************
//*******Supplier*******
//**********************
//
// API
//
Route::get('supplier', 'HomeController@index'); //returns login or welcome text
Route::get('api/supplier', 'ProductController@index'); // returns all suppliers as json
Route::get('api/supplier/{id}', function ($id) { // returns given supplier as json
    $supplier = \App\Supplier::with('product')->find($id);
    return response(view('supplier',array('supplier'=>$supplier)),200, ['Content-Type' => 'application/json']);
});
//
// SITE1
//
Route::get('site1/supplier', function ($id) { // returns all suppliers as view, styled as site1
    $suppliers = \App\Supplier::all();
    return response(view('site1/suppliers',array('suppliers'=>$suppliers)),200);
});
Route::get('site1/supplier/{id}', function ($id) { // returns given supplier as view, styled as site1
    $product = \App\Supplier::with('product')->find($id);
    return response(view('site1/supplier',array('supplier'=>$supplier)),200);
});
//
// SITE2
//
Route::get('site2/supplier', function ($id) { // returns all suppliers as view, styled as site2
    $suppliers = \App\Supplier::all();
    return response(view('site2/suppliers',array('suppliers'=>$suppliers)),200);
});
Route::get('site1/supplier/{id}', function ($id) { // returns given supplier as view, styled as site2
    $product = \App\Supplier::with('product')->find($id);
    return response(view('site2/supplier',array('supplier'=>$supplier)),200);
});

It works, but:

  • I think it can be done better / simpler
  • How can i connect domains to an in app route like /site1 in apache via htacces or httpd/vhost?

If that's not possible, maybe its better to create a middleware for it to regognise the way its requested and not duplicating the routes for each site?

Bart

PS: The answer of @thefuzzy0ne looks like a better way than mine. I'm still thinking about the other way's of @dylanh too and let all the possible way's to do it pass my thoughts, but for now this one is the real answer on my initial question ;)

=============================================================

thefuzzy0ne — 15 hours ago

Or better yet: (Still untested):

function viewForDomain($view, $data = [], $mergeData = [])
{
    $domain = str_replace('.', '_', url());
    return view($domain.'.'.$view, $data, $mergeData);
}

Now you don't have to specify the domain at all, the function will use it automatically. You can still use the standard view() helper when you just want a normal view.

=============================================================

edit from Bart:

i made it working with this one as helper (multiple ways to make helpers, google for that) and added a fallback, so you can make base views and overrule them per domain:

<?php
function viewForDomain($view, $data = [], $mergeData = [])
{
    $domain = str_replace('.', '_', str_replace('http://', '', url('/')));
    if (view()->exists($domain.'.'.$view)) {
        return view($domain.'.'.$view, $data, $mergeData);
    }
    else{
        return view('base.'.$view, $data, $mergeData);
    }
}
?>
0 likes
23 replies
BartHuis's avatar

hi @thefuzzy0ne, thanks, i was looking at that grouping part of the docs too, thinking about what to use, but yeah, maybe specifying the whole domain would be a good thing. maybe i can also make the other parameters variable, get the objects and views, and catching the non excisting ones? or would that be dangerous?

if anybody has other ideas, its still welcome...

ps: edited the options

dylanh's avatar

+1 for what @thefuzzy0ne said. Definitely consider subdomain routing. So instead of having http://www.example.com/site1/products, you can have http://site1.example.com/products.

Also, you can make your routes.php much easier to understand if you use Route Groups / Prefixes.

Lastly, you should be using controllers to do things like pulling data from the database rather than just putting everything in your routes file.

I think you'd benefit greatly from this series: https://laracasts.com/series/laravel-5-from-scratch

It gives you a good foundation on how to do all of the above and more.

BartHuis's avatar

@dylanh thanks, first, the domains will just be www.site1.com and www.site2.com, pointing to the same application. the routes /site1 and /site2 i gave were only laravel routes. but maybe i can still put that in the domain route like:

Route::group(['domain' => 'site1.com'], function () {}

i don't really understand the route groups / prefixes yet. i think i'll play along some with it.

I did that from scratch one allready a while ago, but maybe its a good thing to look at it again now i've done some more in laravel, this setup was just a test setup to find the right way for programming the whole application. but yes, i have to put more in the controllers ;)

When i have something more working, i'll post it here, if in the meantime someone has a better idea, please share it, so all options are in this topic ;)

Thanks, Bart

dylanh's avatar

Ok cool.

So you have 2 different domains, that you want to have 2 different sets of views (and you're not doing too much with controllers yet).

Is there any reason why they can't just be 2 different apps? What do they have in common that makes you want to build them as one laravel app?

niells's avatar

Hi,

i don't think you can map 2 different domains on a single laravel deployment location (you can use subdomains for that as previously stated above) but with 2 different domains you'll most certainly have cookie problems on 1 of those

Why not use the API itself on both of your domains? (instead of implementing the same data pulling logic in all your websites you could leave that solely for the API and on your "main" websites you could simply perform AJAX requests to the specific API url to fetch the data, then populate the existing views with that data - you might need to write a CORS middleware on your API to allow such requests if the domains are different

A design option would be to create 3 laravel projects, 1 for the API (all your business models would go here) and 2 for the front-end, that could share the same data fetching JS functions (calls to fetch data from the API) and would only require specific UI design.

dylanh's avatar

@niells makes some valid points and good suggestions. The API route might make sense if you have a lot of business logic that is common between the 2 domains.

Although, it is possible to have the same app served on multiple domains. For example, if you deploy an app under the "default" domain on forge (which is basically just a catchall for all traffic hitting that server IP) the app will serve on any domain that is pointed to the server. Its definitely not ideal or recommended, but does work.

BartHuis's avatar

@dylanh there is going to be more in controllers, we're going to look at a possibility to make general views, and let them overrule my site specific views, but thats a second job to look at.

The idea is that we'll be building one application, and put it on a git, and deploy it to two servers. So actualy, the server at site1.com doesn't need the views of site2.com and the other way around. But to force ourselves to always make a change compatible to both, we don't end up in maintaining 2 versions. Kind of protecting yourself for bad habits of don't make a change from site1 to site2.

BartHuis's avatar

@dylanh that answer was on your reply of 10 minutes ago, now reading the rest...

BartHuis's avatar

@niells @dylanh we thought about that too, but there are also some data things that are different between the two sites. and running on one server isn't possible becouse of seo rules they need a different ip address ;)

thefuzzy0ne's avatar

I was thinking about this some more. Perhaps you could create your own view helper that will accept a domain. Which will essentially map to a sub-directory in your views directory.

Untested:

function viewFor($domain, $view, $data = [], $mergeData = [])
{
    $domain = str_replace('.', '_', $domain);
    return view($domain.'.'.$view, $data, $mergeData);
}

Now your site-specified views will be in sub-directories like www_example_com and www_someothersite_co_uk.

With that said, it still begs the question as to how you're going to ensure you have the right data for the right site, so this idea might not work after all. But I thought I'd put it out there anyway. :)

BartHuis's avatar

and one other thing is that the api actually don't function as seperate one, that was an error from my side. It had to be site1.com/product, site1.com/api/product, site2.com/product and site2.com/api/product, so when we make for example a search function at site1.com, we can get the result from the api instead of getting a view... but i left it like a 3rd one just to dont make it more dificoult than it allready was ;)

thefuzzy0ne's avatar

Or better yet:

(Still untested):

function viewForDomain($view, $data = [], $mergeData = [])
{
    $domain = str_replace('.', '_', url());
    return view($domain.'.'.$view, $data, $mergeData);
}

Now you don't have to specify the domain at all, the function will use it automatically. You can still use the standard view() helper when you just want a normal view.

BartHuis's avatar

i'll look at that @thefuzzy0ne :)

@niells when hosting two sites with a part of it being the same, its better to host the two sites on different ip's for better SEO ;)

BartHuis's avatar

i'll look at all the options and make a nice mix, with the api being after the domains instead of being a seperate one...

dylanh's avatar

Without knowing all of the business reasons behind this, I could be wrong.... But I can't help but think that you're making your life unnecessarily complicated here.

I would deploy at least 2 separate apps. Site1.com is a site with some views and an API (this is where the Route Prefix/Group can be useful) Site2.com just consumes the API from site1.com if it needs to and has its own views/controllers to do the rest.

Alternatively, if needed make the API standalone on its own domain/subdomain and keep site1.com and site2.com as public facing.

It will be much easier to do this on at least 2 separate code bases rather than trying to manage 2 different sites on the same code base.

johndoe128's avatar

Can't believe that you confidentially advise the guy that he cannot achieve what he wants in one laravel app. He can easily make middleware or some interceptor logic where he includes different views based on domain/host which he can detect easily.

Multiple codebases are a totally wrong approach in this case.

BartHuis's avatar

The idea behind it is that both sites getting different results, also bases on whitch site they are. so yeah, me made made a dicision about this becouse of bussiness reasons, but, everything is in scratch fase, so at this moment we can change things. i'll still think about all options. thats why i said, please drop all your ideas, so we can compare it.

Becouse the fact that they have different data, we need both sites to have its own api, so site1.com's search can query its own api, and site2.com's can get its own. but maybe, we can still let them use the same parent application and still use domain bases output of the api. we'll think about all options. Thanks for thinking with me!

Bart

thefuzzy0ne's avatar
Level 6

If it's really just the same data with different presentation, I think my idea should work fine. Then your routes.php will look something like this (I tried to optimise it a little, but I'm sure it can be optimised a bit more):

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::auth();

Route::get('home', 'HomeController@index');
Route::get('api', 'HomeController@index');

/***********************
 *       Product       *
 ***********************/

Route::group(['prefix' => '/product'], function() {
    Route::get('/', function () { // returns all products as view
        $products = \App\Product::all();
        return viewForDomain('products', ['products'=>$products]);
    });
    Route::get('/{id}', function ($id) { // returns given product as view
        $product = \App\Product::with('category','supplier')->find($id);
        return viewForDomain('product', ['product'=>$product]);
    });
})
    
/******************
 *      API       *
 ******************/

Route::group(['prefix' => '/api'], function() {
    Route::get('/category', 'ProductController@index'); // returns all categories as json

    Route::get('/category/{id}', function ($id) { // returns given category as json
        $category = \App\Category::with('product')->find($id);
        return response()->json($category);
    });

    Route::get('/supplier', 'ProductController@index'); // returns all suppliers as json

    Route::get('/supplier/{id}', function ($id) { // returns given supplier as json
        $supplier = \App\Supplier::with('product')->find($id);
        return response()->json($supplier);
    });

    Route::get('/product', 'ProductController@index'); // returns all products as json

    Route::get('/product/{id}', function ($id) { // returns given product as json
        $product = \App\Product::with('category', 'supplier')->find($id);
        return response()->json($product);
    });
})

/***********************
 *       Category      *
 ***********************/

Route::group(['prefix' => '/category'], function() {
    Route::get('/', function () { // returns all categories as view
        $categories = \App\Category::all();
        return viewForDomain('categories', ['categories'=>$categories]);
    });
    Route::get('/{id}', function ($id) { // returns given category as view
        $product = \App\Category::with('product')->find($id);
        return viewForDomain('category', ['category' => $category]);
    });
});

/***********************
 *       Supplier      *
 **********************/
Route::group(['prefix' => '/supplier'], function() {
    Route::get('/', function () { // returns all suppliers as view
        $suppliers = \App\Supplier::all();
        return viewForDomain('suppliers', ['suppliers'=>$suppliers]);
    });
    Route::get('/{id}', function ($id) { // returns given supplier as view
        $supplier = \App\Supplier::with('product')->find($id);
        return viewForDomain('supplier', ['supplier'=>$supplier]);
    });
});

I've not tested it, but hopefully you get the idea. Now, to add a new site, you just need to add the corresponding views. You won't need to touch the routes unless you add a new URI.

Note, there's no more mention of site1, site2 or whatever. I'm sure you could apply this same technique to your API too.

BartHuis's avatar

Thnx @thefuzzy0ne , that looks like a better way than mine. I'm still thinking about the other way's of @dylanh too and let all the possible way's to do it pass my thoughts, but for now this one is the real answer on my initial question ;)

BartHuis's avatar

i made it working with this one as helper (multiple ways to make helpers, google for that) and added a fallback, so you can make base views and overrule them per domain:

<?php
function viewForDomain($view, $data = [], $mergeData = [])
{
    $domain = str_replace('.', '_', str_replace('http://', '', url('/')));
    if (view()->exists($domain.'.'.$view)) {
        return view($domain.'.'.$view, $data, $mergeData);
    }
    else{
        return view('base.'.$view, $data, $mergeData);
    }
}
?>

Please or to participate in this conversation.