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';
4

2 回答 2

1

我认为这与您的操作方式无关,而与您放置代码的位置有关。(在我看来,像你一样使用 Containable 是理想的)

“然而,这意味着我必须每次都添加它,这并不理想,因为我会得到很多订单价格。我真的可以用一种方法将尽可能多的这个放入模型中,所以我不必重复。”

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

//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()第一种是获取单个订单的非常简单的方法,第二种是允许我以多种不同方式、方向、限制、连接来拉订单的所有参数。 ..etc 等等。我不确定这是否理想,但它很适合我。

于 2013-01-07T17:19:29.140 回答
0

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

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

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

应用程序/模型/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'
    )
)

我只需要弄清楚如何对历史价格做同样的事情,这样我就可以加入订单历史中任何时间的价格,而不仅仅是第一个或最后一个。

于 2013-01-08T11:10:06.510 回答