Closed ametad closed 5 years ago
The routes are perfectly working when going there with a browser:
@luceos Perhaps I am trying something that is not ment to be. Could you elaborate on how you think about tenant routes in scope other then http protocol (like cli)?
It's likely just an ordering issue with ServiceProviders or middleware or something. That should definitely work, though I'd have to dig deeper to figure out why it's not for you.
Hi @fletch3555 thank you for your time,
The example I give on this page is with a clean install of Laravel 5.7. And hyn/multi-tenant
is auto loaded
in the app.
composer.json
{
"name": "laravel/laravel",
"type": "project",
"description": "The Laravel Framework.",
"keywords": [
"framework",
"laravel"
],
"license": "MIT",
"require": {
"php": "^7.1.3",
"fideloper/proxy": "^4.0",
"hyn/multi-tenant": "5.3.*",
"laravel/framework": "5.7.*",
"laravel/homestead": "^7.19",
"laravel/tinker": "^1.0"
},
"require-dev": {
"beyondcode/laravel-dump-server": "^1.0",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"autoload": {
"psr-4": {
"App\\": "app/"
},
"classmap": [
"database/seeds",
"database/factories"
]
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
}
}
Homestead.yaml
ip: 192.168.10.10
memory: 2048
cpus: 1
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa
folders:
-
map: /home/anne/WebDev/playground/laravel-app-multi-tenant-route-list
to: /home/vagrant/code
sites:
-
map: homestead.test
to: /home/vagrant/code/public
-
map: foo.test
to: /home/vagrant/code/public
databases:
- homestead
- tenancy
name: laravel-app-multi-tenant-route-list
hostname: laravel-app-multi-tenant-route-list
There is no customized code in the fresh installed Laravel app.
The only added code is in:
composer require <package>
)(All mentioned files are shown in the above.)
I think this may qualify as 'standard out-of-the-box' installation of Tenancy, wouldn't you agree?
As a work around I duplicate the tenant routes in routes/tenants.php
into routes/web.php
:
routes/tenants.php
<?php
Route::middleware('web')
->namespace('App\\Http\\Controllers\\')
->group(function ()
{
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
});
routes/web.php
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
Then the artisan command can list the routes correctly (and the routes work correct on http protocol also):
vagrant@laravel-app-multi-tenant-route-list:~/code$ artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest |
| | POST | password/reset | password.update | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
But this is not optimal solution if I need fallback routes to be different.
@fletch3555 Can I help you providing some data/info you need to know? I gladly provide.
Test without auto-loading.
composer.json snippet
"extra": {
"laravel": {
"dont-discover": [
"hyn/multi-tenant"
]
}
},
config/app.php snippet
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
Hyn\Tenancy\Providers\TenancyProvider::class,
Hyn\Tenancy\Providers\WebserverProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
Composer dumpautoload
vagrant@laravel-app-multi-tenant-route-list:~/code$ composer dump
Generating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: fideloper/proxy
Discovered Package: laravel/nexmo-notification-channel
Discovered Package: laravel/slack-notification-channel
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
Generated optimized autoload files containing 4217 classes
Route list with duplicate definitions in routes/web.php
and routes/tenants.php
vagrant@laravel-app-multi-tenant-route-list:~/code$ artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest |
| | POST | password/reset | password.update | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
Route list with definitions in routes/tenants.php
only
vagrant@laravel-app-multi-tenant-route-list:~/code$ artisan route:list
+--------+----------+----------+------+---------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+----------+------+---------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
+--------+----------+----------+------+---------+--------------+
The route:list command won't show the tenants routes because these are only added when a tenant was identified. Either try to use the tenancy:run command, but it's very likely not going to be of much use here. Instead we might need a custom command to get these routes in cli.
Thank you @luceos ,
Test with tenancy:run
fails indeed as you predicted:
(Test is done with Laravel auto discovery and without auto discovery but manually load package in config/app.php
.)
https://github.com/hyn/multi-tenant/issues/584#issuecomment-424677030
This implies it should work.., right? What am I missing?
Hi, I have the same problem right now... how did you solve? Thanks
This is not solved and won't be put on our backlog. Feel free to PR a command that lists the tenant routes.
I created a TenancyRoute command like this, and it worked:
<?php
namespace App\Console\Commands;
use Closure;
use Hyn\Tenancy\Environment;
use Hyn\Tenancy\Models\Website;
use Illuminate\Console\Command;
use Hyn\Tenancy\Providers\Tenants\RouteProvider;
use Illuminate\Support\Facades\Route as RouteFacade;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Routing\Route;
class TenancyRoute extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'tenancy:route:list {--name} {--path} {--method} {--sort} {--reverse} {--c|compact} {--columns} {--json}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'List all registered routes on tenant';
/**
* The table headers for the command.
*
* @var array
*/
protected $headers = ['Domain', 'Method', 'URI', 'Name', 'Action', 'Middleware'];
/**
* The columns to display when using the "compact" flag.
*
* @var array
*/
protected $compactColumns = ['method', 'uri', 'action'];
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$website = Website::first();
if (! $website) {
$this->error('Tenant not found!');
return true;
}
$app = app();
$app[Environment::class]->identifyHostname();
$app[Environment::class]->hostname();
$hostname = $website->hostnames()->first();
config(['tenancy.hostname.default' => $hostname->fqdn]);
config(['tenancy.routes.replace-global' => true]);
// Refresh routes with above configuration now set.
$app->call([new RouteProvider($app), 'boot']);
$routes = RouteFacade::getRoutes();
$this->displayRoutes($this->getRoutes($routes));
}
/**
* Compile the routes into a displayable format.
*
* @return array
*/
protected function getRoutes($data)
{
$routes = collect($data)->map(function ($route) {
return $this->getRouteInformation($route);
})->filter()->all();
if ($sort = $this->option('sort')) {
$routes = $this->sortRoutes($sort, $routes);
}
if ($this->option('reverse')) {
$routes = array_reverse($routes);
}
return $this->pluckColumns($routes);
}
/**
* Get the route information for a given route.
*
* @param \Illuminate\Routing\Route $route
* @return array
*/
protected function getRouteInformation(Route $route)
{
return $this->filterRoute([
'domain' => $route->domain(),
'method' => implode('|', $route->methods()),
'uri' => $route->uri(),
'name' => $route->getName(),
'action' => ltrim($route->getActionName(), '\\'),
'middleware' => $this->getMiddleware($route),
]);
}
/**
* Sort the routes by a given element.
*
* @param string $sort
* @param array $routes
* @return array
*/
protected function sortRoutes($sort, array $routes)
{
return Arr::sort($routes, function ($route) use ($sort) {
return $route[$sort];
});
}
/**
* Remove unnecessary columns from the routes.
*
* @param array $routes
* @return array
*/
protected function pluckColumns(array $routes)
{
return array_map(function ($route) {
return Arr::only($route, $this->getColumns());
}, $routes);
}
/**
* Display the route information on the console.
*
* @param array $routes
* @return void
*/
protected function displayRoutes(array $routes)
{
if ($this->option('json')) {
$this->line(json_encode(array_values($routes)));
return;
}
$this->table($this->getHeaders(), $routes);
}
/**
* Get before filters.
*
* @param \Illuminate\Routing\Route $route
* @return string
*/
protected function getMiddleware($route)
{
return collect($route->gatherMiddleware())->map(function ($middleware) {
return $middleware instanceof Closure ? 'Closure' : $middleware;
})->implode(',');
}
/**
* Filter the route by URI and / or name.
*
* @param array $route
* @return array|null
*/
protected function filterRoute(array $route)
{
if (($this->option('name') && ! Str::contains($route['name'], $this->option('name'))) ||
$this->option('path') && ! Str::contains($route['uri'], $this->option('path')) ||
$this->option('method') && ! Str::contains($route['method'], strtoupper($this->option('method')))) {
return;
}
return $route;
}
/**
* Get the table headers for the visible columns.
*
* @return array
*/
protected function getHeaders()
{
return Arr::only($this->headers, array_keys($this->getColumns()));
}
/**
* Get the column names to show (lowercase table headers).
*
* @return array
*/
protected function getColumns()
{
$availableColumns = array_map('strtolower', $this->headers);
if ($this->option('compact')) {
return array_intersect($availableColumns, $this->compactColumns);
}
if ($columns = $this->option('columns')) {
return array_intersect($availableColumns, $this->parseColumns($columns));
}
return $availableColumns;
}
/**
* Parse the column list.
*
* @param array $columns
* @return array
*/
protected function parseColumns(array $columns)
{
$results = [];
foreach ($columns as $i => $column) {
if (Str::contains($column, ',')) {
$results = array_merge($results, explode(',', $column));
} else {
$results[] = $column;
}
}
return $results;
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['columns', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Columns to include in the route table'],
['compact', 'c', InputOption::VALUE_NONE, 'Only show method, URI and action columns'],
['json', null, InputOption::VALUE_NONE, 'Output the route list as JSON'],
['method', null, InputOption::VALUE_OPTIONAL, 'Filter the routes by method'],
['name', null, InputOption::VALUE_OPTIONAL, 'Filter the routes by name'],
['path', null, InputOption::VALUE_OPTIONAL, 'Filter the routes by path'],
['reverse', 'r', InputOption::VALUE_NONE, 'Reverse the ordering of the routes'],
['sort', null, InputOption::VALUE_OPTIONAL, 'The column (domain, method, uri, name, action, middleware) to sort by', 'uri'],
];
}
}
Description
On the cli
php artisan route:list
would show a list of all defined routes in the application. With tenancy perhaps one would usephp tenancy:run route:list
to load in all tenancy requirements.Actual behavior
The list (with or without
tenancy:run
) only shows routes that are defined inroutes/web.php
, not the routes that are defined inroutes/tenants.php
.Expected behavior
The routes defined in
routes/tenants
will also be listed on command line interface.Information
tenancy.php config
webserver.php config
database.php config
.env
routes/web.php
routes/tenants.php
Command line interface