Можно ли изменить оператор условий по умолчанию с OR на AND в расширении Taggable?

$tagged = Os :: model()-> withTags("windows, windows7, windowsXp")-> find();

Я хочу получить записи, помеченные любым из следующих windows, windows7, windowsXp.

По умолчанию теги генерируют условие, которое объединяется по И. Я хочу использовать оператор OR для тегов. Поэтому, если запись содержит окна, windows7, но не windowsXp, она не будет получена.

Мне удалось найти обходной путь, отредактировав getFindByTagsCriteria() в ETaggableBehavior.php, который находится в папке расширения.

/**
     * Get criteria to limit query by tags.
     * @access private
     * @param array $tags
     * @return CDbCriteria
     */
    protected function getFindByTagsCriteria($tags) {
            $criteria = new CDbCriteria();

            $pk = $this->getOwner()->tableSchema->primaryKey;

            if(!empty($tags)){
                    $conn = $this->getConnection();
                    $criteria->select = 't.*';

                    if(count($tags) >0){
                            $criteria -> join .= "
                                    JOIN {$this->getTagBindingTableName()} bt 
                                    ON t.{$pk} = bt.{$this->getModelTableFkName()}

                                    JOIN {$this->tagTable} tag0 
                                    ON tag0.{$this->tagTablePk} = bt.{$this->tagBindingTableTagId} AND (";


                            for($i = 0, $count = count($tags); $i < $count; $i++){
                                    $tag = $conn->quoteValue($tags[$i]);
                                    $criteria->join .= " tag0.`{$this->tagTableName}` = $tag OR";
                            }
                            $criteria -> join = rtrim($criteria -> join, "OR");
                            $criteria -> join .= ")";
                    }
            }

            if($this->getScopeCriteria()){
                    $criteria->mergeWith($this->getScopeCriteria());
            }

            return $criteria;
    }

Я был бы очень признателен за любой другой способ без необходимости изменять сам плагин.


person Keeper Hood    schedule 01.10.2012    source источник


Ответы (2)


Здесь я бы установил метод withTags() в вашей модели, чтобы он принимал значение массива, например, что-то вроде этого:

/**
 * @param array $tags List of tags to search for
 * @return named scope
 */
public function withTags($tags)
{
    $condition = '1';
    $params = array();
    foreach($tags as $key=>$value)
    {
        $condition.=' OR tag = :tag'.$key;
        $params[':tag'.$key] = $value;
    }
    $this->getDbCriteria()->mergeWith(array(
        'condition'=>$condition,
        'params'=>$params,
    ));

    return $this;
}

Таким образом, вы должны иметь возможность вызывать свою именованную область следующим образом:

$tags = array('windows', 'windows7', 'windowsXp'),
$tagged = Os::model()->withTags($tags)->findAll();
person Stu    schedule 02.10.2012
comment
Я обновил свой вопрос. Аналогично вашему ответу я изменил основной файл плагина. Проблема в том, что я понятия не имею, какие побочные эффекты это может вызвать. Он строит запрос из JOIN и не использует поиск тегов через WHERE (условие ИЛИ/И). Пожалуйста, посмотрите github.com/yiiext/taggable-behavior/blob/master/ - person Keeper Hood; 02.10.2012
comment
Похоже, вы можете удалить ` AND tag$i.{$this->tagTableName} = $tag` в конце оператора соединения в строке 511 и вставить его в свой собственный оператор «условия» с помощью OR вместо AND? Может быть, нужно немного больше возиться с этим? - person Stu; 02.10.2012

Сэм из команды разработчиков Yii помог мне решить эту проблему, добавив еще две функции в файл ETaggableBehavior.php.

/**
 * Get criteria to limit query to match any of tags specified
 * @access private
 * @param array $tags
 * @return CDbCriteria
 */
protected function getFindByAnyTagsCriteria($tags) {
    $criteria = new CDbCriteria();

    $pk = $this->getOwner()->tableSchema->primaryKey;

    if(!empty($tags)){
        $conn = $this->getConnection();
        foreach($tags as &$tag) {
            $tag = $conn->quoteValue($tag);
        }
        unset($tag);
        $tags = implode(', ', $tags);

        $criteria->select = 't.*';
        $criteria->join .=
            "JOIN {$this->getTagBindingTableName()} bt ON t.{$pk} = bt.{$this->getModelTableFkName()}
            JOIN {$this->tagTable} tag ON tag.{$this->tagTablePk} = bt.{$this->tagBindingTableTagId} AND tag.`{$this->tagTableName}` IN ($tags)";
        }
    }

    if($this->getScopeCriteria()){
        $criteria->mergeWith($this->getScopeCriteria());
    }

    return $criteria;
}

/**
 * Limit current AR query to have any of tags specified.
 * @param string|array $tags
 * @return CActiveRecord
 */
public function taggedWithAnyOf($tags) {
    $tags = $this->toTagsArray($tags);

    if(!empty($tags)){
        $criteria = $this->getFindByAnyTagsCriteria($tags);
        $this->getOwner()->getDbCriteria()->mergeWith($criteria);
    }

    return $this->getOwner();
}
person Keeper Hood    schedule 09.02.2013