【问题标题】:Is there another way to "setConnection" on an Eloquent Model?是否有另一种方法可以在 Eloquent 模型上“设置连接”?
【发布时间】:2017-05-07 11:17:00
【问题描述】:

我目前正在处理“多数据库即时交换连接”之类的项目。

所以我最终要做的是:

$connectionName = uniqid();
\Config::set('database.connections.' . $connectionName, [/** db options **/]);
\Artisan::call('migrate', ['--database' => $connectionName]);

$connectionName = uniqid();           
\Config::set('database.connections.' . $connectionName,[/** db options **/]);

$user = new User();
$user->setConnection($connectionName);
$user->first_name = 'Daisy';
$user->last_name = 'Demo';
$user->is_not_being_ignored_by_santa_this_year = 0;
$user->email = //and so so on
$user->save();

对于 Artisan 调用,我有点理解为什么 Laravel 需要引用保存在配置数组中的字符串中的连接。

但是在 Eloquent 模型本身上,我发现必须将我的数据库连接写入配置数组有点麻烦。所以它可以通过模型中的“单例方法”\Config::get().. 来获取。

有没有更优雅的东西,我可以直接注入配置而无需将其写入某个超级全局?

还是我错过了什么?

【问题讨论】:

  • 一个MySQL实例有多个DATABASEs?还是多个单独的连接参数?
  • 一个连接,但不同的数据库

标签: php mysql laravel-5 eloquent


【解决方案1】:

creating a configuration array for each connection 可能会更好,然后您可以通过指定要使用的连接轻松地在连接之间切换。

如果您需要在同一型号上使用多个连接,您可以使用on method

所以它会像User::on('dbconnection2')->find(1)

如果只是想为不同的模型使用不同的连接,可以在模型上设置protected $connection属性:

class User extends Model
{
    protected $connection = 'dbconnection2';
}

希望对您有所帮助。

