문제

업데이트됨: 2009년 9월 2일 - 질문 수정, 더 나은 예시 제공, 포상금 추가.


안녕,
데이터베이스와 엔터티(도메인 개체) 사이의 데이터 매퍼 패턴을 사용하여 PHP 애플리케이션을 구축하고 있습니다.내 질문은 다음과 같습니다

일반적으로 수행되는 작업을 캡슐화하는 가장 좋은 방법은 무엇입니까?

예를 들어 일반적인 작업 중 하나는 사이트 매퍼에서 하나 이상의 사이트 엔터티를 검색하고 페이지 매퍼에서 관련(홈) 페이지 엔터티를 검색하는 것입니다.현재로서는 다음과 같이 할 것입니다.

$siteMapper = new Site_Mapper();
$site = $siteMapper->findByid(1);

$pageMapper = new Page_Mapper();
$site->addPage($pageMapper->findHome($site->getId()));

이는 매우 사소한 예이지만 실제로는 각 사이트에 연관된 로캘이 있고 페이지에 실제로 여러 개정판이 있기 때문에 더 복잡해집니다. (비록 이 작업의 목적상 가장 최근의 개정판에만 관심이 있지만) ).

내 응용 프로그램 내의 여러 위치에서 이 작업(사이트 및 관련 홈 페이지, 로케일 등 가져오기)을 수행해야 하는데 이 작업을 캡슐화하는 가장 좋은 방법/장소가 생각나지 않습니다. 여기저기서 반복해야 해요.이상적으로는 다음과 같이 끝내고 싶습니다.

$someObject = new SomeClass();
$site = $someObject->someMethod(1); // or
$sites = $someObject->someOtherMethod();

결과 사이트 엔터티에는 이미 관련 엔터티가 생성되어 사용할 준비가 되어 있습니다.

이러한 개체를 다시 저장할 때도 동일한 문제가 발생합니다.사이트 엔터티와 연결된 홈 페이지 엔터티가 있고 둘 다 수정되었다고 가정해 보겠습니다. 다음과 같은 작업을 수행해야 합니다.

$siteMapper->save($site);
$pageMapper->save($site->getHomePage());

다시 말하지만, 이 예는 간단합니다.코드 중복이 여전히 적용됩니다.

내 생각에는 다음을 처리할 수 있는 일종의 중앙 개체를 갖는 것이 합리적입니다.

  • 사이트(들) 및 모든 필수 관련 엔터티 검색
  • 새로운 연관된 엔터티로 새 사이트 엔터티 만들기
  • 사이트(또는 여러 사이트)를 가져와 해당 사이트 및 모든 관련 엔터티(변경된 경우) 저장

그럼 제 질문으로 돌아가서, 이 객체는 무엇이 되어야 할까요?

  • 기존 매퍼 객체?
  • 저장소 패턴을 기반으로 하는 것인가요?*
  • 작업 단위 패턴을 기반으로 한 것인가요?*
  • 다른 것?

* 나는 아니에요 충분히 아마도 짐작할 수 있듯이 둘 중 하나를 이해하십시오.

이 문제에 접근하는 표준 방법이 있습니까? 누군가 이를 구현하는 방법에 대한 간단한 설명을 제공할 수 있습니까?나는 완벽하게 작동하는 구현을 제공할 사람을 찾고 있지 않습니다. 단지 이론만 제공할 뿐입니다.

감사해요,

도움이 되었습니까?

해결책

리포지토리/서비스 패턴을 사용하면 리포지토리 클래스가 각 엔터티에 대한 간단한 CRUD 인터페이스를 제공하고 서비스 클래스는 엔터티 종속성 연결과 같은 추가 논리를 수행하는 추가 계층이 됩니다.그러면 앱의 나머지 부분은 서비스만 활용합니다.귀하의 예는 다음과 같습니다.

$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();

그런 다음 SiteService 클래스 내부에는 다음과 같은 내용이 있습니다.

function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}

저는 PHP를 잘 알지 못하므로 문법적으로 잘못된 점이 있으면 양해해 주시기 바랍니다.

