코드이그나이터4 데이터베이스 다루기 - 5 - 풍부한 모델 사용하기

koeunyeon·2021년 3월 31일
0

풍부한 모델 사용하기

코드이그나이터4는 기본 모델 외에 자동으로 생성,수정,삭제 시간을 넣어주는 기능이 있습니다. 또한 삭제를 컨트롤하는 소프트 딜리트(soft delete)도 있죠. 기본 모델보다 조금 더 풍부한 기능을 사용해 보겠습니다.

본 예제는 https://github.com/koeunyeon/ci4/tree/model-rich-model에 있습니다.

데이터베이스 테이블 생성

마이그레이션 부분을 따라하셨다면 이미 sample_rich 테이블이 생성되어 있을 겁니다. 만약 없거나, 따로 데이터베이스에서 만드시려면 아래의 쿼리를 입력하세요.

-- auto-generated definition
create table sample_rich
(
    sample_rich_id bigint unsigned auto_increment
        primary key,
    name           varchar(40) not null,
    age            int         null,
    created_at     varchar(25) not null,
    updated_at     varchar(25) not null,
    deleted_at     varchar(25) null
)
    charset = utf8;

풍부한 모델 클래스 작성하기

/app/Models 디렉토리 아래에 RichModel.php 파일을 생성하고 아래의 내용으로 대체합니다.
/app/Models/RichModel.php

<?php


namespace App\Models;


use CodeIgniter\Model;

class RichModel  extends Model
{
    protected $table = 'sample_rich';
    protected $allowedFields = ['name','age'];
    protected $primaryKey = "sample_rich_id";

    protected $useSoftDeletes = true; // (1)
    protected $useTimestamps = true; // (2)
}

(1) 소프트 딜리트(soft delete)를 사용할 지 여부를 결정합니다. 기본값은 false입니다. 소프트 딜리트를 사용하면 데이터가 실제로 삭제되는 것이 아니라 데이터베이스에 삭제되었다고 표시만 됩니다.
소프트 딜리트를 사용하려면 $deletedField 멤버 변수가 선언되어 있어야 합니다. 다행히도 CodeIgniter\Model 클래스에는 이미 $deletedField = 'deleted_at' 멤버 변수가 선언되어 있으므로 데이터베이스 열 이름이 deleted_at이라면 직접 선언할 필요는 없습니다.

(2) 자동 시간대를 사용할 지 여부를 나타냅니다. 기본값은 false입니다.
자동 시간대를 사용하려면 $createdField 멤버변수와 $updatedField 멤버변수가 필요합니다. 선언하지 않으면 각각 부모 클래스의 정의에 따라 created_atupdated_at을 사용합니다.

테스트를 위한 컨트롤러 작성하기

테스트를 위한 컨트롤러 클래스를 작성하겠습니다. /app/Controllers 디렉토리 아래에 Rich.php 파일을 생성하고 아래의 내용으로 대체합니다.
/app/Controllers/Rich.php

<?php


namespace App\Controllers;


use App\Models\RichModel;

class Rich extends BaseController
{
    public function create()
    {
        $richModel = new  RichModel();
        // (1)
        $last_insert_id = $richModel->insert([
            'name' => 'rich',
            'age' => 22
        ]);

        $find_result = $richModel->find($last_insert_id);
        return $this->response->setJSON($find_result);
    }

    public function save($id, $name, $age) // (2)
    {
        $richModel = new  RichModel();
        $richData = $richModel->find($id);
        if ($richData === null) { // (3)
            $richData = [];
        }

        // (4)
        $richData['name'] = $name;
        $richData['age'] = $age;
        
        $richModel->save($richData); // (5)
        
        $last_insert_id = $richModel->getInsertID(); // (6)

        if ($last_insert_id == null) { // (7)
            $last_insert_id = $richData['sample_rich_id'];
        }

        $last_insert_data = $richModel->find($last_insert_id); // (8)
        return $this->response->setJSON($last_insert_data);
    }

    public function remove($id)
    {
        $richModel = new  RichModel();
        $richModel->delete($id); // (9)

        $select_result_before = $richModel->find($id); // (10)
        $with_deleted_result_before = $richModel->withDeleted()->find($id);  // (11)

        $richModel->purgeDeleted();  // (12)

        $select_result_after = $richModel->find($id);  // (13)
        $with_deleted_result_after = $richModel->withDeleted()->find($id);  // (14)

        return $this->response->setJSON([  // (15)
            'select_result_before' => $select_result_before,
            'with_deleted_result_before' => $with_deleted_result_before,
            'select_result_after' => $select_result_after,
            'with_deleted_result_after' => $with_deleted_result_after,
        ]);
    }
}

(1) 샘플 데이터를 입력합니다. 데이터는 nameage 두개를 세팅했습니다.

브라우저로 http://localhost:8080/rich/create 에 접속해서 결과를 확인하겠습니다.

{
    "sample_rich_id": "1",
    "name": "rich",
    "age": "22",
    "created_at": "2021-02-02 00:09:56",
    "updated_at": "2021-02-02 00:09:56",
    "deleted_at": null
}

created_at, updated_at, 필드는 데이터를 설정하지 않았음에도 불구하고 데이터가 입력되어 있는 것을 확인할 수 있습니다.

(2) 데이터를 입력하거나 수정하는 엔드포인트 save 를 만듭니다. 각 파라미터는 주 키(id), 이름(name), 나이(age)를 나타냅니다.

