평범한 이야기들

[TIL] PHP Laravel - Database #4 엘로퀀트(Eloquent) - 1 본문

평범한 개발 이야기/Laravel

[TIL] PHP Laravel - Database #4 엘로퀀트(Eloquent) - 1

songsariya 2021. 2. 11. 18:25
728x90

엘로퀀트 #1

엘로퀀트 (Eloquent) 액티브 레코드 ORM : 테이블에 연결된 클래스를 이용해 CRUD를 쉽게 할 수 있음

여러 데이터베이스 작업을 하나의 인터페이스로 처리할 수 있는 데이터베이스 추상화 레이어

// 간단한 엘로퀀트 예
public function save(Request $request)
{
    // 사용자의 입력으로부터 새로운 연락처 데이터를 생성하고 저장
    $contact = new Contact();
    $contact->first_name = $request->input('first_name');
    $contact->last_name = $request->input('last_name');
    $contact->email = $request->input('email');
    $contact->save(); // 저장

    return redirect('contacts');
}

관례에 의해 테이블명을 알아내고 이를 통해 테이블을 제어한다 (모델이 Contact면 테이블명은 contacts )

모델명은 Camel 표기법, 테이블명은 snake 표기법 (BillingAccount → billing_accounts)

 

엘로퀀트 모델 생성

~ php artisan make:model Contact

# app/Models/Contact.php 파일 생성, 모델 기본구조로 되어있음.

#모델 생성시 해당 모델 마이그레이션도 함께 하기
~ php artsaan make:model Contact --migration

엘로퀀트 모델이 연결되는 테이블 명을 변경할 때

protected $table = '변경할 테이블명';

라라벨은 기본적으로 테이블에 id라는 자동으로 증가되는 정수형 기본 키가 있다고 가정

테이블의 기본 키 이름이 다른 경우 모델 클래스에서 변경

// 기본 키 지정
protected $primaryKey = '필드명';

// 자동증가 되지 않게 막으려면
public $incrementing = false;

라라벨은 모든 테이블에 created_at, updated_at 타임스탬프 칼럼을 가지고 있다고 생각

// created_at, updated_at 칼럼이 없을때
public $timestamps = false; 

// 유닉스 타임을 기반으로 한 초 형태 (time()과 동일한 형태)
protected $dateFormat = "U";

 

엘로퀀트를 사용해 데이터 조회

// 전체 데이터 가져오기
$allContacts = Contact::all();

// 조건 추가
$vipContacts = Contact::where('vip',true)->get();

first(), firstOrFail(), find(), findOrFail() 메서드와 같이 하나의 레코드를 반환하는 경우에는 엘로퀀트 클래스의 인스턴스 반환

get()은 여러 개의 레코드를 반환

많은 양의 레코드를 처리할 때 chunk() 메서드를 이용해서 작업

