CakeDC搜索插件在HABTM查询中使用复杂条件


CakeDC search plugin using complex conditions in query with HABTM

我两天前写信询问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天的艰苦工作,我成功了。无论如何,谢谢你的帮助。