릴레이션 메소드
이름처럼 모델과 모델의 관계를 정의합니다.
아래 User 모델은 Post 를 hasMany로 갖는 1:다의 관계를 말합니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
그리고 Post 에서는 BelongsTo로 관계를 정의하고 있습니다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Models\User;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'user_id',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}
릴레이션 메소드는 쿼리빌더로 동작하고 , lazy loading 을 하지 않습니다.
다이나믹 프라퍼티: 동적 프라퍼티 (Dynamic Property)
프라퍼티에 억세스하는 것처럼 릴레이션(메소드)에 억세스가능한것을 다이나믹 프라퍼티라고 합니다.
use App\Models\User;
$user = User::find(1);
foreach ($user->posts as $post) {
}
위 코드처럼 유저의 포스트를 구할때 마치 프라퍼티값을 가져오는 것처럼 메소드명을 억세스할 수 있다.
두가지 방법의 차이는 다음과 같다.
$user->posts //Illuminate\Database\Eloquent\Collection 을 리턴
$user->posts() //릴레이션을 리턴
$user->posts()->get() //Illuminate\Database\Eloquent\Collection 을 리턴
다이나믹 프라퍼티는 Illuminate\Database\Eloquent\Collection 을 리턴하고 lazy loading 을 합니다. 그래서 N+1 의 문제가 발생하는것입니다.
N+1 문제
$posts = Post::all();
foreach($posts as $post){
$post->user;
}
를 하면 디버그바에서 쿼리가 다음과 같이 실행됨을 확인할 수 있습니다.
select * from `posts`
select * from `users` where `users`.`id` = 30 limit 1
select * from `users` where `users`.`id` = 22 limit 1
select * from `users` where `users`.`id` = 41 limit 1
select * from `users` where `users`.`id` = 33 limit 1
select * from `users` where `users`.`id` = 26 limit 1
select * from `users` where `users`.`id` = 38 limit 1
.........
많은 쿼리 발생과 시간지연등의 문제가 생깁니다. 예 N+1 문제입니다.
이걸 해결하는 것이 Eager Loading 입니다.
Eager Loading
$posts = Post::with('user')->get();
foreach($posts as $post){
$post->user;
}
위와 같이 with('user') 로 데이터를 불러오면
select * from `posts`
select * from `users` where `users`.`id` in
(2, 3, 7, 8, 9, 10, 13, 14, 15, 16, 17, 19, 22, 23, 41, 42, 46, 47, 48, 49, 50)
쿼리는 2번 발행됩니다.
기본적으로 with 를 통한 릴레이션을 불러내면 N+1은 해결될겁니다.
Eager Loading 의 방법도 여러가지입니다.
모델에서 가져올때 with() 가져오거나,
Model 내에
protected $with = [ ];
로 지정할 수도 있습니다.
Lazy Loading 방지하기
Laravel 8 부터 추가된
// app/Providers/AppServiceProvider.php
public function boot()
{
Model::preventLazyLoading(! app()->isProduction());
}
를 설정하면 위 예에서 with 없이 데이터 가져오면 lazy loading 을 강제로 못하게 하고 아래와 같은 예외를 냅다 던집니다.
물론 개발 서버에서만 사용되니 이걸 설정해놓는것도 괜찮을듯 싶습니다.
관련링크
https://laravel.com/docs/8.x/eloquent-relationships#relationship-methods-vs-dynamic-properties
'iaa.dev > Laravel8' 카테고리의 다른 글
Laravel 8 - 인스톨할 만한 Package 리스트 (0) | 2022.01.26 |
---|---|
Laravel 8 에서 Repository 패턴 사용하기 (0) | 2022.01.21 |
Laravel 8 - 소셜로그인(Laravel Socialite) - 1 (1) | 2022.01.14 |
Laravel 8 관리자 화면 - CRUD 생성,수정,삭제 기능 (1) | 2022.01.10 |
Laravel 8 Paginator 를 Bootstrap 에서 사용시 (0) | 2022.01.09 |
댓글