这个问题非常开放,所以我将给出一个相当广泛的答案,并尽量不要太过分。首先,我会说像 Doctrine、Propel 或 Symfony 这样的 ORM 解决方案可能是管理关系对象的最理想的解决方案,但并不总是能够快速或干净地实现(学习 ORM 然后转换可能需要一段时间现有代码)。这是我对更轻量级方法的看法。
首先,将数据库查询从类构造函数中取出可能会有所帮助,这样您就可以更好地控制何时访问数据库。一种策略是将静态方法添加到您的类中以获取结果。此外,您可以提供“预取”子对象的选项,以便您可以批量执行查询。因此,进入您的示例,外部 API 将如下所示:
$orders = Order::getOrders(array(
'items' => true
));
这里的想法是您希望使用该getOrders()
方法获取一个订单数组并告诉getOrders()
同时获取item
子对象。在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
// ...
}
}
现在,如果您知道在收到订单时需要物品,请传入 atrue
选项'items'
:
$orders = Order::getOrders(array(
'items' => true
));
或者,如果您不需要项目,请不要指定任何内容:
$orders = Order::getOrders();
无论哪种方式,当您遍历订单时,访问商品的 API 都是相同的:
// 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();
}
如您所见,提供该'items'
选项可以更有效地使用数据库,但如果您只是需要orders
而不乱搞items
,您也可以这样做。
并且因为我们为 提供了一系列选项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 简单并且只在需要时进行优化,它就可以很好地工作。希望这可以帮助。