스키마 빌더(Schema Builder)
라라벨 스키마 빌더는 데이타베이스의 스키마를 손쉽게 관리할 수 있는 기능을 제공합니다.
스키마 빌더도 파사드이므로 Schema::create 처럼 스태틱 메소드처럼 사용할 수 있으며 데이타베이스 의존적인 부분은 가려지므로 데이타베이스에 의존하지 않고도 스키마를 다룰 수 있습니다.
그러면 스키마 빌더의 주요 기능에 대해서 알아보고 마이그레이션을 작성해 보겠습니다.
테이블 생성
새로운 테이블을 생성할 경우 create 메소드를 사용하며 파라미터로 테이블 명을 주면 됩니다. 두 번째 파라미터는 클로져로 Blueprint 객체가 전달되며 여기에 생성할 스키마를 적어 주면 됩니다.
Schema::create('projects', function (Blueprint $table) {
$table->increments('id');
});
만약 테이블이 존재하는지 확인하려면 hasTable 을 사용할 수 있습니다.
public function down()
{
// 테이블이 있으면 삭제
if (Schema::hasTable('projects')) {
Schema::drop('projects');
}
}
컬럼 추가
테이블에 컬럼을 추가할 경우 BluePrint 의 컬럼 타입에 따른 적절한 메소드를 사용하면 됩니다.
CreateProjectTable
Schema::create('projects', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->string('name', 20);
$table->boolean('public')->default(false);
$table->timestamps();
});
$table 의 컬럼 타입에 따라 별도의 메소드가 존재하며 메소드 명을 보면 컬럼 타입을 짐작할 수 있으므로 별도의 설명은 생략하겠습니다.
timestamps() 메소드를 사용하면 테이블에 created_at, updated_at 컬럼을 자동으로 만들어 주게 됩니다. 이 컬럼은 데이타의 삽입/갱신시 사용되는 컬럼으로 다른 컬럼명으로 사용하려면 timestamp() 메소드에 컬럼명을 적어주면 되며 다음은 added_on 이라는 컬럼을 생성하는 예제입니다.
$table->timestamp('added_on');
컬럼에 기본 값이나 널 허용 여부등이 필요할 경우 메소드 체이닝을 통해 unsigned() 같은 Modifier 를 지정해 주면 됩니다.
Modifier | 의미 |
---|---|
->nullable() | 해당 컬럼은 NULL 을 허용 |
->default($value) | 컬럼의 기본 값을 설정 |
->unsigned() | integer 컬럼을 0 이상의 값만 허용 |
이외에도 아래와 같은 컬럼 명령이 있으므로 자세한 내용은 http://laravel.kr/docs/5.2/migrations#creating-columns 를 참고하시기 바랍니다.
인덱스
스키마 빌더는 컬럼에 여러 가지 타입의 인덱스를 추가할 수 있습니다. 인덱스는 컬럼을 생성하면서 바로 추가할수도 있고 별도의 명령을 통해 추가할 수도 있습니다.
라라벨에는 기본적으로 사용자 테이블이 포함(database/migrations/2014_10_12_000000_create_users_table.php)되어 있으며 이 파일을 예를 들어보면 id 는 primary 키이므로 인덱스가 생성되며 이메일은 유일해야 하므로 unique() 인덱스를 사용하고 있습니다.
database/migrations/2014_10_12_000000_create_users_table.php
public function up()
{
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password', 60);
$table->timestamps();
});
}
인덱스를 별도의 구문으로 분리하여 생성하려면 아래의 문법을 사용하여 인덱스를 추가하면 됩니다.
명령 | 설명 |
---|---|
$table->primary('id'); | id 컬럼을 기본 키로 추가 |
$table->primary(['first', 'last']); | first 와 last 컬럼을 다중 기본 키로 사용 |
$table->unique('email'); | email 컬럼에 유일 인덱스 추가 |
$table->index('state'); | state 컬럼에 기본 인덱스 추가 |
인덱스 삭제
인덱스의 유형에 따라 별도의 삭제 메서드가 제공되고 있으며 파라미터로 인덱스 이름을 전달해야 합니다. 인덱스 이름은 테이블 이름, 컬럼명, 인덱스 종류를 조합하여 만들어 집니다.
명령 | 설명 |
---|---|
$table->dropPrimary('users_id_primary'); | users 테이블에서 기본 키 삭제 |
$table->dropUnique('users_email_unique'); | users 테이블에서 email 컬럼에 설정된 유일 인덱스 삭제 |
$table->dropIndex('geo_state_index'); | geo 테이블에서 state 컬럼에 설정된 기본 인덱스 삭제 |
외래 키 제약
스키마 빌더는 외래키를 지원하므로 foreign() 메소드를 사용하여 외래키를 지정해 주면 됩니다.
모든 task 는 projects 에 포함되므로 projects 테이블의 기본키인 id 를 외래키로 갖고 있어야 하므로 다음과 같이 project_id 는 외래키이며 projects 테이블의 id 를 참고한다고 지정할 수 있습니다.
2015_07_05_022752_create_task_table.php
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->integer('project_id')->unsigned();
$table->foreign('project_id')->references('id')->on('projects');
$table->string('name', 50);
$table->text('description')->nullable();
$table->timestamps();
$table->timestamp('due_date')->nullable();
});
}
외래키를 삭제할 경우 dropForeign() 메소드를 사용하며 파라미터로 외래키의 의 이름을 전달해야 합니다. 외래키의 이름도 인덱스처럼 테이블이름,외래키 컬럼, foreign 키워드를 조합하여 사용하면 됩니다.
위에서 생성한 외래키의 이름은 tasks_project_id_foreign 이므로 다음과 같이 reset 때 삭제할 수 있습니다.
public function down()
{
Schema::table('tasks', function (Blueprint $table) {
$table->dropForeign('tasks_project_id_foreign');
});
}
migrate 적용
이제 생성된 스키마를 적용하기 위해 migrate 를 실행할 순서입니다. migrate 는 artisan migrate 명령어로 실행할 수 있으며 먼저 현재 상태를 보기 위해 migrate:status 를 실행하면 migration 파일명과 적용 여부가 표시됩니다.
$ php artisan migrate:status
+------+------------------------------------------------+
| Ran? | Migration |
+------+------------------------------------------------+
| N | 2014_10_12_000000_create_users_table |
| N | 2014_10_12_100000_create_password_resets_table |
| N | 2015_07_05_022234_create_project_table |
| N | 2015_07_05_022752_create_task_table |
+------+------------------------------------------------+
적용은 migrate 명령을 통해서 하면 되며 migration 파일이 생성된 순서대로(먼저 생성된 파일이 먼저 실행) 실행되며 정상적으로 종료되면 아래와 같이 migration 한 파일 목록이 출력됩니다.
$ php artisan migrate
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2015_07_05_022234_create_project_table
Migrated: 2015_07_05_022752_create_task_table
production 환경에서는 migration 시 스키마 변경에 따른 데이타의 손실이 발생할 수 있으므로 실행 여부를 묻는 프롬프트를 출력하고 'y' 를 입력해야 진행됩니다.
vagrant@homestead:~/Code/Laravel$ php artisan migrate
**************************************
* Application In Production! *
**************************************
Do you really wish to run this command? [y/N] (yes/no) [no]:
>
migrate 시 "Class 'CreateTaskTable' not found" 와 같은 에러가 발생한다면 migration 클래스를 오토로딩하지 못해서 이므로 다음 명령어로 새로 오토로딩 정보를 생성해 주면 됩니다.
composer dump-autoload
MySQL 클라이언트나 Heidisql 등의 쿼리 툴을 통해 생성된 테이블을 확인해 볼 수 있습니다.
스키마 변경
애플리케이션을 변경할 경우 스키마도 같이 변경해야 하는 경우가 많으며 라라벨 migration 은 스키마의 버전 관리가 가능하므로 잦고 사소한 변경 작업시 특히 유용합니다.
생성된 테이블의 컬럼을 변경할 경우 Schema::create 대신 Schema::table 메소드를 사용하며 클로져에서 변경 내용을 기술합니다.
- 컬럼 속성 변경: 프로젝트 테이블의 name 컬럼이 20에서 50자로 변경될 경우 $table->string('name', 50)->change(); 처럼 변경될 컬럼과 크기를 적고 change() 메소드를 사용하면 됩니다.
- 컬럼 이름 변경: renameColumn() 메소드에 예전 컬럼명과 새로운 컬럼명을 파라미터로 호출합니다.
- 컬럼 추가: 신규 테이블 생성시 컬럼 지정 방법과 동일합니다.
그러면 컬럼을 변경하는 migration 파일을 생성해 봅시다.
$ php artisan make:migration increase_name_and_rename_public_column_on_project --table=projects
Created Migration: 2015_07_05_031307_increase_name_and_rename_public_column_on_project
기존에 있는 테이블을 변경하므로 --create 옵션은 제외하고 --table 옵션으로 테이블을 지정했습니다.
마이그레이션 클래스는 작업명인 add_label_column_to_project_table 를 studly_case() 헬퍼 메소드를 사용하여 StudlyCase 로 변환한 이름인AddLabelColumnToProjectTable 입니다.
php artisan tinker 를 사용하여 간단하게 생성되는 클래스 파일명을 확인할 수 있습니다.
>>> studly_case('add_label_column_to_project_table')
=> "AddLabelColumnToProjectTable"
이제 생성된 파일을 열어서 20자로 생성된 name 컬럼을 50자로 변경하고 public 컬럼 명을 is_public 으로 변경해 보겠습니다.
public function up()
{
Schema::table('projects', function (Blueprint $table) {
$table->string('name', 50)->change();
$table->renameColumn('public', 'is_public');
$table->string('label', 20)->nullable();
});
}
public function down()
{
Schema::table('projects', function (Blueprint $table) {
$table->dropColumn('label');
});
}
컬럼이 추가되므로 down() 메소드에 추가된 컬럼인 label 을 삭제하는코드를 넣어주고 바로 migrate 을 적용해 봅시다.
$ php artisan migrate
Changing columns for table "projects" requires Doctrine DBAL; install "doctrine/dbal".
라라벨 5.1은 다음과 같이 에러 메시지가 출력되며 5.2에 비해 이해하기가 어렵습니다.
PHP Fatal error: Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found in /home/vagrant/Code/Laravel/vendor/laravel/framework/src/Illuminate/Database/MySqlConnection.php on line 64 PHP Stack trace: PHP 1. {main}() /home/vagrant/Code/Laravel/artisan:0 PHP 2. Illuminate\Foundation\Console\Kernel->handle() /home/vagrant/Code/Laravel/artisan:36
이번 migration 은 정상적으로 동작하지 않고 Doctrine의 DBAL 패키지를 찾지 못한다는 예외가 발생합니다. 이는 컬럼 이름을 변경하거나 사이즈를 변경하는 작업을 하려면 DBAL(Database Abstraction Layer - https://github.com/doctrine/dbal) 이라는 패키지가 필요한데 라라벨에 기본적으로 포함되어 있지 않기 때문입니다.
이 에러는 composer 를 사용하여 패키지를 추가해 주면 간단하게 해결됩니다.
$ composer require "doctrine/dbal" "~2.0"
이제 migration 을 실행하면 정상적으로 완료되며 스키마를 조회하면 컬럼 사이즈와 이름이 변경된 것을 확인할 수 있습니다.
$ php artisan migrate
Migrated: 2015_07_05_031307_increase_name_and_rename_public_column_on_project
migrate 되돌리기
수행한 migration 의 마지막을 돌리려면 migrate:rollback 을 사용하면 되며 모두 되돌리려면 migrate:reset 명령어를 사용하면 됩니다. 리셋을 수행하면 가장 최신의 migration 파일부터 down() 메소드를 호출해 줍니다.
$ php artisan migrate:reset
Rolled back:: 2015_07_05_022752_create_task_table
Rolled back:: 2015_07_05_022234_create_project_table
Rolled back:: 2014_10_12_100000_create_password_resets_table
Rolled back:: 2014_10_12_000000_create_users_table
만약 reset 을 수행하고 migration 을 처음부터 시작할 필요가 있다면 migrate:refresh 명령을 사용하면 되며 이는 migrate:reset 과 migrate 를 연속으로 호출한 것과 동일합니다.
migration 을 되돌리는 작업은 데이타의 손실이 발생할수 있으므로 운영 환경에서는 주의깊게 사용해야 합니다. 특히 모든 스키마를 삭제하고 다시 설치하는 migrate:reset 과 migrate:refresh 는 운영 환경에서 절대 사용하면 안 됩니다.
되돌리기 전에 어떤 쿼리를 수행하는지를 알고 싶으면 --pretend 옵션을 추가하여 실행하면 롤백시 수행하는 SQL 을 화면에 덤프해 주므로 개별 되돌리기 작업이 어떤 영향을 주는지 파악할 수 있습니다.
$ php artisan migrate:rollback --pretend
AddLabelColumnToProjectTable: alter table `projects` drop `label`
CreateProjectTable: drop table `projects`
CreateBookTable: select * from information_schema.tables where table_schema = ? and table_name = ?
CreateAuthorTable: select * from information_schema.tables where table_schema = ? and table_name = ?
CreatePasswordResetsTable: drop table `password_resets`
CreateUsersTable: drop table `users`
Ref