【讨论】:

    【解决方案2】:

    您可以为您的模型创建一个工厂,并在引导您的应用时将连接传递给它:

    <?php
    
    class ModelFactory
    {
        private $db;
    
        public function __construct($dbConnection)
        {
            $this->db = $dbConnection;
        }
    
        public function createNewModel($class)
        {
            $object = new $class();
            $object->setConnection($this->db);
            return $object;
        }
    }
    

    然后在你的代码中:

    $user = $factory->createModel(User::class);
    

    像这样!祝你好运! :-)

    【讨论】:

    • 这很漂亮。
    【解决方案3】:

    我构建了一个多租户 laravel 应用程序,但很惊讶没有开箱即用的方法。

    我有一个可通过不同子域使用的应用程序,子域应该是不同配置的关键,例如数据库连接。

    您可以轻松地将其调整为您需要的任何标准,而不是子域。

    通过动态 Config::set()

    所以我的第一次尝试是始终使用“默认”连接并创建一个动态调用Config::set("database.connection.default.host", "1.3.5.7"); 等所有其他设置的中间件。

    但也有一些缺点。例如,它非常慢,因为我从数据库中读取了所有值,后来又从 redis 中读取。但是更大的问题是例如设置到 redis 缓存的连接,因为在调用中间件覆盖配置设置之前,redis 连接已经建立。

    通过自己的配置文件

    所以我的第二种也是目前的方法是通过配置文件来实现。所以我创建了以下文件结构:

    • config/_myapp.php
    • config/subdomain1/_myapp.php

    注意:下划线很重要,因为文件是按字母顺序读取的,我们的新文件必须首先读取才能在其他配置文件中使用。

    目标是使用config('_myapp.DB_HOST') 从 config/subdomain1/_myapp.php 中检索 DB_HOST 值,并将其用作 config/database.php 中的值。 所以 config/_myapp.php 只返回特定子域的 _myapp.php 内容。

    在配置/_myapp.php 中:

    // get the current subdomain from $_SERVER['HTTP_HOST']
    $subdomain = \App\Helper::getSubDomain();
    
    define('SUBDOMAIN', $subdomain);
    
    if(!empty($subdomain) && file_exists($file = __DIR__."/$subdomain/".basename(__FILE__)))
        return require($file);
    
    return [
    
    ];
    

    并在 config/_myapp.php 中:

    return [
        ...
        'DB_HOST' => '1.3.5.7',
        ...
    ];
    

    例如在 config/database.php 或任何您需要域特定配置的地方使用它:

    ...
       'host' => config('_myapp.DB_HOST'),
    ...
    

    请询问是否有问题,我花了很长时间才弄清楚。

    【讨论】:

      【解决方案4】:

      这完全取决于您如何处理多个连接。我从事过类似的需求项目。

      • 我们有主从/租户数据库连接。配置https://gist.github.com/safoorsafdar/c6c623f9ec7b440f563d76995faf7aec#file-database-php
      • 租户数据库/迁移/种子通过在系统中创建新帐户即时创建
      • 可以从根控件软删除数据库。
      • 在用户登录时,我们检查它是否是master,连接到master连接,否则获取租户连接,将信息存储在会话中以供Web访问。检查Tenant Setup after login
      • 数据库连接相关操作中心类。查看DatabaseConnection
      • 中心租户会话处理程序类,以帮助从会话中解析租户 ID。查看TenantContextSession
      • 我们有单独的模型和抽象类供租户和主人设置模型连接。检查
      • 您可能还需要在系统中处理迁移,请查看此内容 https://gist.github.com/safoorsafdar/cc9252a2f14301c3da942ca7bec7d66e

      租户模型抽象类

      <?php
      
      namespace App\Models\Abstracts;
      
      use App\Models\Abstracts\AbstractBaseModel;
      use App\Models\Picklist\PicklistValue;
      
      class TenantAbstractBaseModel extends AbstractBaseModel
      {
          function __construct(array $attributes = array())
          {
              parent::__construct($attributes);
              if ( ! is_null(app('tenant.context')->getConnectionName())) {
                  $this->setConnection(app('tenant.context')->getConnectionName());
              }
      
              //todo; should be dynamic
              if (is_null(app('tenant.context')->getConnectionName())
                  && app()->runningInConsole()
              ) {
                  //todo; need to resolve database connection through terminal and application.
                  //dd(config('tenant.tenant_connection'));
                  //$this->setConnection(config('tenant.tenant_connection'));
              }
      
          }
      }
      

      登录后的租户设置

      $connection = config('tenant.tenant_connection');
                      //config()->set('database.default', config('tenant.tenant_connection'));
                  app('tenant.context')->setConnectionName($connection);
                  app('tenant.context')->setTenantId($company_id);
      
                   //$database = config('database.connections.' . $connection . '.database') . $company_id;
                   $company_system_name
                          = $this->auth->user()->company->system_name;
                   config()->set('database.connections.'.$connection.'.database',
                          $company_system_name);
                   //config()->set('database.connections.' . $connection . '.database', $database);
                 config()->set('database.default', $connection);
      

      数据库连接

      <?php
      
      namespace App\Tenancy\Tenant;
      
      use Config;
      use DB;
      use App\Tenancy\Exceptions\TenantDatabaseException;
      use App\Tenancy\Models\Tenant;
      
      /**
       * Class DatabaseConnection
       *
       * Helps with tenant database connections
       */
      class DatabaseConnection
      {
          /**
           * See the multi-tenant configuration file. Configuration set
           * to use separate databases.
           */
          const TENANT_MODE_SEPARATE_DATABASE = 'database';
      
          /**
           * See the multi-tenant configuration file. Configuration set
           * to use prefixed table in same database.
           */
          const TENANT_MODE_TABLE_PREFIX = 'prefix';
          /**
           * Current active global tenant connection.
           *
           * @var string
           */
          protected static $current;
          /**
           * @var string
           */
          public $name;
          /**
           * @var Tenant
           */
          protected $tenant;
          /**
           * @var \Illuminate\Database\Connection
           */
          protected $connection;
      
          public function __construct(Tenant $tenant)
          {
              $this->tenant = $tenant;
      
              $this->name = "tenant.{$this->tenant->hash_id}";
      
              $this->setup();
          }
      
          /**
           * Sets the tenant database connection.
           */
          public function setup()
          {
              Config::set("database.connections.{$this->name}", $this->config());
          }
      
          /**
           * Generic configuration for tenant.
           *
           * @return array
           */
          protected function config()
          {
              $clone             = Config::get(sprintf('database.connections.%s',
                  static::tenantConnectionName()));
              $clone['database'] = $this->tenant->system_name;
      
              return $clone;
          }
      
          /**
           * Central getter for system connection name.
           *
           * @return string
           */
          public static function systemConnectionName()
          {
              return Config::get('tenant.master_connection', 'mysql');
          }
      
          /**
           * Checks whether current connection is set as global tenant connection.
           *
           * @return bool
           */
          public function isCurrent()
          {
              return $this->name === static::getCurrent();
          }
      
          /**
           * Loads the currently set global tenant connection name.
           *
           * @return string
           */
          public static function getCurrent()
          {
              return static::$current;
          }
      
          /**
           * Sets current global tenant connection.
           */
          public function setCurrent()
          {
              static::$current = $this->name;
              Config::set(sprintf('database.connections.%s',
                  static::tenantConnectionName()), $this->config());
      
              DB::purge(static::tenantConnectionName());
          }
      
          /**
           * Central getter for tenant connection name.
           *
           * @return string
           */
          public static function tenantConnectionName()
          {
              return Config::get('tenant.tenant_connection', 'tenant_mysql');
          }
      
          /**
           * Loads connection for this database.
           *
           * @return \Illuminate\Database\Connection
           */
          public function get()
          {
              if (is_null($this->connection)) {
                  $this->setup();
                  $this->connection = DB::connection($this->name);
              }
      
              return $this->connection;
          }
      
          /**
           * @return bool
           */
          public function create()
          {
              $clone = $this->config();
      
              return DB::connection(static::systemConnectionName())
                  ->transaction(function () use ($clone) {
                      if ( ! DB::connection(static::systemConnectionName())
                          ->statement("create database if not exists `{$clone['database']}`")
                      ) {
                          throw new TenantDatabaseException("Could not create database {$clone['database']}");
                      }
                      if ( ! DB::connection(static::systemConnectionName())
                          ->statement("grant all on `{$clone['database']}`.* to `{$clone['username']}`@'{$clone['host']}' identified by '{$clone['password']}'")
                      ) {
                          throw new TenantDatabaseException("Could not create or grant privileges to user {$clone['username']} for {$clone['database']}");
                      }
      
                      return true;
                  });
          }
      
          /**
           * @throws \Exception
           *
           * @return bool
           */
          public function delete()
          {
              $clone = $this->config();
      
              return DB::connection(static::systemConnectionName())
                  ->transaction(function () use ($clone) {
                      if ( ! DB::connection(static::systemConnectionName())
                          ->statement("revoke all on `{$clone['database']}`.* from `{$clone['username']}`@'{$clone['host']}'")
                      ) {
                          throw new TenantDatabaseException("Could not revoke privileges to user {$clone['username']} for {$clone['database']}");
                      }
                      if ( ! DB::connection(static::systemConnectionName())
                          ->statement("drop database `{$clone['database']}`")
                      ) {
                          throw new TenantDatabaseException("Could not drop database {$clone['database']}");
                      }
      
                      return true;
                  });
          }
      }
      

      *TenantContextSession *

      <?php
      
      namespace App\Repositories\Tenant;
      
      use App\Repositories\Tenant\TenantContextRepositoryContract;
      
      /**
       * Description of TenantContextSession
       *
       * @author safoor
       */
      class TenantContextSession implements TenantContextRepositoryContract
      {
      //    public function __construct() {
      //        $this->clearTenantSession();
      //    }
      
          /**
           * Sets the connection name for the actual context
           * this tenant
           * @param $name
           * @return mixed
           */
          public function setConnectionName($name)
          {
              if (session()->has('tenant_connection')) {
                  session()->set('tenant_connection', '');
              }
      
              session()->put('tenant_connection', $name);
          }
      
          /**
           * Get the name of the current connection in context
           * @return mixed
           */
          public function getConnectionName()
          {
              return session()->get('tenant_connection');
          }
      
          /**
           * Sets the id value filter data in the current context
           * @param $id
           * @return mixed
           */
          public function setTenantId($id)
          {
              if (session()->has('tenant_id')) {
                  session()->set('tenant_id', '');
              }
              session()->put('tenant_id', $id);
          }
      
          /**
           *
           * @return mixed
           */
          public function getTenantId()
          {
              return session()->get('tenant_id');
          }
      
          public function clearTenantSession()
          {
              session()->flush();
              session()->set('tenant_id', '');
              session()->set('tenant_connection', '');
          }
      }
      

      我希望这将有助于您的场景,我已尝试提及我们必须涵盖的所有方面,但如果仍有一些令人困惑的地方需要我的解释,请告诉我。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-13
        • 1970-01-01
        • 2011-07-28
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多