Dynamic_table_laravel_5.8.md

Translated by ChatGTP

動態表格(Laravel 5.8)

假設我們有以下這些表格:

  • org_1_members
  • org_2_members
  • org_3_members
  • org_1_purchases
  • org_2_purchases
  • org_3_purchases

而這些表格的結構都是相同的:org_1_membersorg_2_membersorg_3_membersorg_1_purchasesorg_2_purchasesorg_3_purchases

問題

如果我們要讓模型的代碼可以重複使用,就必須在獲取資料時動態更改表格名稱,但是 Laravel 5.8 不支援動態表格。

或許我們可以嘗試擴展擁有重複邏輯的類別,但是當有新組織時,我們就必須創建新的模型,這樣不是一個好辦法,尤其是組織也是動態的。

簡單的解決方案

Illuminate\Database\Eloquent\Model 中有一個 getTable() 函數,可以在使用 Eloquent 建構器時獲取表格名稱。

我們可以重寫這個函數來獲取當前使用相關組織表格。

public function getTable()
{
    return 'org_' . config('database.connections.mysql.orgId') . '_members';
}
  • 動態更改 orgId 以使用登入信息
  • 在您的模型中覆蓋該函數

這個解決方案也適用於 Eloquent 關係,需要在相關模型中編寫類似的函數。

不完美之處

如果我想要獲取具有「org_2_purchases」關聯的「org_1_members」,那麼這個使用情況可能永遠不會發生,但是如果我的表格形式如下,會怎麼樣呢?

  • org_1_members
  • org_1_members_purchase_type_a
  • org_1_members_purchase_type_a

其中的類型也是動態的,也許這種情況更有意義,因此可以想像出有 2 個問題。

  • 必須在使用之前每次更改配置。
    • 忘記重置配置時也沒有警告,會獲取錯誤的資料。
  • 模型同時只能連接到一個表格。

其他解決方案

改為從靜態表格改為動態表格,但仍然存在上面提到的問題。

最終解決方案

實現自動表格功能

遷移
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTestTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        foreach ([1 ,2, 3] as $orgId) {
            $memberTable    = 'org_' . $orgId . '_members';
            $purchasesTable = 'org_' . $orgId . '_purchases';
            Schema::create($memberTable, function (Blueprint $table) {
                $table->increments('id');
                $table->string('name');
                $table->timestamp('created_at')->useCurrent();
                $table->timestamp('updated_at')->useCurrent();
            });

            Schema::create($purchasesTable, function (Blueprint $table) {
                $table->increments('id');
                $table->unsignedInteger('member_id');
                $table->string('product');
                $table->timestamp('created_at')->useCurrent();
                $table->timestamp('updated_at')->useCurrent();
            });

            \Illuminate\Support\Facades\DB::table($memberTable)->insert([
                'name' => 'org_' . $orgId . '_user_1',
            ]);

            \Illuminate\Support\Facades\DB::table($purchasesTable)->insert([
                'member_id' => 1,
                'product'   => 'org_' . $orgId . '_product_1',
            ]);

        }
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('org_1_members');
        Schema::dropIfExists('org_2_members');
        Schema::dropIfExists('org_3_members');
        Schema::dropIfExists('org_1_purchases');
        Schema::dropIfExists('org_2_purchases');
        Schema::dropIfExists('org_3_purchases');
    }
}
模型

MemberModel

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * App\Model\memberLogRecordModel
 *
 * @mixin \Eloquent
 */
class MemberModel extends Model
{
    protected $table = '';

    protected $fillable = [
        'name'
    ];

    public function getTable()
    {
        return 'org_' . config('database.connections.mysql.orgId') . '_members';
    }

    public function lastPurchase()
    {
        return $this->hasOne('App\Models\PurchaseModel', 'member_id', 'id')->orderByDesc('id');
    }
}

PurchaseModel

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * App\Model\memberLogRecordModel
 *
 * @mixin \Eloquent
 */
class PurchaseModel extends Model
{
    protected $table = '';

    protected $fillable = [
        'member_id',
        'product',
    ];

    public function getTable()
    {
        return 'org_' . config('database.connections.mysql.orgId') . '_purchases';
    }
}
執行代碼
    $orgId = 2;
//  在 middleware 中更改配置
    Config::set('database.connections.mysql.orgId', $orgId);

    $member = MemberModel::with(['lastPurchase'])->first();

    dd([
        'memberName: ' => $member->name,
        'lastProduct: ' => $member->lastPurchase->product,
    ]);

設置配置應在 middleware 中更改,模型應像往常一樣使用。

結果

^ array:2 [
  "memberName: " => "org_2_user_1"
  "lastProduct: " => "org_2_product_1"
]

使代碼可重複使用

在 Trait 中刪除 getTable()