利用laravel重置密码功能: Password Resets
php artisan make:notification TenantCreated
<?php
namespace App\Notifications;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Password;
class TenantCreated extends Notification
{
private $hostname;
public function __construct($hostname)
{
$this->hostname = $hostname;
}
public function via()
{
return ['mail'];
}
public function toMail($notifiable)
{
$token = Password::broker()->createToken($notifiable);
$resetUrl = "https://{$this->hostname->fqdn}/password/reset/{$token}";
$app = config('app.name');
return (new MailMessage())
->subject("{$app} Invitation")
->greeting("Hello {$notifiable->name},")
->line("You have been invited to use {$app}!")
->line('To get started you need to set a password.')
->action('Set password', $resetUrl);
}
}
然后修改 CreateTenant.php#handle():
...
$password = str_random();
$this->addAdmin($name, $email, $password)
->notify(new TenantCreated($hostname)); // 触发通知
$this->info("Tenant '{$name}' is created and is now accessible at {$hostname->fqdn}");
$this->info("Admin {$email} has been invited!");
...
运行测试一下:
“Route [password.reset] not defined.”
还没有建立用户验证模块,执行:
php artisan make:auth

填写提交:
QueryException about missing password_resets table !!!
找不到 相关 数据表。
这是因为 系统 配置 routes/web.php 是查找系统 数据库,而不是租户的数据库。所以要修改 转向到 租户自己的数据库:
方法:利用 Laravel middleware:
php artisan make:middleware EnforceTenancy
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Config;
class EnforceTenancy
{
public function handle($request, Closure $next)
{
Config::set('database.default', 'tenant');
return $next($request);
}
}
应用中间件 middleware, 配置app/Http/Kernel.php
...
protected $routeMiddleware = [
...
'tenancy.enforce' => \App\Http\Middleware\EnforceTenancy::class
];...
配置路由中间件:routes/web.php
Route::group(['middleware' => 'tenancy.enforce'], function () {
Auth::routes();
});
现在再测试一下:

现在再回顾,重构一下:
新建文件 app/Tenant.php
<?php
namespace App;
use Hyn\Tenancy\Environment;
use Hyn\Tenancy\Models\Customer;
use Hyn\Tenancy\Models\Hostname;
use Hyn\Tenancy\Models\Website;
use Illuminate\Support\Facades\Hash;
use Hyn\Tenancy\Contracts\Repositories\CustomerRepository;
use Hyn\Tenancy\Contracts\Repositories\HostnameRepository;
use Hyn\Tenancy\Contracts\Repositories\WebsiteRepository;
/**
* @property Customer customer
* @property Website website
* @property Hostname hostname
* @property User admin
*/
class Tenant
{
public function __construct(Customer $customer, Website $website = null, Hostname $hostname = null, User $admin = null)
{
$this->customer = $customer;
$this->website = $website ?? $customer->websites->first();
$this->hostname = $hostname ?? $customer->hostnames->first();
$this->admin = $admin;
}
public function delete()
{
app(HostnameRepository::class)->delete($this->hostname, true);
app(WebsiteRepository::class)->delete($this->website, true);
app(CustomerRepository::class)->delete($this->customer, true);
}
public static function createFrom($name, $email): Tenant
{
// create a customer
$customer = new Customer;
$customer->name = $name;
$customer->email = $email;
app(CustomerRepository::class)->create($customer);
// associate the customer with a website
$website = new Website;
$website->customer()->associate($customer);
app(WebsiteRepository::class)->create($website);
// associate the website with a hostname
$hostname = new Hostname;
$baseUrl = config('app.url_base');
$hostname->fqdn = "{$name}.{$baseUrl}";
$hostname->customer()->associate($customer);
app(HostnameRepository::class)->attach($hostname, $website);
// make hostname current
app(Environment::class)->hostname($hostname);
$admin = static::makeAdmin($name, $email, str_random());
return new Tenant($customer, $website, $hostname, $admin);
}
private static function makeAdmin($name, $email, $password): User
{
$admin = User::create(['name' => $name, 'email' => $email, 'password' => Hash::make($password)]);
$admin->guard_name = 'web';
$admin->assignRole('admin');
return $admin;
}
public static function retrieveBy($name): ?Tenant
{
if ($customer = Customer::where('name', $name)->with(['websites', 'hostnames'])->first()) {
return new Tenant($customer);
}
return null;
}
}
<?php
namespace App\Console\Commands;
use App\Notifications\TenantCreated;
use App\Tenant;
use Hyn\Tenancy\Models\Customer;
use Illuminate\Console\Command;
class CreateTenant extends Command
{
protected $signature = 'tenant:create {name} {email}';
protected $description = 'Creates a tenant with the provided name and email address e.g. php artisan tenant:create boise boise@example.com';
public function handle()
{
$name = $this->argument('name');
$email = $this->argument('email');
if ($this->tenantExists($name, $email)) {
$this->error("A tenant with name '{$name}' and/or '{$email}' already exists.");
return;
}
$tenant = Tenant::createFrom($name, $email);
$this->info("Tenant '{$name}' is created and is now accessible at {$tenant->hostname->fqdn}");
// invite admin
$tenant->admin->notify(new TenantCreated($tenant->hostname));
$this->info("Admin {$email} has been invited!");
}
private function tenantExists($name, $email): bool
{
return Customer::where('name', $name)->orWhere('email', $email)->exists();
}
}
<?php
namespace App\Console\Commands;
use App\Tenant;
use Illuminate\Console\Command;
class DeleteTenant extends Command
{
protected $signature = 'tenant:delete {name}';
protected $description = 'Deletes a tenant of the provided name. Only available on the local environment e.g. php artisan tenant:delete boise';
public function handle()
{
// because this is a destructive command, we'll only allow to run this command
// if the environment is local or testing
if (!(app()->isLocal() || app()->runningUnitTests())) {
$this->error('This command is only avilable on the local environment.');
return;
}
$name = $this->argument('name');
if ($tenant = Tenant::retrieveBy($name)) {
$tenant->delete();
$this->info("Tenant {$name} successfully deleted.");
} else {
$this->error("Couldn't find tenant {$name}");
}
}
}