我已经忙着使用cakePHP框架几个月了,我真的很喜欢它。目前我正在开发一个非常新的项目,它可以完成它应该做的工作(我想......)但我对我编写的一些代码感到不舒服。事实上,我应该优化我的分页条件查询,以便立即获得正确的结果(现在我通过一堆Set :: extract方法调用来操作结果集。
我将草拟应用程序的相关方面。我有一个模型'Site'与模型'SiteMeta'有一个hasMany关系。最后一个表格如下:id,site_id,key,value,created。
在最后一个模型中,我在不同时期记录了几个站点的值。我要存储的密钥的名称(例如alexarank,google pagerank,...),当然还有价值。在给定的时间间隔,我让我的应用程序更新此数据库,以便我可以跟踪此值的演变。
现在我的问题是这个。
在各个网站的概述页面上(controller => Sites,action => index)我想展示网站的CURRENT pagerank。因此,我需要一个精确的SiteMeta记录,其中'created'字段是最高的,'key'中的值应该与'pagerank'一词匹配。我已经尝试了几个我在网上阅读但却没有工作的东西(可包含,绑定模型等)。可能我做错了什么。
现在,当我执行$ this-> paginate
时,我得到这样的结果Array
(
[0] => Array
(
[Site] => Array
(
[id] => 1
[parent_id] => 0
[title] => test
[url] => http://www.test.com
[slug] => www_test_com
[keywords] => cpc,seo
[language_id] => 1
)
[SiteMeta] => Array
(
[0] => Array
(
[id] => 1
[site_id] => 1
[key] => pagerank
[value] => 5
[created] => 2010-08-03 00:00:00
)
[1] => Array
(
[id] => 2
[site_id] => 1
[key] => pagerank
[value] => 2
[created] => 2010-08-17 00:00:00
)
[2] => Array
(
[id] => 5
[site_id] => 1
[key] => alexa
[value] => 1900000
[created] => 2010-08-10 17:39:06
)
)
)
要获取pagerank,我只需遍历所有站点并操作我得到的这个数组。接下来,我使用Set :: extract过滤结果。但这感觉不太对劲:)
$sitesToCheck = $this->paginate($this->_searchConditions($this->params));
foreach($sitesToCheck as $site) {
$pagerank = $this->_getPageRank($site['Site']);
$alexa = $this->_getAlexa($site['Site']);
$site['Site']['pagerank'] = $pagerank;
$sites[] = $site;
}
if (isset($this->params['named']['gpr']) && $this->params['named']['gpr']) {
$rank = explode('-', $this->params['named']['gpr']);
$min = $rank[0];$max = $rank[1];
$sites = Set::extract('/Site[pagerank<=' . $max . '][pagerank>=' . $min .']', $sites);
}
$this->set(compact('sites', 'direction'));
你能帮助我考虑解决方案吗?提前谢谢。
感谢您的贡献。我尝试了这些选项(也是使用bindmodel但也没有工作的东西),但仍然无法让它像它应该的那样工作。如果我定义了这个
$this->paginate = array(
'joins'=> array(
array(
'table'=>'site_metas',
'alias'=>'SiteMeta',
'type' =>'inner',
'conditions' =>array('Site.id = SiteMeta.site_id')
)
),
);
我得到重复的结果 我有一个网站有3个不同的SiteMeta记录和一个有2个不同记录的网站。 paginate方法总共返回5条记录。这可能是一个简单的解决方案,但我无法弄明白:)
我也尝试自己编写一个sql查询,但在这种情况下我似乎无法使用分页魔法。查询我想模仿分页选项和条件如下。查询完全按照我想要的方式返回。
$sites = $this->Site->query('SELECT * FROM sites Site, site_metas SiteMeta WHERE SiteMeta.id = (select SiteMeta.id from site_metas SiteMeta WHERE Site.id = SiteMeta.site_id AND SiteMeta.key = \'pagerank\' order by created desc limit 0,1 )');
答案 0 :(得分:2)
当您尝试检索hasMany关系中的数据时,默认情况下,cakephp不会加入表。如果您选择joins
,您可以执行以下操作:
$this->paginate = array(
'joins'=>array(
array(
'table'=>'accounts',
'alias'=>'Account',
'type' =>'inner',
'conditions' =>array('User.id = Account.user_id')
)
),
'conditions'=> array('OR' =>
array(
'Account.name'=>$this->params['named']['nickname'],
'User.id' => 5)
)
);
$users = $this->paginate();
$this->set('users',$users);
debug($users);
$this->render('/users/index');
你必须根据自己的需要来配合这个。关于加入的More,就像在另一个回答中已经提到的那样。
编辑1:这是因为您缺少第二个“条件”。请参阅我的代码段。第一个'条件'只表示连接发生的位置,而第二个'条件'表示实际选择。
编辑2: Here有关如何编写条件以便选择所需数据的一些信息。您可能希望在精炼条件下使用列max
上的rdbms的created
函数。
编辑3:不能同时使用包含和连接。引自手册:使用包含Containable行为的连接可能会导致一些SQL错误(重复的表),因此如果您的主要目标是基于相关数据执行搜索,则需要使用连接方法作为Containable的替代方法。 Containable最适合限制find语句带来的相关数据量。我想你还没有尝试过我的编辑。
编辑4 :一种可能的解决方案是向表last_updated
添加字段Sites
。然后可以在第二个条件语句中使用此字段与SiteMeta.created
值进行比较。
答案 1 :(得分:0)
尝试这样的事情:
$this->paginate = array(
'fields'=>array(
'Site.*',
'SiteMeta.*',
'MAX(SiteMeta.created) as last_date'
),
'group' => 'SiteMeta.key'
'conditions' => array(
'SiteMeta.key' => 'pagerank'
)
);
$data = $this->paginate('Site');
或者这个:
$conditions = array(
'recursive' => 1,
'fields'=>array(
'Site.*',
'SiteMeta.*',
'MAX(SiteMeta.created) as last_date'
),
'group' => 'SiteMeta.key'
'conditions' => array(
'SiteMeta.key' => 'pagerank'
)
);
$data = $this->Site->find('all', $conditions);
答案 2 :(得分:0)
尝试这样的事情(在模型上设置可容纳的内容):
$this->Site->recursive = -1;
$this->paginate = array(
'conditions' => array(
'Site.title' => 'title') //or whatever conditions you want... if any
'contain' => array(
'SiteMeta' => array(
'conditions' => array(
'SiteMeta.key' => 'pagerank'),
'limit' => 1,
'order' => 'SiteMeta.created DESC')));
我使用可包含的东西,实际上我在app_model文件中有这个,所以它适用于所有模型:
var $actsAs = array('Containable');
答案 3 :(得分:0)
许多人认为所有能帮助我完成这一切的人:) 毕竟,我把它弄好了。
最终这对我来说是个伎俩
$this->paginate = array(
'joins'=> array(
array(
'table'=>'site_metas',
'alias'=>'SiteMeta',
'type' =>'inner',
'conditions' => array('Site.id = SiteMeta.site_id'))
),
'group' => 'Site.id',
'contain' => array(
'SiteMeta' => array(
'conditions' => array(
'SiteMeta.key' => 'pagerank'),
'limit' => 1,
'order' => SiteMeta.created DESC',
)));
$sites = $this->paginate();