簡體   English   中英

有沒有另一種方法可以在 Eloquent 模型上“設置連接”?

[英]Is there another way to “setConnection” on an Eloquent Model?

我目前正在處理“多數據庫動態交換連接”類型的項目。

所以我最終做的是以下內容:

$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().. 來獲取。

有沒有更優雅的東西,我可以直接注入配置而不必將其寫入一些超級全局?

或者我錯過了什么?

您可能最好為每個連接創建一個配置數組,然后您可以通過指定要使用的連接輕松地在連接之間切換。

如果您需要在同一模型上使用多個連接,您可以使用on方法

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

如果只想為不同的模型使用不同的連接,可以在模型上設置 protected $connection屬性:

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

希望有幫助。

您可以為您的模型創建一個工廠,並在引導您的應用程序時將連接傳遞給它:

<?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);

像這樣的東西! 祝你好運! :-)

我構建了一個多租戶 laravel 應用程序,並驚訝於沒有開箱即用的方法來做到這一點。

我有一個通過不同子域可用的應用程序,子域應該是不同配置的關鍵,比如數據庫連接。

您可以輕松地將其調整為您需要的任何標准,而不是子域。

通過動態 Config::set()

所以我的第一次嘗試是始終使用“默認”連接並創建一個動態調用Config::set("database.connection.default.host", "1.3.5.7");的中間件Config::set("database.connection.default.host", "1.3.5.7"); 等等所有其他設置。

但也有一些缺點。 例如,它非常慢,因為我從數據庫讀取所有值,然后從 redis 讀取。 但是更大的問題是例如設置到 redis 緩存的連接,因為在調用中間件覆蓋配置設置之前已經建立了 redis 連接。

通過自己的配置文件

所以我的第二個也是當前的方法是通過配置文件來完成這一切。 所以我創建了以下文件結構:

  • 配置/_myapp.php
  • 配置/子域1/_myapp.php

注意:下划線很重要,因為文件是按字母順序讀取的,我們的新文件必須首先讀取才能在其他配置文件中使用它。

目標是使用config('_myapp.DB_HOST')從 config/subdomain1/_myapp.php 檢索 DB_HOST 值並將其用作 config/database.php 中的值。 所以 config/_myapp.php 只返回特定子域的 _myapp.php 的內容。

在 config/_myapp.php: <?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'),
...

請詢問是否有問題,我花了很長時間才弄明白。

這完全取決於您如何處理多個連接。我曾從事過類似的需求項目。

  • 我們有主從/租戶數據庫連接。 與配置https://gist.github.com/safoorsafdar/c6c623f9ec7b440f563d76995faf7aec#file-database-php
  • 租戶數據庫/遷移/種子動態創建,並在系統中創建新帳戶
  • 可以從根控件軟刪除數據庫。
  • 在用戶登錄時,我們檢查其是否為 master,連接到 master 連接,否則獲取租戶連接,將信息存儲在 session 中以供 web 訪問。 Tenant Setup after login檢查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;
            });
    }
}

*租戶上下文會話*

<?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', '');
    }
}

我希望這對您的場景有所幫助,我已嘗試提及我們必須涵蓋的所有方面,但如果仍有一些令人困惑的問題並需要我的解釋,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM