일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 우분투
- 라라벨
- php
- 옵지
- ubuntu
- 제주도
- 업비트
- 옵G
- Selenium
- 프레임워크
- 코드이그나이터
- 라즈베리파이
- Laravel
- C
- TiL
- 20.04
- 맛집
- Ubuntu 20.04
- FMS
- upbit
- MySQL
- 셀레니움
- 라즈비안
- codeigniter
- 옵티머스 g
- 옵티머스g
- 맥
- 회고
- 우분투 20.04
- Raspberry Pi
- Today
- Total
평범한 이야기들
[TIL] PHP Laravel - Database #5 엘로퀀트(Eloquent) - 2 본문
접근자, 변경자, 속성 값 형 변환을 사용한 커스텀 필드 사용
접근자 ( Getter )
// 모델에서 접근자 정의
class Contact extends Model
{
// 기본
public function getNameAttribute($value)
{
return $value ?: '(no name provided)';
}
// 테이블에 존재하지 않은 값에 접근하는 속성값을 접근자를 이용해 정의
public function getFullNameAttribute()
{
return $this->first_name . ' ' . $this->last_name;
}
}
// 정의한 접근자 사용
$name = $contact->name;
$name = $contact->full_name;
변경자 ( Setter )
// 모델에서 변경자 정의
class Order extends Model
{
// 기본
public function setAmountAttribute($value)
{
$this->attributes['amount'] = $value > 0 ? $value : 0;
}
//테이블에 존재하지 않은 값을
public function setWorkgroupNameAttribute($workgroupName)
{
$this->attributes['email'] = "{$workgroupName}@company.com";
}
}
// 정의한 접근자 사용
$order->amount = 15;
$order->workgroup_name = 'SR_';
속성 값 형 변환
class Contact
{
protected $casts = [
'vip' => 'boolean',
'children_names' => 'array',
'birthday' => 'date',
];
}
커스텀 형 변환 (라라벨7 이상)
CastsAttributes 인터페이스를 구현한 형 변환을 정의하는 클래스 작성
<?php
namespace App\Models\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class Json implements CastAttributes
{
// 주어진 값 형변환
public function get($model, $key, $value, $attributes)
{
return json_decode($value, $key);
}
// 주어진 값을 저장하기 위해 준비
public function set($model, $key, $value, $attributes)
{
return json_encode($value, $key);
}
}
// 클래스를 생성.
class Contact
{
protected $casts = [
'options' => Json:class
];
}
날짜 변경자
// 해당 칼럼이 타임스탬프 칼럼으로 작동
class Contact
{
protected $dates= [
'met_at',
];
}
// $dates에는 기본적으로 'created_at','updated_at' 포함되어있다.
쿼리 결과 형 변환
$users = User::select(['id','name','vip'])->withCasts([
'vip' => 'bool'
])->get();
// vip 속성값은 불리언 값이 된다.
엘로퀀트 연관관계
1:1
, 1:N
, N:N
관계가 존재
일대일 연관관계 ( hasOne()
, belongsTo()
)
// 하나의 Contact는 하나의 PhoneNumber 모델을 가지고 있다. -> hasOne
class Contact extends Model
{
public function phoneNumber(){
return $this->**hasOne**(PhoneNumber::class);
// 관례에 따르면 contact_id 라는 키로 연결을 시킨다고 생각한다.
// 칼럼명이 다르다면 2번째 인자를 사용
return $this->hasOne(PhoneNumber::class, 'owner_id');
}
}
// 사용방법
$contact = Contact::first();
$contactPhone = $contact->phoneNumber();
일대일 역방향 연관관계
// PhoneNumber 모델에서 Contact 모델에 접근하기 위해
class PhoneNumber extends Model
{
public function contact(){
return $this->belongTo(Contact::class);
}
}
// 사용방법
$contact = $phoneNumber->contact;
일대다 연관관계 ( hasMany()
,belongsTo()
)
// User가 여러 Contact를 가지는 관계
class User extends Model
{
public function contacts(){
return $this->hasMany(Contact::class);
}
}
// 사용방법
$user = User::first();
$usersContacts = $user->contacts; // 컬렉션 반환
// 컬렉션으로 반환하기 때문에 컬렉션 메서드 가능
$donors = $user->contacts->filter(function($contact){
return $contact->status == 'donor';
});
$lifetimeValue = $contact->orders->reduce(function($carry, $order){
return $carry + $order->amount;
}, 0);
일대다 역방형 연관관계
class Contact extends Model
{
public function user(){
return $this->belongsTo(User::class);
}
}
// 사용방법
$userName = $contact->user->name;
// 연관관계 쿼리 빌더로 사용
$donors = $user->contacts()->where('status', 'donor')->get();
// 연관관계 존재하는 레코드만 조회
$postsWithComments = Post::has('comments')->get();
$postsWithManyComments = Post::has('comments', '>=', 5)->get(); // 기준조건 추가
$usersWithPhoneBooks = User::has('contacts.phoneNumbers')->get(); // 중첩된 연관관계를 조건으로 사용
//전화번호 문자열에 867-5309'가 포함된 전화번호를 가진 모든 연락처 조회
$jennyIGotYourNumber = Contact::whereHas('phoneNumbers',function($query){
$query->where('number', 'like', '%867-5309%');
});
연결을 통한 다수 연관관계
ex) 하나의 사용자(User) 모델 이 다수의 연락처(Contact)를 가지고 있고 Contact는 여러 개의 PhoneNumber를 가지고 있다고 가정
hasManyThrought()
hasOneThrought()
메서드를 이용
class User extends Model
{
public function phoneNumbers()
{
return $this->hasManyThrought(PhoneNumber::class, Contact::class);
}
// 사용방법
$user->phone_numbers // 연관관계로 접근 가능
다대다 연관관계 (belongsToMany()
)
class User extends Model
{
public function contacts()
{
return $this->belongsToMany(Contact::class);
}
}
class Contact extends Model
{
public function contacts()
{
return $this->belongsToMany(User::class);
}
}
다대다 관계에서는 피벗 테이블이 필요함
라라벨 관례에 의해 알파벳 순서대로 정렬해 위와 같은 경우 contaact_user
테이블이 필요함 (contact_id, user_id 칼럼 필요로 함)
피벗 테이블명을 변경하려면 belongsToMany()
메서드의 두 번째 인자에 테이블명을 넘겨줘야 함
// 사용법.
$user = User::fisrt();
$user->contacts->each(function ($contact) {
// 작업 실행
});
$contact = Contact::first();
$contact->users->each(function ($user) {
// 코드 실행
});
피벗 테이블에서 데이터 조회
public function contacts()
{
return $this->belongsToMany(Contact::class)
->withTimestamps(); // created_at, updated_at 칼럼 여부를 알려줌
->withPivot('status', 'preferred_greeting'); // withPivot() 메서드로 특정 필드를 정의
}
// 사용예
$user = User::first();
$user->contacts->each(function($contact){
echo sprintf('연락처가 이 사용자와 연결된 시각 : %s', $contact->pivot->created_at);
});
// pivot 속성을 가진다.
// as() 메서드를 이용하면 pivot 속성 이름을 변경할 수 있다.
return $this->belongsToMany(Contact::class)
->withTimestamps(); // created_at, updated_at 칼럼 여부를 알려줌
->as('membership');
User::first()->groups->each(function($contact){
echo sprintf('연락처가 이 사용자와 연결된 시각 : %s', $contact->membership->created_at);
});
다형성 연관관계
사용자(User)가 연락처(Contact)와 이벤트(Event)를 즐겨찾기 (Star) 할 수 있는 시스템으로 예를 듬
stars 에는 id 칼럼과 starrabled_id, starrable_type 칼럼이 추가되어야 함
class Star extends Model
{
public function starrable()
{
return $this->morphTo();
}
}
class Contact extends Model
{
public function stars()
{
return $this->morphMany(Star::class, 'starrable');
}
}
class Event extends Model
{
public function stars()
{
return $this->morphMany(Star::class, 'starrable');
}
}
// Star 모델 데이터 생성
$contact = Contact::first();
$contact->stars()->create();
// 연락처에서 즐겨찾기를 모두 찾기
$contact = Contact::first();
$contact->stars->each(function($star){
// 필요한 코드
});
특정 연락처를 즐겨찾기 한 사람을 조회하는 경우
- stars 테이블에 user_id 칼럼 추가
- User 모델과 Star 모델을 일대 다 연관관계로 연결
- stars 테이블은 User, Contact, Event 사이를 나타내는 피벗 테이블로 됨
class Star extends Model
{
public function starrable()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class User extends Model
{
public function stars()
{
return $this->hasMany(Star::class);
}
}
$user = User::first();
$event = Event::first();
$event->start()->create(['user_id' => $user->id]);
다대다 다형성 연관관계
Contact와 Event에 각각 태그를 달 수 있는 상황
- contacts 테이블, events 테이블, tags 테이블 필요
- tag_id, taggable_id, taggalbe_type 칼럼을 가지고 있는 taggable 테이블 필요함.
// 클래스 정의
class Contact extends Model
{
public function tags()
{
return $this->morphToMany(tag::class, 'taggable');
}
}
class Event extends Model
{
public function tags()
{
return $this->morphToMany(tag::class, 'taggable');
}
}
class Tag extends Model
{
public function contacts()
{
return $this->morphedByMany(Contact::class, 'taggable');
}
public function events()
{
return $this->morphedByMany(Contact::class, 'taggable');
}
}
//태그 생성 후 연락처 연결
$tag = Tag::firstOrCreate('name'=>'likes-cheese']);
$contact = Contact::first();
$contact->tags()->attach($tag->id);
// 연락처에서 태그 목록 조회
$contact = Contact::fisrt();
$contact->tags->each(function($tag){
// 작업
});
// 태그에서 연락처 목록 조회
$tag = Tag::first();
$tag->contacts->each(function ($contact){
// 작업
});
하위 모델에서 상위 모델의 타임스탬프 값 갱신
belongsTo
belongsToMany
연관관계로 연결된 경우 모델이 변경될 대마다 연관된 모델도 변경됐다고 작업을 해줄 수 있음
class PhoneNumber extends Model
{
protected $touches = ['contact']; // 속성 추가
public function contacts() {
return $this->belongsTo(Contact::class);
}
}
lazy로딩과 eager 로딩
엘로퀀트는 연관관계 모델을 지연로딩(lazy loading)
기법을 사용해 가져옴
→ 모델의 인스턴스를 가져왔을 때 연관관계에 있는 모델을 함께 불러오지 않음
→ 모델에서 연관관계 모델에 접근할 때 로딩이 이루어짐
// $contact 에서 phone_numbers를 통해 계속 쿼리를 호출하게 된다.
$contacts = Contact::all();
foreach($contacts as $contact) {
foreach($contact->phone_numbers as $phone_number) {
echo $phone_number->number;
}
}
이런 단점을 해결하기 위해 eager 로딩 (연관관계 모델을 함께 사용하고 있다는 걸 미리 알면 적용)
$contacts = Contact::with('phoneNumbers')->get();
모델을 조회할 때 with() 메서드를 사용하면 연관된 모델 데이터를 함께 가져옴
eager 로딩 제약 추가
$contacts = Contact::with(['phoneNumbers' => function($query){
$query->where('milable',true); // 조건 추가
}]);
지연 eager 로딩
$contacts = Contact::all();
if($showPhoneNumbers) {
$contacts->load('phoneNumber');
}
// 연관관계를 조회하지 않았을 때에만 eager 로딩을 해오기를 원한다며 loadMissing() 메서드 사용
$contacts = Contact::all();
if($showPhoneNumbers) {
$contacts->loadMissing('phoneNumber');
}
엘로퀀트 이벤트
엘로퀀트 모델은 특정 작업이 발생할 때마다 애플리케이션 내부의 이벤트를 발생
자세한 이벤튼 내용은 ( 라라벨문서 )