[Tip] Laravel with method specific columns

Laravel의 with method는 relation된 다른 테이블의 값을 함께 가져올 때 사용된다.

다만 이번 프로젝트에서 users 테이블의 연결된 logs의 timestamp 값 (created_at, updated_at)을 가져오고 싶었는데 hidden으로 감추기엔 모두 사용되는 값이였고,
그렇다고 모두 가져오기엔 필요없는 데이터와, 유저에게 표시되면 안되는 부분도 있어서 생성일만 가져오는게 제일인 상황에서 아래와 같이 코드를 짰었다.

// ...
public function history()
{
    // ...
    $logs = $user->logs;
    foreach ($logs as $log) {
        $user->log = [
            'created_at' => $log->created_at,
            'updated_at' => $log->updated_at,
        ];
    }
    // ...
    return $user;
}
// ...

리펙토링을 하는중에 가만 생각해보니 Raw로 쿼리를 짯을 땐 select로 원하는것만 가져오면되는데,
이걸 또 with으로 가져오다보니 select를 따로 하려면 callback을 짜야하는데 간단히 select 정도만 하면되는데 콜백으로 가독성이 더 떨어질까 생각하는 와중에 with에서 callback없이 원하는 column만 가져올 수 있는 방법을 찾앗다.

$user->with('logs:id,created_at,updated_at')->get()

with에 relation method명과 원하는 컬럼 명을 위와같은 포맷으로 전달해 주면 해당 결과만 표시되고 그외의 결과는 표시되지 않는다.

callback보다 보기 깔끔하고, 위에 코드보다 깨끗(?) 하다.

다만 주의해야 하는점이 반드시 ‘id‘값을 포함해야 한다. 예를 들어
$user->with('logs:created_at,updated_at')->get() 와 같이 작성하면 작동되지 않는다.
id 값을 기준으로 가져오기때문에 반드시 logs:id,를 넣어주어야한다.

Laravel $model->relation Vs. $model->relation()

TL;DR

$model->relation()은 모델 객체(object)를 반환
$model->relation은 relation의 결과값을 반환

Laravel에선 모델간의 관계(relation)을 편리하게 사용할수 있는 기능을 제공한다.[docs]
외래키(foreign_key)를 이용하여 해당 데이터에 해당되는 값을 가져올 수 있는 편리한 기능이다.

다음과 같은 구조의 DB가 있다고 생각해보자.

User : table
– id : integer (auto_increase, primary_key)
– name : string
– password : string

Post : table
– id : integer (auto_increase, primary_key)
– title : text
– content : text
– user_id : integer (foreign_key on user table)

참고로 DB에 string이란 type은 없다.
이해를 돕기 위해 string으로 표기한것이다.

이때 Post tableuser_id 로 해당 post를 작성한 user의 정보를 가져올 수 있고,
user_idUser Tableid(primary_key)를 검색하여 user가 적성한 post를 가져올 수 있다.

이를 Laravel의 Eloquent의 RelationhasMany, belongsTo등을 이용해
Model끼리의 Relation을 만들 수 있다.
자세한 사항은 Eloquent Relation 문서를 확인하기 바라며,
위와 같이 연결하였다면, 다음과 같이 Post혹은 User를 불러올 수 있다.

<?php
// Get user info of user id 1
$user = App\User::find(1)->first();
// Get posts that user_id is 1 in post table
$user->posts();
$user->posts;
<?php
// Get user info of user id 1
$post = App\Post::find(1)->first();
// Get user that id is 1 in user table
$post->user();
$post->user;

그런데 여기서 궁금증이 생겼다.
Model의 Relation을 통해 데이터를 가져올 수 있는 방식은 총 2가지 인데,
$model->relation()$model->relation 이다.

이 두가지의 방법의 가장 큰 차이는
Result(결과 값) 를 반환 하느냐
Object 를 반환 하느냐의 차이이다.

$model->relation()

$model->relation()은 Object를 반환하는 데 해당 오브젝트는 hasMany, BelongsTo 등 여러가지가 있지만 일단 Eloquent를 상속 받은 object를 반환한다.

실제로 tinker을 통해 get_class() 를 통해 어떠한 값을 반환하는 지 확인해 보면 아래와 같은 값을 출력한다.

>>> get_class(App\User::find(1)->posts())
"Illuminate\Database\Eloquent\Relations\HasMany"
>>> get_class(App\Post::find(1)->user())
"Illuminate\Database\Eloquent\Relations\BelongsTo"

위와 같이 Eloquent의 Relation class를 가지는 object를 반환하며 해당 값은 Eloquent의 함수들인
->select(),->where(), ->find() 등을 사용할 수 있다. [docs]

$model->relation

$model->relation 은 Result를 반환하는데 여기서 말하는 Result는 Model을 말한다.

이것도 tinker를 이용해 결과값을 보면 아래와 같이 Model을 반환하는 것을 볼 수 있다.

>>> get_class(App\User::find(1)->posts)
=> "App\Post"
>>> get_class(App\Todo::find(1)->user)
=> "App\User"

즉, $model->relation은 Relation에 해당되는 결과 값 모델을 가져온다는 뜻이다.

여기서 당연한 사항을 한개 말하자면,
get_class($model->relation)get_class($model->relation()->first()) 은 서로 같은 값을 반환한다.

>>> get_class(App\Post::find(1)->user()->first())
=> "App\User"
>>> get_class(App\Post::find(1)->user)
=> "App\User"

참조
https://stackoverflow.com/questions/28223289/difference-between-method-calls-model-relation-and-model-relation