(3) 만약 주 키인 $id로 조회했을 때 데이터가 없으면 모델은 null을 반환합니다. 따라서 null이라면 데이터를 빈 배열로 초기화($richData = [];) 합니다.

(4) 저장할 데이터를 파라미터로 설정합니다.

(5) 데이터를 저장합니다. richData['sample_rich_id'] 가 설정되어 있다면 update, 아니면 insert입니다.

(6) 코드이그나이터4에서는 모델의 insert메소드를 썼을 때 반환값이 마지막 입력된 주 키 입니다. 하지만 save의 경우에는 반환값이 성공/실패 여부를 나타내는 boolean 타입이므로 만약 save 메소드가 insert로 작동했다면 반환값만으로는 주 키를 알 수 없게 됩니다.
따라서 마지막 ID를 가져오는 getInsertID() 메소드를 사용합니다. 이 메소드는 update로 동작했을 때는 null을 리턴합니다.

(7) $last_insert_id === nullsave메소드가 update로 동작했다는 뜻입니다. 즉 이미 richData 연관배열에 주 키가 설정되어 있다는 뜻이므로 $last_insert_id 변수의 값을 $richData['sample_rich_id'] 로 교체합니다.

(8) 마지막으로 생성/수정된 데이터를 조회합니다.

브라우저에서 실행해 보겠습니다. http://localhost:8080/rich/save/1/richmodel/33 주소에 접속해 봅니다.
이미 생성된 데이터가 있을 경우 수정이 일어나므로 created_at 데이터와 updated_at데이터가 서로 달라야 합니다.

{
    "sample_rich_id": "1",
    "name": "richmodel",
    "age": "33",
    "created_at": "2021-02-02 00:09:56",
    "updated_at": "2021-02-02 00:20:30",
    "deleted_at": null
}

생성인 경우에는 두 시간이 동일합니다. http://localhost:8080/rich/save/2/richmodel2/44 에 접속해 봅시다.

{
    "sample_rich_id": "4",
    "name": "richmodel2",
    "age": "44",
    "created_at": "2021-02-02 00:23:03",
    "updated_at": "2021-02-02 00:23:03",
    "deleted_at": null
}

created_at 데이터와 updated_at 데이터가 동일함을 알 수 있습니다.

한번 http://localhost:8080/rich/save/2/richmodel2/44 요청이 일어나면 이번엔 수정으로 동작하므로 created_atupdated_at이 달라집니다.

{
    "sample_rich_id": "5",
    "name": "richmodel2",
    "age": "44",
    "created_at": "2021-02-02 00:47:51",
    "updated_at": "2021-02-02 00:47:51",
    "deleted_at": null
}

(9) 삭제 엔드포인트를 살펴보겠습니다. 모델에서 $useSoftDeletes = true로 설정했을 경우 deleted_at 열만 수정됩니다. 다시말해 실제로는 delete 쿼리가 실행되지 않습니다.

(10) 실제로 코드이그나이터4에서 소프트 딜리트를 사용할 경우 조회 쿼리 조건에 deleted_at is not null을 붙여서 실행됩니다. 따라서 $select_result_before 변수는 null을 반환합니다.

(11) deleted_at 열을 무시하고 모든 데이터를 가져와야 할 경우도 있을 겁니다. 그럴 때는 withDeleted() 옵션을 줄 수 있습니다. $with_deleted_result_before 변수에는 실제 데이터베이스에 존재하는 데이터라면 deleted_at 열의 null 여부와 무관하게 모든 데이터를 가져올 수 있게 됩니다.

(12) deleted_at 열이 null이 아닌 모든 데이터를 실제로 삭제합니다. 쿼리로 표현하자면 delete from 테이블 where $deletedField is not null 입니다.
이제 데이터가 모두 삭제되었으므로 withDeleted() 여부에 상관없이 데이터는 조회할 수 없습니다.

(13) **(10)**과 동일한 조회입니다. purgeDeleted() 이후에도 null을 동일하게 리턴합니다. 다만 $select_result_before는 "실제로 데이터가 있으나 조회가 되지 않음 것"인데 반해, $select_result_after는 "실제로 데이터가 없으니까 조회할 수 없음"입니다.

(14) **(11)**과 동일한 조회입니다. 하지만 이번에는 **(11)**과 다르게 null을 반환하는데, 실제로 데이터가 데이터베이스 테이블에서 삭제되었기 때문입니다.

(15) 결과를 확인하기 위해 모든 변수를 JSON 형식으로 리턴합니다.
http://localhost:8080/rich/remove/1 주소에 접속해서 확인해 봅시다.

{
    "select_result_before": null,
    "with_deleted_result_before": {
        "sample_rich_id": "1",
        "name": "richmodel",
        "age": "33",
        "created_at": "2021-02-02 00:09:56",
        "updated_at": "2021-02-02 00:32:03",
        "deleted_at": "2021-02-02 00:32:03"
    },
    "select_result_after": null,
    "with_deleted_result_after": null
}

이제 모든 데이터가 삭제되었으므로 http://localhost:8080/rich/remove/1 주소에 접속해서 확인해 보면 아무것도 조회할 수 없음을 볼 수 있습니다.

{
    "select_result_before": null,
    "with_deleted_result_before": null,
    "select_result_after": null,
    "with_deleted_result_after": null
}
profile
스타트업에 관심이 많은 10 + n년차 웹 개발자. 자바 스프링 (혹은 부트), 파이썬 플라스크, PHP를 주로 다룹니다.

0개의 댓글