根据请求参数获取应用标识(品牌ID)
定义一个 SubDomain 类封装获取应用标识(品牌ID)
根据子域名获取
定义 getHost
方法获取当前请求的域名,在通过 getBrandIdByHost
找到对应的品牌ID。
因为通过子域名获取品牌ID调用会很频繁,这里使用 Cacheable 注解,避免频繁调用数据库。
代码语言:javascript复制#[Cacheable(prefix: "brand_id_host", ttl: 86400)]
private function getBrandIdByHost(string $host = ''): int
{
return intval(IotBrand::where('sub_domain', $host)
->value('brand_id'));
}
private function getHost($request)
{
try {
$host = $request->getHeader('host');
return $host[0] ?? '';
} catch (Throwable $exception) {
return "";
}
}
根据header传参获取
这个适用于只有一个域名,但是仍要实现多应用访问的情况。例如一个管理后台,但是多个小程序前端,那么就可以在每次请求的header上带上 BrandId
参数。注意:header传参优先级高于子域名。
在 getBrandId
和 getBrand
中,都是先检查header中的 BrandId 参数是否有传,如果有则有限返回。
public function getBrand(): IotBrand
{
$request = Context::get(ServerRequestInterface::class);
$brand_id = intval($request->getHeader('BrandId'));
if ($brand_id > 0) {
$brand = IotBrand::find($brand_id);
if ($brand instanceof IotBrand) {
return $brand;
}
}
$host = $this->getHost($request);
return $this->getBrandByHost($host);
}
public function getBrandId(): int
{
$request = Context::get(ServerRequestInterface::class);
$header_BrandId = $request->getHeader('BrandId');
if (is_array($header_BrandId)) {
$brand_id = intval($header_BrandId[0] ?? 0);
} else {
$brand_id = intval($header_BrandId);
}
if ($brand_id > 0) {
return $brand_id;
}
$host = $this->getHost($request);
return $this->getBrandIdByHost($host);
}
定义Model全局条件
定义 BelongBrandScope 类,来处理model全局条件问题(类似软删的 SoftDeletingScope )。
全局条件应用 apply(Builder $builder, Model $model)
定义 BelongBrandScope 全部条件的应用,让加载了scope的 Model 默认都会带上 apply 定义的条件。
当非http请求(例如队列或计划任务执行)的时候 getBrandId
调用会抛出异常。这时候就默认不添加 全局条件。
public function apply(Builder $builder, Model $model)
{
try {
$brand_id = $this->sub_domain->getBrandId();
if ($brand_id == 0) {
throw new ErrorException('未找到对应品牌');
}
$builder->where('brand_id', $brand_id);
} catch (Throwable) {
}
}
扩展 extend
伴随着全局条件的应用,有些特殊情况也是需要加入一下额外的操作,例如软删需要强制删除的时候。有些操作需要不限制应用标识(品牌ID)的时候。
需要定义属性 protected array $extensions = ['WithoutBrand'];
withoutBrand
向builder
对象注入忽略全局条件方法。将原先默认注入的 GlobalScope 类unset掉。
protected function addWithoutBrand(Builder $builder)
{
$builder->macro('withoutBrand', function (Builder $builder) {
$builder->withoutGlobalScope($this);
return $builder;
});
}
extend 方法
该方法会在Builder对象的 withGlobalScope
中检查是否存在
public function extend(Builder $builder)
{
foreach ($this->extensions as $extension) {
$this->{"add{$extension}"}($builder);
}
}
extend 在 Builder.php 对象会被执行。
代码语言:javascript复制public function withGlobalScope($identifier, $scope)
{
$this->scopes[$identifier] = $scope;
if (method_exists($scope, 'extend')) {
$scope->extend($this);
}
return $this;
}
BelongBrand
定义 BelongBrand 类,封装Model必须要的方法。
注入全局条件 bootBelongBrand
Model 在初始化的时候,都会对boot {类目}的方法执行。
代码语言:javascript复制protected function bootTraits(): void
{
$class = static::class;
$booted = [];
TraitInitializers::$container[$class] = [];
foreach (class_uses_recursive($class) as $trait) {
$method = 'boot' . class_basename($trait);
if (method_exists($class, $method) && ! in_array($method, $booted)) {
forward_static_call([$class, $method]);
$booted[] = $method;
}
if (method_exists($class, $method = 'initialize' . class_basename($trait))) {
TraitInitializers::$container[$class][] = $method;
TraitInitializers::$container[$class] = array_unique(TraitInitializers::$container[$class]);
}
}
}
全局写入 saving
对于所有执行插入操作的。默认写入应用标识(品牌ID),但是注意的是,非HTTP请求会导致 getBrandId
失败。如果是在队列获取计划任务上执行,建议在插入数据直接任务。请求的子协程上也会有同样的问题。
public function saving(Saving $event)
{
if (($this->brand_id ?? 0) == 0) {
$domain_brand_id = (new SubDomain())->getBrandId();
if ($domain_brand_id > 0) {
$this->brand_id = $domain_brand_id;
}
}
}