다른 팁

[편집하다:이 항목은 문제를 패턴에 맞추는 것보다 상황을 직접 처리하기 위해 사용자 정의 코드를 작성하는 것이 더 쉽다는 사실을 다루려고 합니다.]

패턴은 개념적으로는 훌륭하지만 항상 "매핑"되는 것은 아닙니다.수년간의 고급 PHP 개발 끝에 우리는 이러한 문제를 처리하는 매우 직접적인 방법을 결정했습니다.이걸 고려하세요:

파일:Site.php

class Site
{
   public static function Select($ID)
   {
      //Ensure current user has access to ID
      //Lookup and return data
   }

   public static function Insert($aData)
   {
      //Validate $aData
      //In the event of errors, raise a ValidationError($ErrorList)

      //Do whatever it is you are doing

      //Return new ID
   }

   public static function Update($ID, $aData)
   {
      //Validate $aData
      //In the event of errors, raise a ValidationError($ErrorList)

      //Update necessary fields
   }

그런 다음 (어디서나) 호출하려면 다음을 실행하세요.

$aData = Site::Select(123);

Site::Update(123, array('FirstName' => 'New First Name'));

$ID = Site::Insert(array(...))

OO 프로그래밍과 PHP에 관해 명심해야 할 한 가지...PHP는 요청 사이의 "상태"를 유지하지 않으므로 즉시 파괴되도록 객체 인스턴스를 생성하는 것은 종종 의미가 없습니다.

아마도 공통 작업을 어딘가의 도우미 메소드로 추출한 다음 디자인이 요구하는 것이 무엇인지 기다리는 것부터 시작할 것입니다.아직 말하기에는 너무 이른 것 같습니다.

이 방법의 이름은 무엇입니까?이름은 일반적으로 메소드가 속한 위치를 암시합니다.

class Page {

  public $id, $title, $url;

  public function __construct($id=false) {
    $this->id = $id;
  }

  public function save() {
    // ...
  }

}

class Site {

  public $id = '';
  public $pages = array();

  function __construct($id) {
     $this->id = $id;
     foreach ($this->getPages() as $page_id) {
       $this->pages[] = new Page($page_id);
     }
  }

  private function getPages() {
    // ...
  }

  public function addPage($url) {
    $page = ($this->pages[] = new Page());
    $page->url = $url;
    return $page;
  }

  public function save() {
    foreach ($this->pages as $page) {
      $page->save();
    } 
    // ..
  }

}

$site = new Site($id);
$page = $site->addPage('/');
$page->title = 'Home';
$site->save();

사이트 개체를 집계 루트 복잡한 연관성을 캡슐화하고 일관성을 보장합니다.

그런 다음 사이트 저장소 사이트 집계를 검색하고 해당 하위 항목(모든 페이지 포함)을 채우는 책임이 있습니다.

별도의 PageRepository가 필요하지 않으며(페이지를 별도의 집계 루트로 만들지 않는다고 가정) SiteRepository는 페이지 개체도 검색하는 책임을 져야 합니다(귀하의 경우 기존 매퍼를 사용하여).

그래서:

$siteRepository = new SiteRepository($myDbConfig);
$site = $siteRepository->findById(1); // will have Page children attached

그리고 나서 findById 메소드는 사이트의 모든 페이지 하위 항목을 찾는 역할도 담당합니다.이는 CodeMonkey1이 제공한 답변과 유사한 구조를 가지지만 이 작업을 위해 특정 서비스를 생성하는 것보다 집계 및 저장소 패턴을 사용하면 더 많은 이점을 얻을 수 있다고 생각합니다.하위 개체를 포함하여 사이트 집계의 기타 모든 검색/쿼리/업데이트는 동일한 SiteRepository를 통해 수행됩니다.

편집하다: 여기에 짧은 DDD 가이드가 있습니다. 용어에 대한 도움을 드리기 위해 꼭 읽어 보시기를 권하고 싶습니다. 에반스 전체 그림을 원한다면.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top