前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Hcms 如何实现子域名-多应用

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

原创
作者头像
黄振炼
发布2023-08-30 20:03:43
1730
发布2023-08-30 20:03:43
举报
文章被收录于专栏:Hcms

根据请求参数获取应用标识(品牌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;
            }
        }
    }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 根据请求参数获取应用标识(品牌ID)
    • 根据子域名获取
      • 根据header传参获取
      • 定义Model全局条件
        • 全局条件应用 apply(Builder $builder, Model $model)
          • 扩展 extend
            • withoutBrand
            • extend 方法
        • BelongBrand
          • 注入全局条件 bootBelongBrand
            • 全局写入 saving
            相关产品与服务
            云开发 CloudBase
            云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档