查询从相关模型表(CakePHP)加入最新条目

时间:2013-01-07 16:09:26

标签: cakephp cakephp-2.0 cakephp-2.1

我有两张桌子:

订单

id  |  timestamp
--------------------------
1   |  2013-05-01 12:56:33

order_prices

此表存储订单价格的快照。下订单时,第一个条目以原始价格进行。之后,价格可以通过管理系统更改,因此当价格更改时,会记录新价格。

id  |  order_id  |  price   |  timestamp
--------------------------------------------------
1   |  1         |  400.00  |  2013-05-01 12:56:33
1   |  1         |  450.00  |  2013-06-01 18:07:35
1   |  1         |  300.00  |  2013-07-01 17:42:16

模型

class Order extends AppModel {

    public $hasMany = array('OrderPrice');
}


class OrderPrice extends AppModel {

    public $belongsTo = array('Order');
}

所以我的问题是我如何在订单上执行查找,同时拉入当前的'价格(即订单的order_prices表中的最新条目),或原始价格,或任何历史时间点的价格?

我想以最轻松的方式重复使用。我不知道这是否可以通过虚拟字段完成,但这是理想的,因为我可以在需要时提取当前的订单价格。

到目前为止我的解决方案,使用可包容的行为:

$this->Order->find('first', array(
    'contain' => array(
        'OrderPrice' => array(
            'order' => 'OrderPrice.timestamp DESC',
            'limit' => 1
        )

));

然而,这意味着我必须每次都添加,这不是理想的,因为我会获得很多订单价格。我真的可以做到尽可能多地将这些内容放入模型中,所以我不必重复它。

我尝试为OrderPrice模型添加默认订单属性,但它似乎不起作用,并且您无法添加默认的limit属性。

class OrderPrice extends AppModel {

    // Doesn't work from containable
    public $order = 'ObeOrderPrice.timestamp DESC';

2 个答案:

答案 0 :(得分:1)

我认为这不是您执行此操作的具体问题,而是您放置代码的位置的更多问题。 (在我看来,像你一样使用Containable是理想的)

  

“但是,这意味着每次都必须添加,这并不理想   因为我将获得很多订单价格。我真的可以用一种方式   把尽可能多的东西放到模型中,所以我没有   重复一遍。“

这让我觉得你把你的find()放在Controller的多个位置...到你的观点,你应该在模型中有方法可以随时从任何控制器访问......这样的事情:

//OrdersController
public function view($id) {
    $order = $this->Order->getOrder($id);
    $this->set(compact('order'));
}

//Order model
public function getOrder($id) {
    return $this->Order->find('first', array(
        'contain' => array(
            'OrderPrice' => array(
                'order' => 'OrderPrice.timestamp DESC',
                'limit' => 1
            )
    ));
}

这样就可以让你真的不必多次重复你的contain()。您还可以扩展方法以允许为限制,顺序,日期范围等传递不同的参数 - 但您仍然可以在模型中的单个位置上使用该方法。

附注:我经常有两种方法 - 在这种情况下getOrder()getOrders() - 第一种是获得单个订单的非常简单的方法,第二种方法包含允许我使用的所有参数以许多不同的方式,方向,限制,联接等等拉订单。我不确定这是否理想,但它很适合我。

答案 1 :(得分:0)

好的,除非有人有任何其他想法,否则我认为这是最好的解决方案。

我将OrderPrice的可包含选项存储为模型中的变量,这样我就不必在任何时候将当前/原始价格加入订单时重写它们。我可以在任何模型中使用它们。

然后我有一些自定义查找类型,允许我使用我想要的任何参数对订单执行常规查询,同时自动加入自定义/原始价格。

应用/型号/ OrderPrice.php

/**
 * Containable array options for finding current price
 *
 * @var array
 */
public $containCurrent = array(
    'fields' => 'OrderPrice.price',
    'order' => 'OrderPrice.timestamp DESC',
    'limit' => 1
);

/**
 * Containable array options for finding the original price
 *
 * @var array
 */
public $containOriginal = array(
    'fields' => 'OrderPrice.price',
    'order' => 'OrderPrice.timestamp',
    'limit' => 1
);

应用/型号/ Order.php

/**
 * Custom find type which automatically joins the current order price
 *
 * @param  string $state
 * @param  array $query
 * @param  array $results
 * @return array
 * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#creating-custom-find-types
 */
protected function _findJoinCurrentPrice($state, $query, $results = array()) {
    if ($state == 'before') {
        $query['contain']['OrderPrice'] = $this->OrderPrice->containCurrent;
        return $query;
    } else {
        $results = $this->_refinePriceFindResults($query, $results);
    }
    return $results;
}

/**
 * Custom find type which automatically joins the original order price
 *
 * @param  string $state
 * @param  array $query
 * @param  array $results
 * @return array
 * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#creating-custom-find-types
 */
protected function _findJoinOriginalPrice($state, $query, $results = array()) {
    if ($state == 'before') {
        $query['contain']['OrderPrice'] = $this->OrderPrice->containOriginal;
        return $query;
    } else {
        $results = $this->_refinePriceFindResults($query, $results);
    }
    return $results;
}

/**
 * Refine the results of a custom price find
 *
 * If the find returns only one order and/or one price, the data for both
 * models will be returned as element [0] of an otherwise empty array.
 * This method modifies the results array by removing the redundant wrapper
 * array, so that the model data is structured like a normal find('first').
 *
 * @param  array $query The query array from the custom find method
 * @param  array $results The results array from the custom find method
 * @return array The modified results
 */
protected function _refinePriceFindResults($query, $results) {
    if (empty($results)) {
        return false;
    }
    if (isset($query['conditions']['id']) || isset($query['conditions']['Order.id'])) {
        $results = array_shift($results);
    }
    if (isset($results['OrderPrice']['0'])) {
        if (count($results['OrderPrice']) == 1) {
            $results['OrderPrice'] = array_shift($results['OrderPrice']);
        }
    }   
    return $results;
}

<强>控制器

// I can perform a normal find, using any number of complex parameters.

$this->Order->find('joinCurrentPrice', array(
    'fields' => array('Order.type_id'),
    'conditions' => array('Order.id' => $id),
    'contain' => array(
        'Customer' => array(
            'fields' => array('Customer.first_name', 'Customer.last_name')
        )
    )
));


// And the result is the same as the normal find, but the price model is
// automatically joined without any extra work.

array(
    'Order' => array(
        'type_id' => 2,
        'id' => '843654'
    ),
    'Customer' => array(
        'id' => 7348,
        'first_name' => 'Joe',
        'last_name' => 'Bloggs'
    ),
    'OrderPrice' => array(
        'price' => '549.00',
        'order_id' => '843654'
    )
)

我只需要弄清楚如何为历史价格做同样的事情,所以我可以在订单的历史记录中随时加入价格,而不仅仅是第一个或最后一个。