[Model] 같은 형태에 대한 여러개의 테이블이 존재할 때, 1개의 Model로 사용하기 version 2.0

이전에 하나의 DB 스키마 아래에 테이블 앞에 글자만 다르고, 모든 형태가 일치(칼럼, 키, 색인 등 모든 게 일치)하는 경우에 Laravel Model에서 1개의 Model로 여러 테이블에 동적으로 바인딩해서 사용하는 방법을 포스팅 한 적이 있다. 다만 아쉬웠던 게 Laravel의 핵심 기술인 Eloquent ORM의 relastionships를 사용할 수 없는 단점이 존재하였고, 이 단점을 어떻게 해결할 수 있을지 고민하여 그 해답을 찾았다

 

[상황]

[동일한 DB 스키마]
- 상품 관련 테이블 : A_Products , B_Products, C_Products.....
- 주문 관련 테이블 : A_Orders, B_Orders, C_Orders.....

[이전 글]

 

[Model] 같은 형태에 대한 여러개의 테이블이 존재할 때, 1개의 Model로 사용하기 version 1.0

최근 라라벨을 사용하여 API를 만드는 작업을 진행하던 중, 같은 Database 내부에서 사용되는 table이 아래와 같은 경우가 생겼습니다. 상품 관련 테이블 : A_Products , B_Products, C_Products 주문 관련 테이

min-nine.tistory.com


1. Eloquent Orm Relations Function을 커스텀하여  BaseModel 생성

<?php

Class BaseModel extends Model
{
    /**
     * @Description \Illuminate\Database\Eloquent\Relations\BelongsToMany
     * 	를 오버라이딩(PHP는 오버로딩,오버라이딩 개념이 없음) 하기위해 명명을 약간 바꾸고
     *  내부의 내용을 수정하여 사용.
     */
    public function belongsToManyMg($related,$tableFirstName = null, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
                                  $parentKey = null, $relatedKey = null, $relation = null)
    {
        // If no relationship name was passed, we will pull backtraces to get the
        // name of the calling function. We will use that function name as the
        // title of this relation since that is a great convention to apply.
        if (is_null($relation)) {
            $relation = $this->guessBelongsToManyRelation();
        }

        // First, we'll need to determine the foreign key and "other key" for the
        // relationship. Once we have determined the keys we'll make the query
        // instances as well as the relationship instances we need for this.
        $instance = $this->newRelatedInstance($related);
        $instance = $tableFirstName;

        $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();

        $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();

        // If no table name was provided, we can guess it by concatenating the two
        // models using underscores in alphabetical order. The two model names
        // are transformed to snake case from their default CamelCase also.
        if (is_null($table)) {
            $table = $this->joiningTable($related, $instance);
        }

        return $this->newBelongsToMany(
            $instance->newQuery(), $this, $table, $foreignPivotKey,
            $relatedPivotKey, $parentKey ?: $this->getKeyName(),
            $relatedKey ?: $instance->getKeyName(), $relation
        );
    }
}

 

2. 위에서 만든 BaseModel을 상속받은 Product Model을 아래와 같이 생성합니다.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use App\Models\BaseModel;

class Product extends BaseModel
{
    use HasFactory;
    
    protected $connection = "yourDB";
    const TABLE = "_Products";
    
    public function setTableFirstName($tableName)
    {
        $this->table = $tableName.self::TABLE;
    }
    
    public function getTableFirstName()
    {
        return str_replace(self::TABLE,'',$this->getTable());
    }
    
    public function orders()
    {
    	return $this->belongsToManyMg(Order::class,$this->getTableFirstName()."_Orders");
    }
}

 

3. 위에서 만든 BaseModel을 상속받은 Order Model을 아래와 같이 생성합니다.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use App\Models\BaseModel;

class Order extends BaseModel
{
    use HasFactory;

    protected $connection = "yourDB";
    const TABLE = "_orders";
    
    public function setTableFristName($tableName)
    {
        $this->table = $tableName.self::TABLE;
    }

    public function getTableFirstName()
    {
        return str_replace(self::TABLE,'',$this->getTable());
    }

    public function products()
    {
    	return $this->belongsToManyMg(Product::class,$this->getTableFirstName()."_Prders");
    }
}

 

4. Controller에서 동적으로 테이블을 바꾸며 사용하는 법

/**
*  your controller.php
*/

// Product 모델 정의
$productModel = new Product;
$productModel->setTableName("A"); // A_Products Table로 A_Orders Table 연동
dd($productModel->find(1)->orders()); A_Products의 인덱스 1번에 연동되는 A_Orders 테이블 데이터

$productModel->setTableName("B"); // B_Products Table 사용하고 싶으면 변경
dd($productModel->find(1)->orders()); B_Products의 인덱스 1번에 연동되는 B_Orders 테이블 데이터

$productModel->setTableName("C"); // C_Products Table 사용하고 싶으면 변경
dd($productModel->find(1)->orders()); C_Products의 인덱스 1번에 연동되는 C_Orders 테이블 데이터

 

5. 각자 사용하고 싶은 Eloquent ORM Relations Function을 BaseModel에 추가

각자 원하는 relations function을 위의 방법을 참고하여 오버 라이딩 비슷하게 명명을 바꾸고 내용을 바꾸어주어 사용하면 동적으로 모델을 바인딩하면서 동시에 Eloquent ORM Relationship을 사용할 수 있다. 물롱 위와 동일하게 하면 안되고 foreignKey 및 localKey 등을 자신의 table에 맞게 변경하여 사용하여야 한다.