Contact::chunk(100, function($contacts) {
    foreach($contacts as $contact) {
        // 작업 진행
    }
}

 

엘로퀀트를 사용해 데이터 추가 / 수정

데이터 추가

$contact = new Contact;
$contact->name = 'HongGilDong';
$contact->email = 'hong@hong.com';
$contact->save();

// 또는

$contact = new Contact([
    'name' => 'HongGilDong',
    'email' => 'hong@hong.com',
]);
$contact->save();

// 또는
$contact = Contact::create([
    'name' => 'HongGilDong',
    'email' => 'hong@hong.com',
]);

위 2가지 방법은 save() 메서드 호출 전까지 DB 저장 안 함, 마지막 방법은 바로 DB에 저장

데이터 수정

$contact = Contact::find(1);
$contact->email = 'hong2@hong.com';
$contact->save();

엘로퀀트 인스턴스를 조회하고 updqte 메서드에 배열을 전달해 수정하는 방법

Contact::where('created_at', '<', now()->subYear())->update(['longevity' => 'ancient']);

// 또는

$contact = Contact::find(1);
$contact->update(['longevity' => 'ancient']);

대량 할당

fillable 속성 값은 메서드에 배열을 전달할 때, 변경하면 안 되는 속성 값이 실수로 변경되는 일을 막기 위해 존재

fillable에 정의된 속성 값만 변경됨

class Contact
{
    protected $fillable = ['name', 'email']; // 정의된 내용만 수정 가능

    // 또는

    protected $guarded = ['id','created_at','owner_id']; // 정의된 내용은 수정 불가
}

firstOrCreate() , firstOrNew() 는 값을 가지고 있는 데이터를 조회, 없으면 새로 만드는 메서드

firstOrCreate() 는 DB에 저장 후 인스턴스 반환 firstOrNew() 는 인스턴스만 반환

 

엘로퀀트를 사용한 데이터 삭제

기본적인 삭제

$contact = Contact::find(4);
$contact->delete();

Contact::destroy(1);
// 또는 
Contact::destroy([1,5,6]);

// 체이닝으로도 가능
Contact::where('updateed_at','<',now()->subYear())->delete();



소프트 삭제

소프트 삭제는 실제 삭제되지 않고 플래그를 이용해 삭제된 것처럼 보이는 기능

소프트 삭제 활성화 방법

  1. deleted_at 칼럼 추가
  2. 엘로퀀트 모델에서 SoftDeletes 트레이트를 추가
  3. 모델의 $dates 속성에 deleted_at 칼럼을 추가
// 1. 마이그레이션에서 소프트 삭제 칼럼 추가
Schma::table('contact',function(Blueprint $table){
    $table->softDeletes();
});

// 2. 트레이트 추가
// 3. $dates 속성에 칼럼 추가.
class Contact extends Model
{
    use SoftDeletes; // 트레이트 사용
    protected $dates = ['deleted_at']; // 이 칼럼이 date 포맷이라고 알려준다.
}

소프트 삭제된 레코드 조회

// 삭제된 레코드도 포함되서 조회
$allHistoryContacts = Contact::withTrashed()->get(); 

// `trashed()` 메서드를 이용해 삭제 여부 확인
if($contact->trashed()){
    // 로직 수행
}

//삭제된 레코드만 조회
$deletedContacts = Contact::onlyTrashed()->get();

소프트 삭제된 레코드 복원

$contact->restore();
//또는
Contact::onlyTrashed()->where('vip', true)->restore();

소프트 삭제된 레코드 완전 삭제

$contact->forceDelete();
// 또는
Contact::onlyTrashed()->forceDelete();

 

스코프-범위 제한 기능

로컬 스코프

// 두조건을 한번에 처리하는 방법 "로컬스코프"
$activeVips = Contact::where('vip',true)->where('trial',false)->get();

// app/Models/Contact.php
...
public function scopeActiveVips($query)
{
    return $query->where('vip',true)->where('trial',false);
}
...

// 그 후 호출
$activeVips = Contact::activeVips()->get();

인자를 요하는 로컬 스코프

public function scopeStatus($query, $status)
{
    return $query->where('status',$status);
}

// 호출
$friends = Contact::status('friend')->get();

라라벨 5.8 이상에서는 스코프 2개 사이에 orWhere() 메서드를 체이닝 가능함.

 

글로벌 스코프

글로벌 스코프를 정의하는 데에는 클로저를 사용하는 방법, 별도의 클래스를 사용하는 방법 2가지 존재

두 가지 모두 정의된 스코프를 적용하고자 하는 엘로퀀트 모델의 boot()메서드에서 등록

클로저를 사용해 글로벌 스코프 추가 방법


...
class Contact extends Model
{
    protected statisc function boot()
    {
        parent::boot();

        static::addGlobalScope('active', function(Builder $builder) {
            $builder->where('active', true);
        });
    }
}

// active 라는 글로벌 스코프 추가
// 이 모델을 사용해 쿼리를 작성하면 모든 쿼리가 active가 true인 데이터에만 적용되도록 범위 제한

클래스를 추가해 글로벌 스코프 추가 방법

<?php

namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class ActiveScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('active',true);
    }
}

// 위와 같이 클래스를 생성 후 사용하고자 하는 엘로퀀트 모델의 boot() 메서드에서 스태틱 addGlobalScope() 메서드 인자로 전달.
...
protected statisc function boot()
    {
        parent::boot();

        static::addGlobalScope(new ActiveScope);
    }
...

글로벌 스코프 적용하지 않기

withoutGlobalScope(), withoutGlobalScopes() 메서드 사용

$allContacts = Contact::withoutGlobalScope('active')->get();

// 별도의 스코프 클래스를 사용해 등록한 경우
Contact::withoutGlobalScope(ActiveScope::class)->get();
Contact::withoutGlobalScopes([ActiveScope::class, VipScope::class])->get();

// 모든 글로벌 스코프를 적용하지 않는 방법
Contact::withoutGlobalScopes()->get();
728x90
Comments