2013-08-21 2 views
1

데이터를보고하기위한 인트라넷 사이트를 개발 중입니다. 저는 고객, 주문, 아이템, 청구서 등을위한 다양한 클래스를 개발했습니다.이 클래스들은 모두 서로 관련이 있습니다. 각 클래스 생성자는 MySQL 데이터베이스 (여러 쿼리)를 쿼리하고 이에 따라 속성을 채 웁니다.결과 집합 내의 개체 인스턴스화 ... 모범 사례?

현재 코드를 편리하고 일관성있게 유지하기 위해 필자는보고에서 클래스를 인스턴스화하는 데 많은 의존을하고 있습니다. 문제는 각 클래스 생성자가 다양한 소스에서 관련 속성을 가져 오거나 계산하여 여러 개의 MySQL 쿼리를 가질 수 있다는 것입니다. 이로 인해 루프 내의 모든 쿼리로 인해 성능이 저하됩니다. 그것은 사용할 수있어, 나는 한 번에 많은 톤의 사용자를 가지지 않을 것이다. 그러나 훨씬 빨라질 수있다.

예를 들어 고객의 최근 50 개 주문을 나열하고 있다고 가정 해 봅시다. 지금 내가하고있는 방식으로, 일반적으로 고객에게 50 개의 주문 ID만을 반환하는 간단한 쿼리를 작성합니다. 그런 다음 결과를 반복하면서 각 결과에 대해 새로운 순서를 인스턴스화합니다. 때로는 한 단계 더 깊숙히 들어가서 주문 내 각 항목에 대해 새 객체를 인스턴스화 할 수도 있습니다.

일부 의사 아이디어를주고 ....

$result = mysql_query("SELECT DISTINCT id FROM orders WHERE customer = 1234 LIMIT 50"); 
while ($row = mysql_fetch_array($result, MYSQL_NUM)) { 
    $order = new Order($row['id']); // more MySQL queries happen in constructor 
    // All of my properties are pre-calculated and formatted 
    // properly within the class, preventing me from having to redo it manually 
    // any time I perform queries on orders 
    echo $order->number; 
    echo $order->date; 
    echo $order->total; 
    foreach($order->items as $oitem) { 
     $item = new Item($oitem); // even more MySQL queries happen here 
     echo $item->number; 
     echo $item->description; 
    } 
} 
난 단지 요약 행에 개체 속성의 몇 가지를 사용할 수 있지만 내가 드릴 다운 할 때 더 자세히 주문을 볼

의 주문 클래스에는 준비가 필요한 모든 속성이 있습니다. 멋지고 깔끔합니다.

이것이 일반적으로 가장 잘 처리되는 방법은 무엇입니까? 요약 쿼리의 경우 클래스를 인스턴스화하지 않고 클래스와 별도의 쿼리 하나를 모두 가져와야합니까? 나는 내가 결과 집합 쿼리를 할 때마다 매번 클래스 내에서 일반적으로 많은 배경 작업을 수작업으로해야하기 때문에 걱정된다. 차라리 한 곳에서 변경하고 모든 페이지에서 반영된 클래스/속성을 쿼리하고 싶습니다.

이것을 적절하게 처리하는 다른 방법은 무엇입니까?

답변

1

이 질문은 꽤 개방적이어서, 나는 꽤 폭 넓은 대답을하고 너무 기분 전환하려고하지 않을 것입니다. Doctrine, Propel 또는 Symfony와 같은 ORM 솔루션은 관계형 객체를 관리하는 데 가장 이상적이지만 항상 신속하고 깔끔하게 구현할 수있는 것은 아닙니다 (ORM을 배우는 데 시간이 오래 걸릴 수 있습니다. 기존 코드). 좀 더 가벼운 접근법에 대해 알아 보겠습니다.

시작하려면 데이터베이스에 액세스 할 때 더 잘 제어 할 수 있도록 클래스 생성자에서 데이터베이스 쿼리를 가져 오는 것이 도움이 될 수 있습니다. 한 가지 전략은 결과를 가져 오기 위해 클래스에 정적 메서드를 추가하는 것입니다. 또한 조회를 일괄 적으로 수행 할 수 있도록 하위 오브젝트를 "프리 페치 (prefetch)"하는 옵션을 제공 할 수 있습니다. 그래서 예에 가고, 외부 API는 다음과 같이 보일 것이다 :

$orders = Order::getOrders(array(
    'items' => true 
)); 
여기 아이디어는 당신이 getOrders() 방법과 순서의 배열을 얻을 item 아이가에서 개체를 가져 getOrders()을 말하고 싶은 것입니다

