여러 개의 Task 를 갖는 id 가 7인 프로젝트 모델을 삭제하는 경우 테이블을 생성할 때 참조키 관계를 설정했으므로 Task 가 있을 경우 프로젝트는 삭제할 수 없으며 이는 관계형 DBMS 의 기능 동작입니다.


이제 자식 객체가 있는 모델을 가져온 후에 delete() 를 호출하여 삭제할 경우 다음과 같이 참조키 에러가 발생하게 됩니다.

$prj = App\Project::find(7);
$prj->delete();
CODE


Illuminate\Database\QueryException with message 'SQLSTATE[23000]: 
Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`homestead`.`tasks`, CONSTRAINT `tasks_project_id_foreign` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`)) (SQL: delete from `projects` where `id` = 7)'
CODE

위와 같이 자식 객체가 있는 객체 모델을 삭제하는 방법은 다음과 같이 3가지가 있습니다.


Delete시 단계적으로 삭제

 테이블을 생성할 경우 delete() 호출시 관련된 데이타도 삭제하도록 cascade 옵션을 지정하면 됩니다. tasks 테이블을 migrate 하는 파일인 database/migrations/2015_07_05_022752_create_task_table.php 을 열어서 다음과 같이 

->onDelete('cascade') 를 추가해 주면 프로젝트 모델 삭제시 관련된 Task 도 같이 삭제 됩니다. 

$table->foreign('project_id')
      ->references('id')->on('projects')
      ->onDelete('cascade');
CODE


 

자식 객체 삭제후 부모 객체 삭제

부모 모델 객체를 가져온 후에 루프를 돌면서 모든 자식 모델 객체를 삭제하고 그후에 부모 모델 객체를 삭제하면 됩니다. 다음은 id 가 7 번인 프로젝트 객체를 삭제하는 예제입니다.

$prj = App\Project::find(7);
 
foreach($prj->tasks as $task) {
    $task->delete();
}
$prj->delete();
CODE

메소드 호출인 $prj->tasks()가 아니고 dynamic property 인 $prj->tasks 인 것에 주의 하세요 . tasks() 메소드는 모델이 아닌 HasMany 객체를 리턴하므로 의도한 대로 모델이 삭제되지 않으므로 $prj->tasks()->get() 을 사용해야 합니다.


 

모델 이벤트로 처리

부모 모델에 전 절에서 학습한 모델 이벤트중 delete() 가 수행되기 전에 호출되는 이벤트인 deleting() 에 이벤트를 추가하면 자동으로 삭제할 수 있습니다. deleted() 이벤트는 delete() 수행 후 호출되므로 이 경우에 사용할 수 없습니다.

class Project extends Model
{
	public function tasks()
	{
		return	$this->hasMany('App\Task');
	}
	protected static function boot() {
	    parent::boot();
 
		// delete() 수행전 호출
	    static::deleting(function($project) { 
	        $project->tasks()->delete();
	    });
	}
}
CODE

연관 모델 삭제는 위의 세 가지 방법중에 개인적인 선호도에 따라 선택하여 사용하면 됩니다.

 

마치며

이번 장에서는 가볍고 배우기 쉬운 ORM 인 Eloquent 을 통해 데이타베이스를 사용하는 방법에 대해서 학습했습니다.

웹 프로그래밍 개발/유지보수시 비즈니스 로직 변경에 따라 DB 스키마와 SQL을 수정하고 결과를 뷰에 적용하는 일이 대부분의 작업이지만 기존의 SQL 을 직접 입력하는 방식은 사소한 변경이라도 시간이 많이 걸리고 고통스러운 작업이었습니다.

 

또 객체지향적으로 잘 설계해도 DB 쿼리는 객체지향이 아니므로 실제 SQL 로 변환시에는 큰 갭이 있었습니다.

ORM 은 쿼리를 작성하지 않고 객체간의 매핑을 통해 최종 SQL 을 생성할 수 있으므로 ORM 에 익숙해 지면 DB 변경으로 인한 수정을 큰 고통없이 빠르게 대응할 수 있습니다.

 

하지만 ORM 은 마법의 도구가 아니며 제대로 사용하기 위해서는 더 높은 수준의 DB 설계와 객체지향 모델링 지식과 경험이 요구됩니다.

ORM 은 잘못 사용하면 애플리케이션 성능과 DBMS 에 치명적인 영향을 줄 수 있는 N +1 Problem이라는 문제를 접할 수 있으며 이의 해결을 위해서는 이거 로딩(Eager Loading)과 지연 이거 로딩(Lazy Eager Loading) 기법을 필수적으로 익혀야 합니다. (이거 로딩에 대한 내용은 2부에서 다룹니다.)

 

또 아주 복잡하거나 성능에 민감한 쿼리라면 ORM 을 고집하지 말고 해당 부분만 기존 SQL 을 직접 작성하는 방식으로 처리하는 것이 권장됩니다.

라라벨은 이런 경우 사용할 수 있도록 쿼리 빌더(Query Builder - http://goo.gl/q18zlR) 기능을 제공하고 있으므로 업무에 적합한 방식을 선택하여 사용하는 것이 좋습니다.

그러면 이제까지 학습한 내용을 토대로 실전 프로젝트를 진행해 보겠습니다.