[컴] laravel ORM 을 이용할 때 주의할 점

 

ORM eloquent /

laravel ORM 을 이용할 때 주의할 점

원하는대로 query 를 만들어주지 않는다.

아래와 같은 query 를 원했다.

SELECT * 
FROM orders AS t1
JOIN user AS t2
  ON t1.user_id = t2.id
JOIN order_products AS t3
  ON t1.id = t3.order_id
WHERE t1.group_id IN (10)
ORDER BY t1.id

이것을 ORM 식으로 표현해보려고 아래처럼 만들었다. 여기서는 laravel(php) 을 사용했다. 그래서 orm 의 모양은 간단하게 보인다. 그런데 실질적으로 이 ORM 에서 만들어지는 query 를 살펴보면 그렇지 못하다.

// for debug
DB::enableQueryLog();

Order::whereIn('group_id', $param['ids'])
    ->with('user', 'groupOrderProducts.materialBarcodes') // Nested Eager Loading
    ->orderBy('id')
    ->get();

// 출력
dd(DB::getQueryLog()); 

물론 이것은 잘못 만든 code 라 생각한다. with 를 사용하는 것은 확실히 SELECT 를 여러번 할 것을 표현하는 것에 가깝기 때문이다. JOIN 을 할 생각이었다면, 그냥 ->join 을 걸었어야 한다.

여하튼 위의 query 를 돌리면 아래처럼 4번의 query 를 수행한다.

array:4 [
  0 => array:3 [
    "query" => "select * from "orders" where "group_id" in (?) and "orders"."deleted_at" is null order by "id" asc"
    "bindings" => array:1 [
      0 => 1
    ]
    "time" => 0.33
  ]
  1 => array:3 [
    "query" => "select * from "users" where "users"."id" in (1)"
    "bindings" => []
    "time" => 0.28
  ]
  2 => array:3 [
    "query" => "select "order_products".*, "orders"."group_id" as "laravel_through_key" from "order_products" inner join "orders" on "orders"."id" = "order_products"."purchase_id" where "orders"."deleted_at" is null and "orders"."status" = ? and "order_products"."claim_id" is null and "order_products"."final_qty" > ? and "orders"."group_id" in (1, 2)"
    "bindings" => array:2 [
      0 => 1
      1 => 0
    ]
    "time" => 0.34
  ]
  3 => array:3 [
    "query" => "select "barcodes" from "materials" where 0 = 1"
    "bindings" => []
    "time" => 0.17
  ]
]

ORM classes

////////////////////////////////////////
class Order extends Model
{
    ...

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function groupOrderProducts()
    {

        return $this->hasManyThrough(
            OrderProduct::class,
            Order::class,
            'group_id',
            'Order_id',
            'id',   // $this->id
            'id'    // Order:class -> id
        )
        ->where('orders.status', 1)
        ->whereNull('order_products.claim_id')
        ->where('order_products.final_qty', '>', 0);
    }
}

////////////////////////////////////////
class User extends Model
{
    ...
    public function orders()
    {
        return $this->hasMany(Order::class);
    }
}

////////////////////////////////////////
class OrderProduct extends Model
{
    ...

    public function materialBarcodes() {
        return $this->belongsTo(Material::class)->select('barcodes');
    }


}

결론, 이럴때는 안쓰는 것이 낫다.

개인적으로 query 와 가까운 ORM 을 선호한다. 이 관점에서 이야기를 하자면 이번처럼 복잡한 query 를 사용하는 경우에 orm 은 단순한 query 용도로 좋을듯 하다. 여럿이서 code 를 공유하는 경우에 ORM 으로 중복된 query 를 coding 하는 것을 잘 조절할 수는 있겠지만, 반면에 DB 의 부하는 심해질 수 있다. 그리고 사실 query 내부를 볼 수 없으니, 동작의 이해도도 떨어진다.

그래서 ORM 의 relationship 기능의 사용은 주의가 필요하다. 필자는 개인적으로 orm 의 복잡한 기능들을 좋아하지 않지만, 좀 더 간략한 query 에서는 좋은 이해를 줄 수 있고, code 의 재사용성도 높여줄 것이라고는 본다.

물론 그 이전에 ORM 의 특징중 하나인 DB Software를 옮길때의 이점도 있겠지만, 사실 이부분은 대체로 query 가 표준에 맞춰져 있는 상황에서 생각보다 큰 이점이 되지 않을 것으로 본다.

차라리 이 부분에서는 DBBuilder 정도가 어울린다고 생각한다.

댓글 없음:

댓글 쓰기