[CakePHP] Transaction 다루는 방법과 Entity를 이용해서 Insert, Update, Delete하는 방법


Study/PHP  2019. 10. 11. 09:00

안녕하세요. 명월입니다.


이 글은 CakePHP에서 Transaction 다루는 방법과 Entity를 이용해서 Insert, Update, Delete하는 방법에 대한 글입니다.


CakePHP의 ORM을 사용하지 않고 그냥 insert delete 함수를 사용해서 데이터를 추가 삭제를 할 수 있습니다.

링크 - [PHP] CakePHP에서 데이터 베이스(MariaDB(Mysql))를 연결하는 방법


그러나 우리는 데이터 무결성을 위해서 트랜젝션을 만들고 객체를 생성해서 데이터를 추가하는 방법으로 작성해야 합니다.

테이블 예제는 전 포스트의 예제를 이어서 하겠습니다.

링크 - [CakePHP] ORM을 이용한 테이블 Fetch 설정


우리가 User테이블에 데이터를 넣고 파생 데이터로 Info데이터 그리고 Info2데이터까지 넣는다고 예를 생각합니다.

User테이블에서 Info테이블까지는 키가 nvarchar의 id이므로 User테이블에 데이터를 입력하고 Info데이터에 입력하는 것으로 문제가 되지 않습니다.

그러나 Info2테이블의 경우는 Info의 idx키를 가져와야 하는데 이는 Info를 Insert하고 auto_increment된 키를 가져와서 입력을 해야합니다. 이런 작업이 꽤 많다고 할경우, 파생테이블이 많다고 할 경우 꽤 복잡한 처리식이 될 것입니다

처리 중에 에러가 발생할 경우, 전체 롤백을 해야 하는데 이는 Transaction을 이용하면 되므로 ORM과는 상관이 없겠네요.


먼저 Transaction입니다.

<?php

namespace App\Controller;

use Cake\Datasource\ConnectionManager;
use Cake\ORM\TableRegistry;
use Cake\Core\Exception\Exception;

class HomeController extends AppController
{
  public function index()
  {
    $connection = ConnectionManager::get('default');
    $connection->transactional(function ($conn) {
      // 트랜젝션 안에서 에러가 없으면 Commit이 이루어지고, 에러가 발생하면 자동 rollback이 됩니다.
    });
  }
}

CakePHP 프레임워크는 트랜젝션이 옵져버 패턴으로 이미 구현이 되어 있네요.. 자바나 C#의 경우는 사용자가 구현해야 하는데..

링크 - [Java강좌 - 51] JPA에서 transaction 다루기와 옵서버 패턴을 이용해서 Transaction 공통 함수 만들기


저기 안에서 데이터를 추가하도록 합니다.

<?php

namespace App\Controller;

use Cake\Datasource\ConnectionManager;
use Cake\ORM\TableRegistry;

class HomeController extends AppController
{
  public function index()
  {
    $connection = ConnectionManager::get('default');
    $connection->transactional(function ($conn) {
      // Table클래스를 가져온다.
      $userTable = TableRegistry::get('User');
      // Table 클래스를 통해 Entity를 생성한다.
      $user = $userTable->newEntity();
      // id를 new 넣고 name을 new Name을 넣는다.
      $user->id = "new";
      $user->name = "new Name";
      // 저장(transaction이 끝나면 적용된다.)
      $userTable->save($user);

      $infoTable = TableRegistry::get('Info');
      $info = $infoTable->newEntity();
      // User클래스의 id를 가져온다.
      $info->id = $user->id;
      $info->age = 10;
      // 저장(transaction이 끝나면 적용된다.)
      $infoTable->save($info);

      $info2Table = TableRegistry::get('Info2');
      $info2 = $info2Table->newEntity();
      // Info클래스의 id를 가져온다.
      $info2->info_idx = $info->idx;
      $info2->birth = 10;
      // 저장(transaction이 끝나면 적용된다.)
      $info2Table->save($info2);
    });
  }
}

위 소스에서 info 테이블의 들어가는 id는 $user->id = "new"라는 구문으로 데이털르 넣기 때문에 데이터 베이스에서 key를 가져올 필요는 없지만, info2의 info_idx의 경우는 info->idx에서 가져오는 데 이는 데이블에서 insert가 되고 난 값입니다.


이제 update와 delete를 확인하겠습니다.

update와 delete는 데이터 베이스의 있는 데이터를 수정하는 것이므로 일단 검색으로 데이터를 가져옵니다.

<?php

namespace App\Controller;

use Cake\Datasource\ConnectionManager;
use Cake\ORM\TableRegistry;

class HomeController extends AppController
{
  public function index()
  {
    $connection = ConnectionManager::get('default');
    $connection->transactional(function ($conn) {
      // Table클래스를 가져온다.
      $table = TableRegistry::get('User');
      $query = $table->find();
      // id가 new인 데이터를 검색한다.
      $query = $query->where(['id' => 'new']);
      $user = $query->first();
      // name을 수정한다.
      $user->name = "modified!!";
      // 저장(transaction이 끝나면 적용된다.)
      $table->save($user);
    });
  }
}

위에서 데이터 베이스로 부터 데이터를 가져온 후 name을 수정하고 다시 save를 하니 데이터 베이스도 적용이 되었습니다.

<?php

namespace App\Controller;

use Cake\Datasource\ConnectionManager;
use Cake\ORM\TableRegistry;
use Cake\Core\Exception\Exception;

class HomeController extends AppController
{
  public function index()
  {
    $connection = ConnectionManager::get('default');
    $connection->transactional(function ($conn) {
      $table = TableRegistry::get('User');
      $query = $table->find();
      $query = $query->where(['id' => 'new']);
      $user = $query->first();
      // cascade 용으로 파생 데이터들 모두 삭제한다.
      foreach($user->infos as $info){
        foreach($info->info2s as $info2){
          TableRegistry::get('Info2')->delete($info2);  
        }
        TableRegistry::get('Info')->delete($info);
      }
      // 삭제(transaction이 끝나면 적용된다.)
      $table->delete($user);
    });
  }
}

분명 CakePHP에 Fetch기능과 cascade기능이 있습니다. 근데 그 설정이 상당히 복잡해서 생략을 했더니 이렇게 reference를 참조하는 영역에서 이상한 코드가 되어 버리네요. 다시 Fetch기능에 대해 연구해 봐야 겠습니다.


여기까지 CakePHP에서 Transaction 다루는 방법과 Entity를 이용해서 Insert, Update, Delete하는 방법에 대한 글이였습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.