我有两张桌子:
订单
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';
答案 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'
)
)
我只需要弄清楚如何为历史价格做同样的事情,所以我可以在订单的历史记录中随时加入价格,而不仅仅是第一个或最后一个。