我两天前写信询问andConditions,似乎我不理解这个想法,但事实是,两天来,我一直在使用CakeDC:进行下一步
如何在CakeDC搜索插件的"查询"方法中实现复杂的HABTM条件?
我有Offer HABTM功能(表:offers,features,features_offers),当在控制器中使用时,以下功能很好:
debug($this->Offer->find('all', array('contain' => array(
'Feature' => array(
'conditions' => array(
'Feature.id in (8, 10)',
)
)
)
)
)
);
当我想在搜索中使用相同的条件时,问题就来了:
public $filterArgs = array(
array('name' => 'feature_id', 'type' => 'query', 'method' => 'findByFeatures'),
);
........
public function findByFeatures($data = array()) {
$conditions = '';
$featureID = $data['feature_id'];
if (isset($data['feature_id'])) {
$conditions = array('contain' => array(
'Feature' => array(
'conditions' => array(
'Feature.id' => $data['feature_id'],
)
)
)
);
}
return $conditions;
}
我得到一个错误:
错误:SQLSTATE[42S22]:找不到列:1054未知列"where clause"中的"contain"
这让我认为我根本无法执行此搜索和/或在搜索中使用可包含的行为。
如果我遗漏了什么,请在该领域更有经验的人告诉我,或者告诉我在哪里可以找到解决方案——也许是食谱中的一部分?
编辑:也尝试了连接。这在控制器中运行得非常好,返回我需要的所有数据:
$options['joins'] = array(
array('table' => 'features_offers',
'alias' => 'FeaturesOffers',
'type' => 'inner',
'conditions' => array(
'Offer.id = FeaturesOffers.offer_id'
),
array('table' => 'features',
'alias' => 'F',
'type' => 'inner',
'conditions' => array(
'F.id = FeaturesOffers.feature_id'
),
)
),
);
$options['conditions'] = array(
'feature_id in (13)' //. $data['feature_id']
);
debug($this->Offer->find('all', $options));
当我尝试放入搜索方法时,我只在SQL 的where子句中得到返回的条件
WHERE(((joins=(Array))AND(conditions=('feature_id in Array'))
导致错误:
SQLSTATE[42S22]:找不到列:1054未知列"joins"在"where子句"中
编辑:也许我很愚蠢,很抱歉这么说,但插件的文档太糟糕了。
我检查了两次、三次和四次(顺便说一句,在搜索表单facepalm的1个文件上已经损失了30个小时),文档中愚蠢的findByTags对我来说仍然没有任何意义。
public function findByTags($data = array()) {
$this->Tagged->Behaviors->attach('Containable', array('autoFields' => false));
$this->Tagged->Behaviors->attach('Search.Searchable');
$query = $this->Tagged->getQuery('all', array(
'conditions' => array('Tag.name' => $data['tags']),
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
}
据我所知,
$this->Tagged
应该是HABTM关联的模型的名称。
不过,这与cakeHP的标准相去甚远:http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
这里描述的方式是,你不需要另一个模型,而是将配方与配料联系起来,如下所示:
class Recipe extends AppModel {
public $hasAndBelongsToMany = array(
'Ingredient' =>
array(
'className' => 'Ingredient',
'joinTable' => 'ingredients_recipes',
'foreignKey' => 'recipe_id',
'associationForeignKey' => 'ingredient_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
这意味着您可以从Recipe访问HABTM关联表数据,而无需定义模型"IngredientRecipe"。
根据cakeDC文档,您需要的模型是IngredientRecipe,而在cakeHP文档中,这并不是强制性的。即使创建了这个模型,HABTM assoc也不能正常工作——我也尝试过。
现在我需要用我的方式重新编写搜索功能,只使用cakeHP,尽管我已经花了30个小时……太不开心了。:(
每次我在项目中做这件事时,我总是花几个小时来研究如何使用CakeDC搜索行为来完成,所以我写这篇文章是为了尝试用简单的语言提醒自己需要做什么。我还注意到,尽管使用带有相关模型的CakeDC搜索插件,这通常是正确的,但没有任何解释,这使得将其修改为自己的项目变得更加困难。
当您有一个"拥有并属于多个"关系,并且您想要搜索联接表,即其中有两个字段的表,该表以多对多关系将其两侧的表联接在一起时,您想要创建一个子查询,该子查询包含关系中某个表的ID列表。将检查关系另一侧表中的ID,看看它们是否在该记录中,如果在,则将选择主表中的记录。
在以下示例中,
SELECT Handover.id, Handover.title, Handover.description
FROM handovers AS Handover
WHERE Handover.id in
(SELECT ArosHandover.handover_id
FROM aros_handovers AS ArosHandover
WHERE ArosHandover.aro_id IN (3) AND ArosHandover.deleted != '1')
LIMIT 20
来自ArosHandover的所有记录都将被选择,如果它们具有3的aro_id。
关于如何使用CakeDC搜索行为做到这一点。
首先,将字段放入搜索表单中:
echo $this->Form->create('Handover', array('class' => 'form-horizontal'));?>
echo $this->Form->input('aro_id', array('options' => $roles, 'multiple' => true, 'label' => __('For', true), 'div' => false, true));
等等。。。
注意,我没有将表单元素放置在ArosHandover数据空间中;另一种说法是,当发送表单请求时,字段aro_id将被放置在名为Handover的数组下。
在变量$filterArgs:下的模型中
'aro_id' => array('name' => 'aro_id', 'type' => 'subquery', 'method' => 'findByAros', 'field' => 'Handover.id')
注意,类型是"subquery",正如我上面提到的,您需要创建一个子查询,以便能够找到合适的记录,通过将类型设置为subquery,您告诉CakeDC创建SQL的子查询片段。方法是要在其下编写代码的函数名。字段元素是将出现在上面的示例查询的这一部分中的字段的名称
WHERE Handover.id in
然后编写返回子查询的函数:
function findByAros($data = array())
{
$ids = ''; //you need to make a comma separated list of the aro_ids that are going to be checked
foreach($data['aro_id'] as $k => $v)
{
$ids .= $v . ', ';
}
if($ids != '')
{
$ids = rtrim($ids, ', ');
}
//you only need to have these two lines in if you have not already attached the behaviours in the ArosHandover model file
$this->ArosHandover->Behaviors->attach('Containable', array('autoFields' => false));
$this->ArosHandover->Behaviors->attach('Search.Searchable');
$query = $this->ArosHandover->getQuery('all',
array(
'conditions' => array('ArosHandover.aro_id IN (' . $ids . ')'),
'fields' => array('handover_id'), //the other field that you need to check against, it's the other side of the many-to-many relationship
'contain' => false //place this in if you just want to have the ArosHandover table data included
)
);
return $query;
}
在移交控制器中:
public $components = array('Search.Prg', 'Paginator'); //you can also place this into AppController
public $presetVars = true; //using $filterArgs in the model configuration
public $paginate = array(); //declare this so that you can change it
// this is the snippet of the search form processing
public function admin_find()
{
$this->set('title_for_layout','Find handovers');
$this->Prg->commonProcess();
if(isset($this->passedArgs) && !empty($this->passedArgs))
{//the following line passes the conditions into the Paginator component
$this->Paginator->settings = array('conditions' => $this->Handover->parseCriteria($this->passedArgs));
$handovers = $this->Paginator->paginate(); // this gets the data
$this->set('handovers', $handovers); // this passes it to the template
如果你想进一步解释我为什么这么做,请询问,如果我收到一封电子邮件告诉我你已经问过了,如果我能的话,我会给出答案。
这不是插件的问题,而是你如何建立关联的问题。您需要正确地将它们连接起来,以便在这三个表中进行搜索。检查CakePHP默认情况下是如何从HABTM assocs获取数据的。
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-表格
假设一本书有AndBelongsToMany标签关联。此关系使用books_tags表作为联接表,因此您需要联接books表到books_tags表,这与标签表:
$options['joins'] = array(
array('table' => 'books_tags',
'alias' => 'BooksTag',
'type' => 'inner',
'conditions' => array(
'Books.id = BooksTag.books_id'
)
),
array('table' => 'tags',
'alias' => 'Tag',
'type' => 'inner',
'conditions' => array(
'BooksTag.tag_id = Tag.id'
)
)
);
$options['conditions'] = array(
'Tag.tag' => 'Novel'
);
$books=$Book->find(全部,$options);使用联接可以CakePHP处理关联和获取的最大灵活性但是,在大多数情况下,您可以使用其他工具来实现相同的结果,例如正确定义关联、绑定模型在飞行中并使用可控制行为。此功能应小心使用,因为在少数情况下,它可能会导致变形SQL查询(如果与前面描述的任何技术结合使用)用于关联模型。
此外,您的代码在某个地方出错。
Column not found: 1054 Unknown column 'contain' in 'where clause'
这意味着以某种方式调用了$Model->contain()。我在粘贴到这里的代码中没有看到这样的调用,所以它一定在其他地方。如果找不到模型方法,则通常会在字段名为列时发生此错误。
我想与大家分享,使用插件进行HABTM搜索的解决方案就在这里:将CakeDC搜索插件与相关型号一起使用
@burzum,文档还远远不够好。你注意到"type"=>"checkbox"的使用了吗?而且在任何地方都没有提到它是一种类型?更不用说完全缺乏语法,有很多拼写错误和介词缺失了。我花了两天的时间才弄清楚作者的想法,并把单词装订在里面。对此不予置评。
我很高兴经过5天的艰苦工作,我成功了。无论如何,谢谢你的帮助。