라라벨 스키마 빌더는 데이타베이스의 스키마를 손쉽게 관리할 수 있는 기능을 제공합니다.

스키마 빌더도 파사드이므로 Schema::create 처럼 스태틱 메소드처럼 사용할 수 있으며 데이타베이스 의존적인 부분은 가려지므로 데이타베이스에 의존하지 않고도 스키마를 다룰 수 있습니다.


그러면 스키마 빌더의 주요 기능에 대해서 알아보고 마이그레이션을 작성해 보겠습니다.


테이블 생성

새로운 테이블을 생성할 경우 create 메소드를 사용하며 파라미터로 테이블 명을 주면 됩니다. 두 번째 파라미터는 클로져로 Blueprint 객체가 전달되며 여기에 생성할 스키마를 적어 주면 됩니다.

Schema::create('projects', function (Blueprint $table) {
     $table->increments('id');
});
PHP

만약 테이블이 존재하는지 확인하려면 hasTable 을 사용할 수 있습니다.

public function down()
{
	// 테이블이 있으면 삭제
	if (Schema::hasTable('projects')) {
    	Schema::drop('projects');
	}
}
PHP


컬럼 추가

테이블에 컬럼을 추가할 경우 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();
});
PHP

$table 의 컬럼 타입에 따라 별도의 메소드가 존재하며 메소드 명을 보면 컬럼 타입을 짐작할 수 있으므로 별도의 설명은 생략하겠습니다.

timestamps() 메소드를 사용하면 테이블에 created_at, updated_at 컬럼을 자동으로 만들어 주게 됩니다. 이 컬럼은 데이타의 삽입/갱신시 사용되는 컬럼으로 다른 컬럼명으로 사용하려면 timestamp() 메소드에 컬럼명을 적어주면 되며 다음은 added_on 이라는 컬럼을 생성하는 예제입니다.

 $table->timestamp('added_on');
CODE


컬럼에 기본 값이나 널 허용 여부등이 필요할 경우 메소드 체이닝을 통해 unsigned() 같은 Modifier 를 지정해 주면 됩니다.


이외에도 아래와 같은 컬럼 명령이 있으므로 자세한 내용은 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();
    });
}
PHP

인덱스를 별도의 구문으로 분리하여 생성하려면 아래의 문법을 사용하여 인덱스를 추가하면 됩니다.





인덱스 삭제

인덱스의 유형에 따라 별도의 삭제 메서드가 제공되고 있으며 파라미터로 인덱스 이름을 전달해야 합니다. 인덱스 이름은 테이블 이름, 컬럼명, 인덱스 종류를 조합하여 만들어 집니다.

명령설명
$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();
    });
}
PHP

외래키를 삭제할 경우 dropForeign() 메소드를 사용하며 파라미터로 외래키의 의 이름을 전달해야 합니다. 외래키의 이름도 인덱스처럼 테이블이름,외래키 컬럼, foreign 키워드를 조합하여 사용하면 됩니다.

위에서 생성한 외래키의 이름은 tasks_project_id_foreign 이므로 다음과 같이 reset 때 삭제할 수 있습니다.

public function down()
{ 
    Schema::table('tasks', function (Blueprint $table) {
        $table->dropForeign('tasks_project_id_foreign');
    });
}
PHP



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            |
+------+------------------------------------------------+
BASH

적용은 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
BASH

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]:
 > 
CODE


migrate 시 "Class 'CreateTaskTable' not found"  와  같은 에러가 발생한다면 migration 클래스를 오토로딩하지 못해서 이므로 다음 명령어로 새로 오토로딩 정보를 생성해 주면 됩니다.

composer dump-autoload
CODE




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 
CODE

기존에 있는 테이블을 변경하므로 --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');
    });
}
PHP

컬럼이 추가되므로 down() 메소드에 추가된 컬럼인 label 을 삭제하는코드를 넣어주고 바로 migrate 을 적용해 봅시다.


$ php artisan migrate
 
Changing columns for table "projects" requires Doctrine DBAL; install "doctrine/dbal".
BASH

라라벨 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"
BASH


이제 migration 을 실행하면 정상적으로 완료되며 스키마를 조회하면 컬럼 사이즈와 이름이 변경된 것을 확인할 수 있습니다.

$ php artisan migrate
 
Migrated: 2015_07_05_031307_increase_name_and_rename_public_column_on_project 
BASH


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
BASH

만약 reset 을 수행하고 migration 을 처음부터 시작할 필요가 있다면 migrate:refresh 명령을 사용하면 되며 이는 migrate:resetmigrate 를 연속으로 호출한 것과 동일합니다.


migration 을 되돌리는 작업은 데이타의 손실이 발생할수 있으므로 운영 환경에서는 주의깊게 사용해야 합니다. 특히 모든 스키마를 삭제하고 다시 설치하는 migrate:resetmigrate: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`
BASH