Hcms 如何实现子域名-多应用

2023-08-30 20:03:43 浏览数 (2)

根据请求参数获取应用标识(品牌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传参优先级高于子域名。

getBrandIdgetBrand中,都是先检查header中的 BrandId 参数是否有传,如果有则有限返回。

代码语言:javascript复制
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调用会抛出异常。这时候就默认不添加 全局条件。

代码语言:javascript复制
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掉。

代码语言:javascript复制
    protected function addWithoutBrand(Builder $builder)
    {
        $builder->macro('withoutBrand', function (Builder $builder) {
            $builder->withoutGlobalScope($this);

            return $builder;
        });
    }

extend 方法

该方法会在Builder对象的 withGlobalScope 中检查是否存在

代码语言:javascript复制
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失败。如果是在队列获取计划任务上执行,建议在插入数据直接任务。请求的子协程上也会有同样的问题。

代码语言:javascript复制
    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;
            }
        }
    }

0 人点赞