WEBOPIXEL

CakePHPで検索機能を実装できる「CakeDC search plugin」を使ってみた

Posted: 2011.03.17 / Category: CakePHP / Tag: 

「CakeDC search plugin」はCakePHPで簡単に検索機能を実装できるプラグインです。
シンプルに使用する分には本当に簡単ですが、一つのinputから複数のモデルとか、HABTMの検索とかしてえなとか考えてたら、僕程度のレベルではやはりというか、いつものようにはまってしまったのでメモしておきます。
CakePHPのバージョンは1.3です。

Sponsored Link

「CakeDC search plugin」をインストール

プラグインをGithubからダウンロードします。

CakeDC / search

インストールは「app」→「plugin」に「search」というフォルダ名で入れておきます。

データベース

データベースはこんな感じです。

Posts

id bigint(20)
title varchar(100)
text varchar(255)

Tags

id bigint(20)
title varchar(100)

PostsTags

post_id bigint(20)
tag_id varchar(100)

とりあえず一つのFieldから検索する。

とりあえず一つのFieldから検索してみます。これはとても簡単。
ここではPostモデルのtitleフィールドを検索してみます。

[Model] post.php

	class Post extends AppModel {
		public $name = 'Post';
		public $actsAs = array('Search.Searchable');
		public $filterArgs = array(
	    	array('name' => 'title', 'type' => 'like'),
	    );
	}
	

[Controller] posts_controller.php

	class PostsController extends AppController {
		public $name = 'posts';
		public $components = array('Search.Prg');
		public $presetVars = array(
	    	array('field' => 'title', 'type' => 'value'),
	    );
	    function index() {
			$this->Prg->commonProcess();
			$this->paginate['conditions'] = $this->Post->parseCriteria($this->passedArgs);
			$this->set("result", $this->paginate());
	    }
	}
	

[View] posts_controller.php

	echo $this->Form->create('Post', array(
	    'url' => array_merge(array('action' => 'index'), $this->params['pass'])
	));
	echo $this->Form->input('title', array('div' => false));
	echo $this->Form->submit('検索');
	echo $this->Form->end();
	

「title」を検索させるフィールドに変更してください。
これだけでいけるはずです。

一つのinput(テキストボックス)から二つのFieldから検索する。

たとえばブログのサイト内検索だったらタイトルだけじゃなくて記事テキストも検索したいと思うんですよ。
ここではサンプルデータベースの例からPostモデルの「title」と「text」フィールドを検索してみます。

[Model] post.php

	class Post extends AppModel {
		public $name = 'Post';
		public $actsAs = array('Search.Searchable');
		public $filterArgs = array(
	    	array('name' => 'title', 'type' => 'query', 'method' => 'orConditions'),
	    );
	    public function orConditions($data = array()) {
	        $filter = $data['title'];
	        $cond = array(
	            'OR' => array(
	                $this->alias . '.title LIKE' => '%' . $filter . '%',
	                $this->alias . '.text LIKE' => '%' . $filter . '%',
	            ));
	        return $cond;
	    }
	}
	

独自な処理をする場合は「$filterArgs」のtypeを「query」にしてメソッドを記述します。
検索するフィールドを追加する場合は「orConditions」の「OR」に追加していきます。

input(テキストボックス)からHABTMを検索する。

次はHABTMです。
検索したテキストをタグにもヒットさせたいなと。

モデルの「$hasAndBelongsToMany」は規則通りにしてればある程度省略できますが、省略してしまうとサーチプラグインでは問題があったりするみたいなので、ある程度ちゃんとに書きます。
「with」とかがポイントらしいです。

[Model] post.php

	public $hasAndBelongsToMany = array(
	    'Tag' => array(
		'className' => 'Tag',
		'joinTable' => 'posts_tags',
		'foreignKey' => 'post_id',
		'associationForeignKey' => 'tag_id',
		'with' => 'PostsTag',
		'unique' => true
	    ),
	);
	

[Model] posts_tag.php

	public $belongsTo = array(
	    'Tag' => array(
	        'className' => 'Tag',
	        'foreignKey' => 'tag_id',
	        'conditions' => '',
	        'fields' => '',
	        'order' => ''
	    ),
	    'Post' => array(
	        'className' => 'Post',
	        'foreignKey' => 'post_id',
	        'conditions' => '',
	        'fields' => '',
	        'order' => ''
	    )
	);
	

HABTMの場合は「$filterArgs」のtypeを「subquery」にするらしいです。メソッドも変更します。

[Model] post.php

	public $filterArgs = array(
    	array('name' => 'title', 'type' => 'subquery', 'method' => 'findByTags', 'field' => 'Post.id'),
    );
    public function findByTags($data = array()) {
        $this->PostsTag->Behaviors->attach('Containable', array('autoFields' => false));
        $this->PostsTag->Behaviors->attach('Search.Searchable');
        $query = $this->PostsTag->getQuery('all',array(
	    	'conditions' => array(
	        	'Tag.title' => $data['title']
	        ),
	        'fields' => array('post_id'),
	        'contain' => array('Tag')
		));
        return $query;
    }
	

これでTagモデルの「title」フィールドが検索されると思います。

一つのinput(テキストボックス)から複数のFieldとHABTMも検索する。

もうここまできたらHABTMだけといわずPost側のFieldも一緒に検索したいなと。
単純に考えれば上記のサンプルを合体させればいけそうな気がするんですが。

[Model] post.php

	public function findByTags($data = array()) {
        $this->PostsTag->Behaviors->attach('Containable', array('autoFields' => false));
        $this->PostsTag->Behaviors->attach('Search.Searchable');
        $filter = $data['title'];
        $query = $this->PostsTag->getQuery('all',array(
	    	'conditions' => array(
	        	'or' => array(
	    			array('Tag.title' => $filter),
	    			$this->alias . '.title LIKE' => '%' . $filter . '%',
               		$this->alias . '.text LIKE' => '%' . $filter . '%',
	    		)
	        ),
	        
	        'fields' => array('post_id'),
	        'contain' => array('Tag')
		));
        return $query;
    }
	

「or」にlikeを追加していくだけでした。
結果的にほぼGithubのサンプルでできたので、どこではまる必要があったんだという感じですが、、、、。

参考サイト
Search Plugin for CakePHPで簡単検索機能実装
CakePHP の Search Plugin で HABTM なモデルを検索する
CakeDCのsearch pluginの記事が少ないので1個置いときますね。