MVC패턴 중 M에 해당하며 데이터베이스와의 상호작용을 통해 데이터의 관리를 담당하는 Model에 대해서 알아보고자 한다.
Model은 주로 Controller에게 받은 요청에 따라 데이터베이스에 접근, 데이터를 조작(가공)하는 역할을 수행한다.
Laravel의 Model에서 데이터베이스와 상호작용을 하기 위한 방법으로는 크게 두가지, Query Builder와 Eloquent ORM로 나뉘어진다. 간단히 설명하자면 Query Builder는 직접 SQL을 작성하는 방식이며, Eloquent ORM은 각 테이블을 모델 클래스와 매핑하여 상호작용을 하는 방식이다.
이러한 개념을 토대로 데이터베이스를 사용, 세팅하기 위한 전처리 방법과 Query Builder, Eloquent ORM에 대해서 알아보자.
config/database.php
// 기본 DB 연결 지정
'default' => env('DB_CONNECTION', 'mysql'),
// 각각의 DB 지정
'connections' => [
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
]) : [],
],
],
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
migration은 데이터베이스의 테이블을 관리하는 기능으로, 데이터베이스의 테이블의 생성, 수정, 삭제 등의 작업을 PHP 코드로 정의한다. 이를 통해 팀원들간의 데이터베이스 구조 일관성 유지, 더 나은 데이터베이스 유지보수 및 배포 등의 장점이 있다.
Laravel에서의 migration은 아래의 Artisan 명령어를 통해서 추가할 수 있으며, database\migrations\ 하위에서 관리된다.
php artisan make:migration create_users_table
생성된 migration 파일
class CreateUsersTable extends Migration
{
// 테이블 생성, 수정처리
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->rememberToken(); // 사용자 인증 관련 토큰 생성
$table->timestamps();
});
}
// 테이블 삭제
public function down()
{
Schema::dropIfExists('users');
}
}
migration은 아래의 artisan을 통해 사용할 수 있다.
php artisan migrate // migration 실행
php artisan migrate:rollback // migration 되돌리기
php artisan migrate:status // 지금까지 실행된 migration 정보 출력
php artisan migrate:refresh // 모든 migration 되돌리고 재실행
// 지정한 migration 실행
php artisan migrate:refresh --path=/database/migrations/file.php
seeding은 데이터베이스의 테이블에 초기 데이터나 테스트 데이터를 삽입하기 위한 기능이다. migration과 마찬가지로 팀원들간의 초기 데이터의 일관성 유지, 초기 데이터 관리가 쉬운 점 등의 장점이 있다.
Laravel에서의 seeder은 아래의 Artisan 명령어를 통해서 추가할 수 있으며, database\seeders\ 하위에서 관리된다.
php artisan make:seeder UsersTableSeeder
// ※ 전체 migration + seed 처리를 전부 실행하는 명령어
php artisan migrate:fresh --seed
생성된 seeder파일
<?php
>
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class UsersTableSeeder extends Seeder
{
// 데이터베이스에 삽입할 레코드의 정보 작성
public function run()
{
DB::table('users')->insert([
'name' => 'Kim',
'email' => 'kim@example.com',
'password' => bcrypt('password'),
]);
}
}
정의된 seeder메소드는 아래의 artisan을 통해서 실행할 수 있다.
php artisan db:seed --class=UsersTableSeeder
Query Builder는 PHP 메소드 체인을 활용한 데이터베이스와의 상호작용을 하기 위한 기능으로, 기존의 SQL작성방식보다 가독성과 유지보수성이 뛰어나다.
DB::table('users')->get(); // 테이블 모든 레코드
DB::table('users')->where('name', 'John'); // where조건 지정(=)
->where('name', 'like', 'T%'); // where조건 지정(like)
DB::table('users')->find(3); // 키를 통한 단일 행 검색
DB::table('orders')->exists() // 레코드 존재 유무 체크
그 밖에도 Laravel collections의 기능을 제공해주기에, Query Builder와 더불어 collections의 자료도 참고해가며 작성하는 것이 좋다.
Query Builder : https://laravel.kr/docs/8.x/queries
Collection : https://laravel.kr/docs/8.x/collections
Eloquent ORM은 객체지향 방식을 통해 각 테이블을 모델화하여, 모델과 테이블의 매핑을 통해 데이터를 취급한다. 개발자가 테이블을 PHP 클래스를 활용해 보다 쉽게 접근할 수 있도록 해준다.
Model은 테이블과 매핑하여 데이터를 다루기 위한 클래스를 의미한다. 모델 클래스에서 테이블의 각각의 레코드는 하나의 인스턴스로 대응되며 보다 객체지향적인 방식으로 데이터를 다룰 수 있도록 도와준다.
아래의 Artisan 명령어를 통해서 Model을 추가할 수 있으며, App\Models\ 하위에서 관리된다.
php artisan make:model SampleModel
// migration과 함께 생성한다면 --migration옵션을 사용한다.
php artisan make:model SampleModel --migration
생성된 모델 클래스 및 기본 설정에 대한 내용은 아래와 같다.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Sample extends Model
{
// 테이블명
protected $table = 'table_sample';
// 기본키
protected $primaryKey = 'sample_id';
// 각각의 칼럼을 지정
protected $fillable = [
'name', 'email', 'password'
];
// 숨기고 싶은 칼럼 지정 (조회시, 해당 칼럼 표시X)
protected $hidden = [
'password',
'remember_token',
];
// 모델의 해당 칼럼의 데이터타입을 캐스팅(형변환)
protected $casts = [
'email_verified_at' => 'datetime',
];
// auto increament 사용유무 (default : true)
public $incrementing = false;
// 기본키가 정수가 아닐 경우, 타입 지정이 필요
protected $keyType = 'string';
// 타임스탬프 (created_at, updated_at) 자동 설정유무 (default : true)
public $timestamps = false;
// 타임스탬프 명칭 (created_at, updated_at) 커스텀
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
// 해당 모델에서 다른 DB 사용시 대상DB 정의
protected $connection = 'sqlite';
// 모델의 각 칼럼별 default 정의
protected $attributes = [
'del_flg' => false,
];
}
생성된 모델 간의 관계가 존재하는 경우, 각 모델 클래스에 해당 관계에 대한 내용도 명시해야 한다.
// User 테이블
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
// Profiles 테이블
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id')->unique();
$table->string('phone');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
// posts 테이블
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->text('content');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
// roles 테이블
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
// role_user 테이블 (다대다 관계를 위한 중간테이블)
Schema::create('role_user', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('role_id');
$table->timestamps();
$table->primary(['user_id', 'role_id']);
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
});
// User 모델
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
// Profile 모델
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// User 모델
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
// Post 모델
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// User 모델
class User extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
// Role 모델
class Role extends Model
{
public function users()
{
return $this->belongsToMany(User::class);
}
}
class Category extends Model
{
public function products()
{
// 카테고리에 여러 주문이 있고 각 주문에는 여러 상품이 존재
// = 카테고리는 주문이라는 중간테이블 없이는 상품 확인이 불가
return $this->hasManyThrough(Product::class, Order::class);
}
}
// 사용 사례
$category = Category::find(1); // id가 1인 카테고리
$products = $category->products; // 해당 카테고리의 모든 상품정보