Closed RushabhJoshi closed 5 years ago
If you are using Laravel's default Authentication, all you need to put a middleware that will tell the Auth to use the tenant database. A brief explanation is given here : Make Laravel out-of-the-box authentication scaffolding work with tenancy
So... something like this? Where $hostname
is the hostname you want to use.
Route::domain($hostname)->group(function () {
Auth::routes(); // if you want all routes from Laravel's Auth scaffolding
// -- OR --
Route::get('password/reset/{token}', 'AuthController@resetPassword')->name('password.reset');
});
If you want it to be tenant-specific, then don't bother adding the route group at all, and it'll be available to your tenant. If that isn't adequate for your needs, then you'll need to provide more specific requirements.
Maybe I am not clear in my previous question description. But what I need is to create the route for one tenant from another tenant
like I have one tenant which only have super admin authority admin.localhost
here, I can create a new user for another tenant which send email for password reset but I am facing one problem here the reset link has domain name admin
http://admin.localhost/password/reset/<token>
but I need a tenant-specific link
http://xyz.localhost/password/reset/<token>
What you could do is update app.url
whenever a tenant was identified.
In addition make sure to add a connection
key in the auth file: https://github.com/laravel/laravel/blob/d53539b47b6d2a20d0c3d3257ad801bc72547c9d/config/auth.php#L98
See https://github.com/hyn/multi-tenant/issues/443
If this doesn't solve it, please re-open.
Seems like I've already added updating the app url automatically:
tenancy.hostname.update-app-url
configuration key 👍
Let me describe to you my problem again
I am in admin.localhost
website
Just created a new admin user for xyz.localhost
website
here I am sending a mail for password reset now here
password reset show the reset link for admin.localhost
not for xyz.localhost
any hack for this?
FYI I have update package to 5.2
@RushabhJoshi send the password reset inside a TenantAwareJob and use the tenant setter to force it to the new tenant; see https://laravel-tenancy.com/docs/hyn/5.2/queues
Everything worked as expected but still password reset link has a domain name admin.localhost
PS I am testing this thing in QUEUE_DRIVER=sync
Did you set tenancy.hostname.update-app-url
to true?
Yes, it still shows http://admin.localhost
maybe it does not work for sync drive of the queue
@luceos any update on this issue?
Have you tried another queue driver?
Yes, I tried with database driver after you asked
and the result changed but not expected.
now the link address is localhost
instead of admin.localhost
maybe I am missing something in a config
tenancy.php
<?php
/*
* This file is part of the hyn/multi-tenant package.
*
* (c) Daniël Klabbers <daniel@klabbers.email>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @see https://laravel-tenancy.com
* @see https://github.com/hyn/multi-tenant
*/
use Hyn\Tenancy\Database\Connection;
return [
'models' => [
/**
* Specify different models to be used for the global, system database
* connection. These are also used in their relationships. Models
* used have to implement their respective contracts and
* either extend the SystemModel or use the trait
* UsesSystemConnection.
*/
// Must implement \Hyn\Tenancy\Contracts\Hostname
'hostname' => \Hyn\Tenancy\Models\Hostname::class,
// Must implement \Hyn\Tenancy\Contracts\Website
'website' => \Hyn\Tenancy\Models\Website::class
],
'website' => [
/**
* Each website has a short random hash that identifies this entity
* to the application. By default this id is randomized and fully
* auto-generated. In case you want to force your own logic for
* when you need to have a better overview of the complete
* tenant folder structure, disable this and implement
* your own id generation logic.
*/
'disable-random-id' => false,
/**
* The random Id generator is responsible for creating the hash as mentioned
* above. You can override what generator to use by modifying this value
* in the configuration.
*
* @warn This won't work if disable-random-id is true.
*/
'random-id-generator' => Hyn\Tenancy\Generators\Uuid\ShaGenerator::class,
/**
* Enable this flag in case you're using a driver that does not support
* database username or database name with a length of more than 32 characters.
*
* This should be enabled for MySQL, but not for MariaDB and PostgreSQL.
*/
'uuid-limit-length-to-32' => env('LIMIT_UUID_LENGTH_32', false),
/**
* Specify the disk you configured in the filesystems.php file where to store
* the tenant specific files, including media, packages, routes and other
* files for this particular website.
*
* @info If not set, will revert to the default filesystem.
* @info If set to false will disable all tenant specific filesystem auto magic
* like the config, vendor overrides.
*/
'disk' => null,
/**
* Automatically generate a tenant directory based on the random id of the
* website. Uses the above disk to store files to override system-wide
* files.
*
* @info set to false to disable.
*/
'auto-create-tenant-directory' => true,
/**
* Automatically rename the tenant directory when the random id of the
* website changes. This should not be too common, but in case it happens
* we automatically want to move files accordingly.
*
* @info set to false to disable.
*/
'auto-rename-tenant-directory' => true,
/**
* Automatically deletes the tenant specific directory and all files
* contained within.
*
* @see
* @info set to true to enable.
*/
'auto-delete-tenant-directory' => false,
/**
* Time to cache websites in minutes. Set to false to disable.
*/
'cache' => 10,
],
'hostname' => [
/**
* If you want the multi tenant application to fall back to a default
* hostname/website in case the requested hostname was not found
* in the database, complete in detail the default hostname.
*
* @warn this must be a FQDN, these have no protocol or path!
*/
'default' => env('TENANCY_DEFAULT_HOSTNAME'),
/**
* The package is able to identify the requested hostname by itself,
* disable to get full control (and responsibility) over hostname
* identification. The hostname identification is needed to
* set a specific website as currently active.
*
* @see src/Jobs/HostnameIdentification.php
*/
'auto-identification' => env('TENANCY_AUTO_HOSTNAME_IDENTIFICATION', true),
/**
* In case you want to have the tenancy environment set up early,
* enable this flag. This will run the tenant identification
* inside a middleware. This will eager load tenancy.
*
* A good use case is when you have set "tenant" as the default
* database connection.
*/
'early-identification' => env('TENANCY_EARLY_IDENTIFICATION', true),
/**
* Abort application execution in case no hostname was identified. This will throw a
* 404 not found in case the tenant hostname was not resolved.
*/
'abort-without-identified-hostname' => true,
/**
* Time to cache hostnames in minutes. Set to false to disable.
*/
'cache' => 10,
/**
* Automatically update the app.url configured inside Laravel to match
* the tenant FQDN whenever a hostname/tenant was identified.
*
* This will resolve issues with password reset mails etc using the
* correct domain.
*/
'update-app-url' => true,
],
'db' => [
/**
* The default connection to use; this overrules the Laravel database.default
* configuration setting. In Laravel this is normally configured to 'mysql'.
* You can set a environment variable to override the default database
* connection to - for instance - the tenant connection 'tenant'.
*/
'default' => env('TENANCY_DEFAULT_CONNECTION'),
/**
* Used to give names to the system and tenant database connections. By
* default we configure 'system' and 'tenant'. The tenant connection
* is set up automatically by this package.
*
* @see src/Database/Connection.php
* @var system-connection-name The database connection name to use for the global/system database.
* @var tenant-connection-name The database connection name to use for the tenant database.
*/
'system-connection-name' => env('TENANCY_SYSTEM_CONNECTION_NAME', Connection::DEFAULT_SYSTEM_NAME),
'tenant-connection-name' => env('TENANCY_TENANT_CONNECTION_NAME', Connection::DEFAULT_TENANT_NAME),
/**
* The tenant division mode specifies to what database websites will be
* connecting. The default setup is to use a new database per tenant.
* In case you prefer to use the same database with a table prefix,
* set the mode to 'prefix'.
*
* @see src/Database/Connection.php
*/
'tenant-division-mode' => env('TENANCY_DATABASE_DIVISION_MODE', 'database'),
/**
* The database password generator takes care of creating a valid hashed
* string used for tenants to connect to the specific database. Do
* note that this will only work in 'division modes' that set up
* a connection to a separate database.
*/
'password-generator' => Hyn\Tenancy\Generators\Database\DefaultPasswordGenerator::class,
/**
* The tenant migrations to be run during creation of a tenant. Specify a directory
* to run the migrations from. If specified these migrations will be executed
* whenever a new tenant is created.
*
* @info set to false to disable auto migrating.
*
* @warn this has to be an absolute path, feel free to use helper methods like
* base_path() or database_path() to set this up.
*/
'tenant-migrations-path' => database_path('migrations/tenant'),
/**
* The default Seeder class used on newly created databases and while
* running artisan commands that fire seeding.
*
* @info requires tenant-migrations-path in order to seed newly created websites.
*
* @warn specify a valid fully qualified class name.
*/
'tenant-seed-class' => App\Seeders\TenantSeeder::class,
// eg an admin seeder under `app/Seeders/AdminSeeder.php`:
// 'tenant-seed-class' => App\Seeders\AdminSeeder::class,
/**
* Automatically generate a tenant database based on the random id of the
* website.
*
* @info set to false to disable.
*/
'auto-create-tenant-database' => true,
/**
* Automatically generate the user needed to access the database.
*
* @info Useful in case you use root or another predefined user to access the
* tenant database.
* @info Only creates in case tenant databases are set to be created.
*
* @info set to false to disable.
*/
'auto-create-tenant-database-user' => true,
/**
* Automatically rename the tenant database when the random id of the
* website changes. This should not be too common, but in case it happens
* we automatically want to move databases accordingly.
*
* @info set to false to disable.
*/
'auto-rename-tenant-database' => true,
/**
* Automatically deletes the tenant specific database and all data
* contained within.
*
* @info set to true to enable.
*/
'auto-delete-tenant-database' => env('TENANCY_DATABASE_AUTO_DELETE', false),
/**
* Automatically delete the user needed to access the tenant database.
*
* @info Set to false to disable.
* @info Only deletes in case tenant database is set to be deleted.
*/
'auto-delete-tenant-database-user' => env('TENANCY_DATABASE_AUTO_DELETE_USER', false),
/**
* Define a list of classes that you wish to force onto the tenant or system connection.
* The connection will be forced when the Model has booted.
*
* @info Useful for overriding the connection of third party packages.
*/
'force-tenant-connection-of-models' => [
// App\User::class
],
'force-system-connection-of-models' => [
// App\User::class
],
],
/**
* Global tenant specific routes.
* Making it easier to distinguish between landing and tenant routing.
*
* @info only works with `tenancy.hostname.auto-identification` or identification happening
* before the application is booted (eg inside middleware or the register method of
* service providers).
*/
'routes' => [
/**
* Routes file to load whenever a tenant was identified.
*
* @info Set to false or null to disable.
*/
'path' => base_path('routes/tenants.php'),
/**
* Set to true to flush all global routes before setting the routes from the
* tenants.php routes file.
*/
'replace-global' => false,
],
/**
* Folders configuration specific per tenant.
* The following section relates to configuration to files inside the tenancy/<uuid>
* tenant directory.
*/
'folders' => [
'config' => [
/**
* Merge configuration files from the config directory
* inside the tenant directory with the global configuration files.
*/
'enabled' => true,
/**
* List of configuration files to ignore, preventing override of crucial
* application configurations.
*/
'blacklist' => ['database', 'tenancy', 'webserver'],
],
'routes' => [
/**
* Allows adding and overriding URL routes inside the tenant directory.
*/
'enabled' => true,
/**
* Prefix all tenant routes.
*/
'prefix' => null,
],
'trans' => [
/**
* Allows reading translation files from a trans directory inside
* the tenant directory.
*/
'enabled' => true,
/**
* Will override the global translations with the tenant translations.
* This is done by overriding the laravel default translator with the new path.
*/
'override-global' => true,
/**
* In case you disabled global override, specify a namespace here to load the
* tenant translation files with.
*/
'namespace' => 'tenant',
],
'vendor' => [
/**
* Allows using a custom vendor (composer driven) folder inside
* the tenant directory.
*/
'enabled' => true,
],
'media' => [
/**
* Mounts the assets directory with (static) files for public use.
*/
'enabled' => true,
],
'views' => [
/**
* Adds the vendor directory of the tenant inside the application.
*/
'enabled' => true,
/**
* Specify a namespace to use with which to load the views.
*
* @eg setting `tenant` will allow you to use `tenant::some.blade.php`
* @info set to null to add to the global namespace.
*/
'namespace' => null,
/**
* If `namespace` is set to null (thus using the global namespace)
* make it override the global views. Disable to
*/
'override-global' => true,
]
]
];
webserver.php
<?php
/*
* This file is part of the hyn/multi-tenant package.
*
* (c) Daniël Klabbers <daniel@klabbers.email>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @see https://laravel-tenancy.com
* @see https://github.com/hyn/multi-tenant
*/
return [
/**
* Apache2 is one of the most widely adopted webserver packages available.
*
* @see http://httpd.apache.org/docs/
* @see https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu
*/
'apache2' => [
/**
* Whether the integration with Apache2 is currently active.
*/
'enabled' => false,
/**
* Define the ports of your Apache service.
*/
'ports' => [
/**
* HTTP, non-SSL port.
*
* @default 80
*/
'http' => 80,
/**
* HTTPS, SSL port.
*
* @default 443
*/
'https' => 443
],
/**
* The generator taking care of hooking into the Apache services and files.
*/
'generator' => \Hyn\Tenancy\Generators\Webserver\Vhost\ApacheGenerator::class,
/**
* The view that holds the vhost configuration template.
*/
'view' => 'tenancy.generators::webserver.apache.vhost',
/**
* Specify the disk you configured in the filesystems.php file where to store
* the tenant vhost configuration files.
*
* @info If not set, will revert to the default filesystem.
*/
'disk' => null,
'paths' => [
/**
* Location where vhost configuration files can be found.
*/
'vhost-files' => [
'/etc/apache2/sites-enabled/'
],
/**
* Actions to run to work with the Apache2 service.
*/
'actions' => [
/**
* Action that asserts Apache2 is installed.
*/
'exists' => '/etc/init.d/apache2',
/**
* Action to run to test the apache configuration.
*
* @set to a boolean to force the response of the test command.
* @info true succeeds, false fails
*/
'test-config' => 'apache2ctl -t',
/**
* Action to run to reload the apache service.
*
* @info set to null to disable reloading.
*/
'reload' => 'apache2ctl graceful'
]
]
],
/**
* Nginx webserver support.
*
* @see http://nginx.org
*/
'nginx' => [
/**
* Whether the integration with nginx is currently active.
*/
'enabled' => false,
/**
* The php sock to be used.
*/
'php-sock' => 'unix:/var/run/php/php7.1-fpm.sock',
/**
* Define the ports of your nginx service.
*/
'ports' => [
/**
* HTTP, non-SSL port.
*
* @default 80
*/
'http' => 80,
/**
* HTTPS, SSL port.
*
* @default 443
*/
'https' => 443
],
/**
* The generator taking care of hooking into the nginx services and files.
*/
'generator' => \Hyn\Tenancy\Generators\Webserver\Vhost\NginxGenerator::class,
/**
* The view that holds the vhost configuration template.
*/
'view' => 'tenancy.generators::webserver.nginx.vhost',
/**
* Specify the disk you configured in the filesystems.php file where to store
* the tenant vhost configuration files.
*
* @info If not set, will revert to the default filesystem.
*/
'disk' => null,
'paths' => [
/**
* Location where vhost configuration files can be found.
*/
'vhost-files' => [
'/etc/nginx/sites-enabled/'
],
/**
* Actions to run to work with the Nginx service.
*/
'actions' => [
/**
* Action that asserts nginx is installed.
*/
'exists' => '/etc/init.d/nginx',
/**
* Action to run to test the nginx configuration.
*
* @info set to a boolean to force the response of the test command.
* true succeeds, false fails
*/
'test-config' => '/etc/init.d/nginx configtest',
/**
* Action to run to reload the nginx service.
*
* @info set to null to disable reloading.
*/
'reload' => '/etc/init.d/nginx reload'
]
]
]
];
queue.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Driver
|--------------------------------------------------------------------------
|
| Laravel's queue API supports an assortment of back-ends via a single
| API, giving you convenient access to each back-end using the same
| syntax for each one. Here you may set the default queue driver.
|
| Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'default' => env('QUEUE_DRIVER', 'sync'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection information for each server that
| is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more.
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'connection' => 'system',
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
],
'sqs' => [
'driver' => 'sqs',
'key' => 'your-public-key',
'secret' => 'your-secret-key',
'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id',
'queue' => 'your-queue-name',
'region' => 'us-east-1',
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90,
],
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control which database and table are used to store the jobs that
| have failed. You may change them to any database / table you wish.
|
*/
'failed' => [
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],
];
Can you dd config('app.url')
inside your job when fired not as sync?
yes, I did and its http://admin.localhost
My apologies for the delay.
Without more code it's hard to know what is happening here. Feel free to share the repo with me privately or add snippets of how you send the reset password mail.
I am sharing you the tenant aware job which called after creating tenant
in command, I am calling job like this
$website = new Website;
app(WebsiteRepository::class)->create($website);
//Create and connect hostname
$hostname = new Hostname;
$hostname->fqdn = $this->argument('fqdn');
app(HostnameRepository::class)->attach($hostname, $website);
$tenantData = [
'admin' => [
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => 'password',
],
];
$initJob = new InitTenant($tenantData, true);
dispatch($initJob->onTenant($website));
Tenant aware job
<?php
namespace App\Jobs;
use App\AdditionalService;
use App\Notifications\NewUserResetPassword;
use App\Permission;
use App\Role;
use App\User;
use Hyn\Tenancy\Environment;
use Hyn\Tenancy\Queue\TenantAwareJob;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Password;
class InitTenant implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, TenantAwareJob;
public $data;
public $adminTenant;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($data, $adminTenant = false)
{
$this->data = $data;
$this->adminTenant = $adminTenant;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$this->seed();
// Create Tenant admin
$password = str_random(20);
$adminUser = User::create([
'name' => $this->data['admin']['name'],
'email' => $this->data['admin']['email'],
'password' => bcrypt($password),
]);
$passwordBroker = Password::broker();
$token = $passwordBroker->createToken($adminUser);
$adminUser->notify(new NewUserResetPassword($token));
// Assing tenant-admin role to user
if ($this->adminTenant) {
$adminUser->assignRole('superuser');
}
$adminUser->assignRole('admin');
}
private function seed()
{
$this->seedRoles();
$this->seedPermissions();
/////////////////////
// Set permissions //
/////////////////////
$this->setSuperUserPermissions();
$this->setTenantAdminPermissions();
}
private function seedRoles()
{
$roles = Role::DEFAULT_ROLES;
foreach ($roles as $role) {
Role::create([
'name' => $role,
]);
}
}
private function seedPermissions()
{
$permissions = Permission::DEFAULT_PERMISSIONS;
foreach ($permissions as $permission) {
Permission::create([
'name' => $permission,
]);
}
}
private function setSuperUserPermissions()
{
$role = Role::findByName('superuser');
// Add all permissions to tenant-admin
foreach (Permission::DEFAULT_PERMISSIONS as $permission) {
$role->givePermissionTo($permission);
}
}
private function setTenantAdminPermissions()
{
$role = Role::findByName('admin');
$except = ['manage_tenants'];
$permissions = array_diff(Permission::DEFAULT_PERMISSIONS, $except);
// Add all permissions to tenant-admin
foreach ($permissions as $permission) {
$role->givePermissionTo($permission);
}
}
}
Tell me if you need anything more. I will consult with an authorized person to share repo till than I have shared a code snippet
Please share the code for NewUserResetPassword
<?php
namespace App\Notifications;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class NewUserResetPassword extends Notification
{
public $token;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = route('password.set', $this->token);
return (new MailMessage)
->line('You are just got registered')
->action('Click here to set password', $url)
->line('Thank you');
}
}
Apply TenantAwareJob
to the NewUserResetPassword
class too.
I did and still the same
I have created foo.localhost so password set link should be something like http://foo.localhost/password/set/<token>
but it is still wrong
http://admin.localhost/password/set/0b3bc4c1f87e1a602148ea626d45831d1fbb380090ded1133113342b88fc424f
I'm not sure. I see three stages:
Please dump the tenant in each step to see which tenant is identified, by adding the following to the __construct or handle of each of the above steps:
logger(__CLASS__, app(\Hyn\Tenancy\Environment::class)->tenant());
This time I have updated the Laravel version 5.7 and package version to 5.3
Note: I have little change in your code snippet which is
logger(__CLASS__, ['tenant' => app(\Hyn\Tenancy\Environment::class)->tenant()]);
the same story happened
[2018-10-09 10:58:31] local.DEBUG: App\Http\Controllers\Admin\TenantController {"tenant":"[object] (Hyn\\Tenancy\\Models\\Website: {\"id\":1,\"uuid\":\"7cc0e34739874ddd860e470a02132d5a\",\"created_at\":\"2018-10-09 10:44:45\",\"updated_at\":\"2018-10-09 10:44:45\",\"deleted_at\":null,\"managed_by_database_connection\":null})"}
[2018-10-09 10:58:58] local.DEBUG: App\Jobs\InitTenant {"tenant":"[object] (Hyn\\Tenancy\\Models\\Website: {\"id\":2,\"uuid\":\"37141bbfe3aa41d18d17ea13b57377a6\",\"created_at\":\"2018-10-09 10:58:31\",\"updated_at\":\"2018-10-09 10:58:31\",\"deleted_at\":null,\"managed_by_database_connection\":null})"}
[2018-10-09 10:59:02] local.DEBUG: App\Notifications\NewUserResetPassword {"tenant":"[object] (Hyn\\Tenancy\\Models\\Website: {\"id\":2,\"uuid\":\"37141bbfe3aa41d18d17ea13b57377a6\",\"created_at\":\"2018-10-09 10:58:31\",\"updated_at\":\"2018-10-09 10:58:31\",\"deleted_at\":null,\"managed_by_database_connection\":null})"}
here first log is in controller and other as you said 7cc0e34739874ddd860e470a02132d5a is for admin.localhost 37141bbfe3aa41d18d17ea13b57377a6 is for foo.localhost
yes this is what expected but the route is still false
http://admin.localhost/password/set/fe9959db0e4f92fdd75a442887556e02e0a28b3d904677a399c6ffaaace1c14a
Possibly what happens is that updating the app url is misbehaving. We need to dive into this. Thanks for your patience.
Have you tried this?
URL::forceRootUrl('<system url>');
before creating the route in the mail?
Is admin.localhost
is a system
or tenant
?
If it's a system
, you may have identical route alias called password.set
but Laravel only read admin.localhost
's.
If it's an another tenant
in a class which use TenantAwareJob
, make sure current config set for the right website. As documented in https://laravel-tenancy.com/docs/hyn/5.3/identification#manual-identification you can set tenant connection (also see warning):
$website = app('Hyn\Tenancy\Environment')->tenant();
// dd($website); // otherwise set to $targetWebsite:
$website = app('Hyn\Tenancy\Environment')->tenant($targetWebsite);
Then call Laravel route
:
return route('password.reset', [
'token' => 'a-reset-token'
]);
or with subdomain routing described here: https://laravel-tenancy.com/docs/hyn/5.3/fallback#routing-with-domain
return route('password.reset', [
'token' => 'a-reset-token',
'customerDomain' => 'xyz', // 'xyz' is subdomain
]);
URL::forceRootUrl('
');
What could be the system url?
admin.localhost
or newly created tenant url?
Is
admin.localhost
is asystem
ortenant
?If it's a
system
, you may have identical route alias calledpassword.set
but Laravel only readadmin.localhost
's.If it's an another
tenant
in a class which useTenantAwareJob
, make sure current config set for the right website. As documented in https://laravel-tenancy.com/docs/hyn/5.3/identification#manual-identification you can set tenant connection (also see warning):$website = app('Hyn\Tenancy\Environment')->tenant(); // dd($website); // otherwise set to $targetWebsite: $website = app('Hyn\Tenancy\Environment')->tenant($targetWebsite);
Then call Laravel
route
:return route('password.reset', [ 'token' => 'a-reset-token' ]);
or with subdomain routing described here: https://laravel-tenancy.com/docs/hyn/5.3/fallback#routing-with-domain
return route('password.reset', [ 'token' => 'a-reset-token', 'customerDomain' => 'xyz', // 'xyz' is subdomain ]);
Yes @nevertry admin.localhost is tenant, only this tenant can create other tenants. when I create new tenant I call tenent aware job InitTenant. Where first
$passwordBroker->createToken($adminUser)
$admin->notify(new NewUserResetPassword($token))
Everything works fine, seeding, creating user and mail as well
one little problem is in mail password set link is http://admin.localhost/password/set/5f6f02caefc3cf192674af2ba088acbeb03ec9349331b0c32b5667eed8377e68
instead it should be tenant password set link (in my case tenant is xyz) http://xyz.localhost/password/set/5f6f02caefc3cf192674af2ba088acbeb03ec9349331b0c32b5667eed8377e68
PS: I don't know why you need me to set environment When I am using tenant aware job
I have hacked this problem with a Listener in my application that set the app.url
upon website identification and -switch.
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
// ...
'Hyn\Tenancy\Events\Websites\Identified' => [
'App\Listeners\SetAppName',
'App\Listeners\SetAppUrl',
],
'Hyn\Tenancy\Events\Websites\Switched' => [
'App\Listeners\SetAppName',
'App\Listeners\SetAppUrl',
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();
//
}
}
<?php
namespace App\Listeners;
use Hyn\Tenancy\Contracts\Hostname;
use Illuminate\Support\Facades\URL;
class SetAppUrl
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
/** @var Hostname $hostname */
if ($hostname = $this->findHostname($event)) {
$protocol = $hostname->force_https ? 'https://' : 'http://';
$url = "{$protocol}{$hostname->fqdn}";
config(['app.url' => $url]);
URL::forceRootUrl($url);
}
}
/**
* @param $event
* @return mixed
*/
protected function findHostname($event)
{
// todo make more controllable, not only first...
return $event->website->hostnames()->first();
}
}
Now I can generate correct urls in queued jobs (like mails), that are defined in routes/web.php
.
$website = app(\Hyn\Tenancy\Contracts\Repositories\WebsiteRepository::class)->findById(2); // per example.
Mail::to($email)->send((new Payslip($this))->onTenant($website));
Urls in Payslip
are generated correctly, like $url = route('login')
:
@ametad, unless I'm misunderstanding, that's already a feature we have. Just set set-app-url
(I think that's the name at least) to true in the tenancy config file
I'm in the process of pushing a change that ensures the app url is also updated outside of HTTP requests.
@fletch3555 Yes I know the feature, thanks! (config key is 'update-app-url')
But because this is in middleware (\Hyn\Tenancy\Middleware\HostnameActions
), this functionality does not work in queued job. Also the cli context does not know of any Hostname, only Website is known. Trough Website the Hostname(s) is/are known of course.
This should be fixed by now as the package now changes url whenever a tenant is identified.
I need a correct way to create routes for hostname
e.g. I need to create a route for the password reset for hostname XYZ
which will generate appropriate route for password reset for that particular hostname
A solution for above requirement is also helpful, Please share if you have any
Note: tried changing the environment for that hostname but it won't work