// 테이블에 외래키 제약조건이 걸려있을 경우 migration으로 일괄 삭제시에 // 오류가 발생하므로, 외래키 체크 옵션을 비활성화 해줘야될 수 도 있다. publicfunctiondown() { DB::statement('SET FOREIGN_KEY_CHECKS = 0'); Schema::dropIfExists('table_name'); DB::statement('SET FOREIGN_KEY_CHECKS = 1'); }
실행
php artisan migrate 명령어로 실행하면 된다.
migrate => 전체 실행
migrate:refresh => 다시 실행
migrate:rollback => 마지막 migrate 시점으로 돌림
migrate:reset => 제거
Tinker
이미 데이터가 들어간 테이블이 있어 migrate –path로 한 파일만을 실행해야 하는데, tinker를 사용하면 더 쉽게 해당 마이그레이션을 실행시킬 수 있다. Tinker는 커맨드로 라라벨 쉘(실행환경)으로 들어간다고 생각하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# tinker $ php artisan tinker
# shell로 접속된다. Psy Shell v0.8.5 (PHP 7.0.7 ??cli) by Justin Hileman New version is available (current: v0.8.5, latest: v0.8.8) >>> # blueprint 의존성을 추가해주고 (Blueprint는 up 메소드에 DI로 들어가 있기에 읽지 못한다.) $ use Illuminate\Database\Schema\Blueprint; # 새로 실행하려 했던 스키마를 실행시켜주기만 하면 된다. $ Schema::create('new_table', function (Blueprint $table) { $table->increments('idx'); $table->string('id', 20)->unique(); $table->timestamps(); });
DB에 해당 테이블이 생성된 걸 확인할 수 있다.
Seed
Seed는 테이블에 필수 데이터 또는 더미 데이터를 심어주는 과정이다. 테스트에 필요한 데이터를 넣어주는데 아주 효과적이다.
생성
1
$ php artisan make:seeder 시더명
database/seeds 아래에 파일이 생성된다. 파일을 열어보면 run() 메소드 하나가 있는데, 여기에 시드파일이 호출될 때 실행할 로직을 구현해주면 된다.
classBoardextendsModel{ // 임의의 생성 필드가 있다면 설정해준다. (기본값 created_at) constCREATED_AT = 'created_dt'; // 사용하지 않을 경우 Null로 초기화해주자. (기본값 updated_at) constUPDATED_AT = null; // 둘 다 사용하지 않을 경우 timestamps만 꺼주면 된다. (기본값 true) public$timestamps = false; // PK가 auto increment가 아닐경우 false로 바꿔준다. (기본값 true) public$incrementing = false;
// PK 값을 변경한다. (기본값 id) protected$primaryKey = 'idx'; // 테이블 명을 변경한다. (기본값 모델명의 복수형) protected$table = 'board';
// dump insert 및 dump update가 안되는 필드를 설정한다. protected$guarded = [ 'idx' ];
// guarded와 fillable 중 하나만 있으면 된다. (써보니 guarded가 편하더라.) // dump insert 및 dump update가 가능한 필드를 설정한다. //protected $fillable = [ // 'title', 'content' //];
// 노출시키지 않을 필드가 있을 경우 protected$hidden = [ 'password' ]; }
Relationship
1:1
hasOne과 belongsTo로 연결한다.
1:n
hasMany와 belongsTo로 연결한다.
n:m
belongsToMany로 연결한다. 처음에 제일 감이 안왔던 Relationship이지만 코드를 보면 이해하기가 쉽다. 주문(Order) - 주문 상품(OrderProduct) - 상품(Product) 테이블이 있다고 가정한다. 각각 시작 - 링크(피벗) - 종료 테이블이다.
1 2 3 4 5 6 7 8
<?php App/Models/Order.php
publicfunctionproduct(){ // 마지막으로 연결될 모델명, 링크 테이블 명, 시작->링크를 연결시킬 인덱스, 링크->종료를 연결시킬 인덱스 return$this->belongsToMany('App\Models\Product', 'order_product', 'order_id', 'product_id') // 링크 테이블의 컬럼을 가져와야할 때 ->withPivot(['price', 'quantity']); }
이렇게 정의하면 뷰에서 order->product를 foreach로 돌려 product->product_id처럼 접근할 수 있고 product->pivot->price로 pivot 테이블의 데이터도 가져올 수 있다.
1:1:1
이런 관계가 있을 경우에 라라벨에서 관계메소드를 직접 지원하지는 않는다. 억지로 사용하려면, hasOne을 두 번 사용해 연결해야하는데, Select Query를 두 번 날려야 된다는 소리다. (참을 수 없다)
BelongsToThrough trait 패키지를 통해 깔끔하게 해결할 수 있다. 메소드에서 belongsToMany처럼 커스텀 외래키를 사용해야할 경우엔 5번째 파라미터로 [ 클래스 => 키 ] 형식으로 넘겨주면 된다. (여기의 소스를 참고하자)
이슈
ORM의 Join 방식
한 방 쿼리가 불가능하다. 오직 PK를 통해 Select된 데이터들을 다시 PHP 단에서 합쳐서 보여준다. 한 방이 필요할 때는 Database에서 View를 이용하거나 DB 파사드를 써야한다.
Composite Key
라라벨 Eloquent Model에서 복합키를 사용할 수는 없다. 편법으로 trait를 추가할 수 있는데 find 메소드도 overriding 해야 된다.
Timestamps
created_at과 updated_at은 timestamps 형식이라 date_format과 같은 쿼리함수 대신 whereDate 메소드로 연산을 실행해야한다. 물론 DB에 데이터가 들어가기 전 datetime으로 해당 컬럼을 변경해주면 된다. (timestamps의 유효기간은 2035년까지므로.)
Group By
GroupBy를 이용한 쿼리 사용시에 Syntax error or access violation: 1055 ‘table.column’ isn’t in GROUP BY라는 오류 메세지가 보이며 실행이 안 되는 경우가 있다. 해당 컬럼으로 GroupBy를 하지 않았는데도 발생한다.
database config의 strict 모드 중 ONLY_FULL_GROUP_BY 모드가 활성화 되어있어서 그런데 이 모드만 제외를 시켜주면 된다.
실무에선 Paging 호출을 GET보단 AJAX를 쓰는게 깔끔한데 List View와 ListItem View, Controller 세부분을 모두 변경해줘야한다. 아래 소스는 jQuery를 사용하고 있다고 가정한다. (Frontend Framework를 같이 사용하고 있다면 더 깔끔하게 처리 될 수 있을듯)
Controller
YourController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<?php publicfunctionlist(Request $request) { // ajax 요청일 경우에 listitem 뷰 반환 if ($request->ajax()) { returnview('listitem', [ 'data' => Model::where()->paginate(10); ]); // get 요청일 경우에 list 뷰 반환 } else { returnview('list', [ 'data' => Model::where()->paginate(10); ]) } }
<div id="list"> @include('listitem') </div> <script> var paging_listener = function () { // 페이지 버튼에 click listener 등록 $('.pagination a').click(function (e) { // a href 로 페이지가 이동하는걸 방지한다. e.preventDefault(); get_list($(this).attr('href')); }); };
var get_list = function (url) { $.get(url) .then(function(html) { $('#list').html(html); // 페이지가 다시 그려졌으므로 listener를 다시 등록한다. paging_listener(); }); };