같은 시간. Order 클래스 외부에서는 매우 간단합니다. 'items' 키를 true으로 설정 한 배열을 전달하면됩니다.그런 다음 Order 클래스 내에서 :

class Order 
{ 

    public $items = null; 

    public static function getOrders(array $options = array()) 
    { 
     $orders = array(); 

     $result = mysql_query("SELECT DISTINCT id FROM orders WHERE customer = 1234 LIMIT 50"); 
     while ($row = mysql_fetch_array($result, MYSQL_NUM)) { 
      $order = new Order($row); 
      $orders[$order->id] = $order; 
     } 

     // if we're asked to fetch items, fetch them in bulk 
     if (isset($options['items']) && $options['items'] === true) { 
      $this->items = array(); 
      $items = Item::getItemsForOrders($orders); 
      foreach ($items as $item) { 
       $orders[$item->orderId]->items[] = $items; 
      } 
     } 

     return $orders 
    } 

    public function __construct(array $data = array()) 
    { 
     // set up your object using the provided data 
     // rather than fetching from the database 
     // ... 
    } 

    public function getItems() 
    { 
     if ($this->items === null) { 
      $this->items = Item::getItemsForOrders(array($this)) 
     } 

     return $items; 
    } 
} 

그리고 당신의 Item 클래스 : 당신이 당신의 주문을 받고있어 때 항목을해야 함을 알고있는 경우

class Item 
{ 
    public $orderId = null; 

    public static function getItemsForOrders(array $orders, array $options = array()) 
    { 
     // perform query for ALL orders at once using 
     // an IN statement, returning an array of Item objects 
     // ... 
    } 
} 

자,의에 대한 true 전달 'items' 옵션 :

$orders = Order::getOrders(array(
    'items' => true 
)); 

아니면 항목이 필요하지 않은 경우, 아무 것도 지정하지 :

$orders = Order::getOrders(); 

어느 쪽이든, 당신은 당신의 주문을 통해 반복 할 때, API는 액세스하는 항목에 대한 동일합니다 : 당신이 볼 수 있듯이, 'items' 옵션을 제공하는 데이터베이스를보다 효율적으로 사용 될 수 있습니다

// the following will perform only 2 database queries 
$orders = Order::getOrders(array(
    'items' => true 
)); 
foreach ($orders as $order) { 
    $items = $order->getItems(); 
} 

// the following will perform 1 query for orders 
// plus 1 query for every order 
$orders = Order::getOrders(); 
foreach ($orders as $order) { 
    $items = $order->getItems(); 
} 

, 그러나 주위를 어지럽히 지 않고 orders이 필요하다면 그렇게 할 수도 있습니다.

우리가 getOrders()에 옵션의 배열을 제공하기 때문에, 우리는 쉽게 ('선택'해야한다 또는 다른 어떤) 추가 하위 개체에 대한 플래그를 포함하는 우리의 기능을 확장 할 수 있습니다

$orders = Order::getOrders(array(
    'items' => true, 
    'tags' => true, 
    'widgets' => true, 
    'limit' => 50, 
    'page' => 1 
)); 

... 당신은 프록시 자식 개체에 해당 옵션이 필요 할 수있는 경우 :

// ... 
// in the `Order::getOrders()` method, when getting items... 
$items = Item::getItemsForOrders($orders, array(
    'tags' => (isset($options['tags']) && $options['tags'] === true) 
)); 

당신에 대해 현명하지 않은 경우 무엇을해야하거나 개체를 가져 오는 경우,이 방법은 유지 관리가 불타고 발굴 될 수 있지만, 경우 수있는 선택적 안 API 유지 간단하고 필요할 때만 최적화하면 정말 잘 작동합니다. 희망이 도움이됩니다.

+0

이것은 내가 찾고 있었던 것입니다! 필자가 제안한대로 수업을 재구성하기 시작했으며, 더 빠른 유연성으로 훨씬 빠른 결과를 포함하도록 개념을 사용할 수있었습니다. 여기서 제안한 것을 사용하여 항목을로드 할 것인지 여부와 같은 옵션을 전달할뿐만 아니라 기본 getOrders() 쿼리를 다양한 방식으로 필터링하는 추가 옵션을 전달합니다. 꽤 정확히 내가 무엇을 찾고 있었는지. 정말 고맙습니다! – user2702162

+0

기꺼이 도와 드리겠습니다! – Divey