2011年08月03日

cakephpsearchplugin の or 検索 と likeor 検索

お願いします♪いいねとかつぶやいたりして下さい
 
2011/08/05 追記

年齢検索追加

□10-19
■20-29

とすると 10代、 20代のユーザーを絞りこめる。

○コントローラー
array('field' => 'birthday', 'type' => 'checkbox'),


○ビュー
年齢で絞り込み
<?=$form->input('birthday',array('type' => 'select','multiple' => 'checkbox',  'options' => array('10-19' => '10代','20-29' => '20代','30-39' => '30代')));?><br />


○モデル
array('name' => 'birthday', 'type' => 'age'),



こんな感じ。


likeor検索も追加。
_addLikeOr

チェックボックスで

■つけ麺
■ラーメン
□ぼくイケメン

とあり、指定したフィールドから %つけ麺% and &ラーメン& 検索をする。

○モデル
array('name' => 'fortunes', 'type' => 'likeor'),


をやればよい。
で、コンとローラーで checkbox を指定。


or検索できんがね
やり方わからんだけなのか?

よくある
チェックボックスを 2つ3つ選択した場合、そのデータのどれかを持っている物を表示
ってやりたいんだけど。

で・・・改造しました。

改造した箇所は
・_addOrValue メソッドの追加
・parseCriteria をちょいいじり。

改造版 cakephp searchplugin の searchble.php ビヘイビア
<?php
/**
 * CakePHP Tags Plugin
 *
 * Copyright 2009 - 2010, Cake Development Corporation
 *                        1785 E. Sahara Avenue, Suite 490-423
 *                        Las Vegas, Nevada 89104
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright 2009 - 2010, Cake Development Corporation (http://cakedc.com)
 * @link      http://github.com/CakeDC/Search
 * @package   plugins.search
 * @license   MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

/**
 * Searchable behavior
 *
 * @package		plugins.search
 * @subpackage	plugins.search.models.behaviors
 */

class SearchableBehavior extends ModelBehavior {
/**
 * settings indexed by model name.
 *
 * @var array
 * @access public
 */
	public $settings = array();

/**
 * Default settings
 *
 * @var string
 **/
	protected $_defaults = array();

/**
 * Configuration of model
 *
 * @param AppModel $model
 * @param array $config
 */
	public function setup(Model $model, $config = array()) {
		$this->settings[$model->alias] = array_merge($this->_defaults, $config);
	}

/**
 * parseCriteria
 * parses the GET data and returns the conditions for the find('all')/paginate
 * we are just going to test if the params are legit
 *
 * @param array $data Criteria of key->value pairs from post/named parameters
 * @return array Array of conditions that express the conditions needed for the search.
 * @access public
 */
	public function parseCriteria(Model $model, $data) {
		$conditions = array();
		foreach ($model->filterArgs as $field) {
			if (in_array($field['type'], array('string', 'like'))) {
				$this->_addCondLike($model, $conditions, $data, $field);
			} elseif (in_array($field['type'], array('int', 'value'))) {
				$this->_addCondValue($model, $conditions, $data, $field);
			} elseif ($field['type'] == 'expression') {
				$this->_addCondExpression($model, $conditions, $data, $field);
			} elseif ($field['type'] == 'query') {
				$this->_addCondQuery($model, $conditions, $data, $field);
			} elseif ($field['type'] == 'subquery') {
				$this->_addCondSubquery($model, $conditions, $data, $field);
			} elseif ($field['type'] == 'or') {
				$this->_addOrValue($model, $conditions, $data, $field);
			} elseif ($field['type'] == 'likeor') {
				$this->_addLikeOr($model, $conditions, $data, $field);
			} elseif ($field['type'] == 'age') {
				$this->_addAgeBetween($model, $conditions, $data, $field);
			}
		}
		return $conditions;
	}

	protected function _addAgeBetween(Model $model, &$conditions, $data, $field) {
		
		if(!empty($data[$field['name']]) && preg_match('/\|/',$data[$field['name']])){
			$data[$field['name']] = explode('|',$data[$field['name']]);
		}
		
		
		if(!empty($data[$field['name']]) && !is_array($data[$field['name']])){
			$tmp = $data[$field['name']];
			$data[$field['name']] = array();
			$data[$field['name']][] = $tmp;
		}
		
		
		if(!empty($data[$field['name']])){
			$range = array();
			
			foreach($data[$field['name']] as $v){
				$age = explode('-',$v);
				$range[] = $this->_getBirthdayRange($age[0],$age[1]);
			}
		}
		
		$fieldName = $field['name'];
		if (isset($field['field'])) {
			$fieldName = $field['field'];
		}
		if (strpos($fieldName, '.') === false) {
			$fieldName = $model->alias . '.' . $fieldName;
		}
		if (!empty($data[$field['name']])) {
			
			$conditions['or'] = '';
			
			foreach($range as $key => $v){
				$conditions['or'][$key][$fieldName . " >="] = $v[0];
				$conditions['or'][$key][$fieldName . " <="] = $v[1];
			}
			
		}
		
		return $conditions;
	}
	
	//年齢の範囲を取得する
	function _getBirthdayRange($before_age,$after_age = '')
	{
		if(!empty($after_age)){
			$age = $after_age;
		} else {
			$age = $before_age;	
		}
		
		$start = mktime(0, 0, 0, date('m'), date('d') + 1, date('Y') - $age - 1);
		
		
		
		
		$end = mktime(0, 0, 0, date('m'), date('d'), date('Y') - $before_age);
	
		return array(date('Y-m-d', $start), date('Y-m-d', $end));
	}
	


/**
 * Validate search
 *
 * @param object Model
 * @return boolean always true
 * @access public
 */
	public function validateSearch(Model $model, $data = null) {
		if (!empty($data)) {
			$model->set($data);
		}
		$keys = array_keys($model->data[$model->alias]);
		foreach ($keys as $key) {
			if (empty($model->data[$model->alias][$key])) {
				unset($model->data[$model->alias][$key]);
			}
		}
		return true;
	}

/**
 * filter retrieving variables only that present in  Model::filterArgs
 *
 * @param object Model
 * @param array $vars
 * @return array, filtered args
 * @access public
 */
	public function passedArgs(Model $model, $vars) {
		$result = array();
		foreach ($vars as $var => $val) {
			if (in_array($var, Set::extract($model->filterArgs, '{n}.name'))) {
				$result[$var] = $val;
			}
		}
		return $result;
	}

/**
 * Method to generated DML SQL queries using find* style.
 *
 * Specifying 'fields' for new-notation 'list':
 *  - If no fields are specified, then 'id' is used for key and Model::$displayField is used for value.
 *  - If a single field is specified, 'id' is used for key and specified field is used for value.
 *  - If three fields are specified, they are used (in order) for key, value and group.
 *  - Otherwise, first and second fields are used for key and value.
 *
 * @param array $conditions SQL conditions array, or type of find operation (all / first / count / neighbors / list / threaded)
 * @param mixed $fields Either a single string of a field name, or an array of field names, or options for matching
 * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
 * @param integer $recursive The number of levels deep to fetch associated records
 * @return string SQL query string.
 * @access public
 * @link http://book.cakephp.org/view/449/find
 */
	public function getQuery(Model $model, $conditions = null, $fields = array(), $order = null, $recursive = null) {
		if (!is_string($conditions) || (is_string($conditions) && !array_key_exists($conditions, $model->_findMethods))) {
			$type = 'first';
			$query = compact('conditions', 'fields', 'order', 'recursive');
		} else {
			list($type, $query) = array($conditions, $fields);
		}

		$db =& ConnectionManager::getDataSource($model->useDbConfig);
		$model->findQueryType = $type;
		$model->id = $model->getID();

		$query = array_merge(
			array(
				'conditions' => null, 'fields' => null, 'joins' => array(), 
				'limit' => null, 'offset' => null, 'order' => null, 'page' => null, 
				'group' => null, 'callbacks' => true
			),
			(array)$query
		);

		if ($type != 'all') {
			if ($model->_findMethods[$type] === true) {
				$query = $model->{'_find' . ucfirst($type)}('before', $query);
			}
		}

		if (!is_numeric($query['page']) || intval($query['page']) < 1) {
			$query['page'] = 1;
		}
		if ($query['page'] > 1 && !empty($query['limit'])) {
			$query['offset'] = ($query['page'] - 1) * $query['limit'];
		}
		if ($query['order'] === null && $model->order !== null) {
			$query['order'] = $model->order;
		}
		$query['order'] = array($query['order']);


		if ($query['callbacks'] === true || $query['callbacks'] === 'before') {
			$return = $model->Behaviors->trigger($model, 'beforeFind', array($query), array(
				'break' => true, 'breakOn' => false, 'modParams' => true
			));
			$query = (is_array($return)) ? $return : $query;

			if ($return === false) {
				return null;
			}

			$return = $model->beforeFind($query);
			$query = (is_array($return)) ? $return : $query;

			if ($return === false) {
				return null;
			}
		}
		return $this->__queryGet($model, $query, $recursive);
	}

/**
 * Clear all associations
 *
 * @param AppModel $model
 * @param bool $reset
 */
	public function unbindAllModels(Model $model, $reset = false) {
		$assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
		$unbind = array();
		foreach ($assocs as $assoc) {
		  $unbind[$assoc] = array_keys($model->{$assoc});
		}
		$model->unbindModel($unbind, $reset);
	}

/**
 * Add Conditions based on fuzzy comparrison
 *
 * @param AppModel $model Reference to the model
 * @param array $conditions existing Conditions collected for the model
 * @param array $data Array of data used in search query
 * @param array $field Field definition information
 * @return array of conditions.
 * @access protected
 */
	protected function _addCondLike(Model $model, &$conditions, $data, $field) {
		$fieldName = $field['name'];
		if (isset($field['field'])) {
			$fieldName = $field['field'];
		}
		if (strpos($fieldName, '.') === false) {
			$fieldName = $model->alias . '.' . $fieldName;
		}
		if (!empty($data[$field['name']])) {
			$conditions[$fieldName . " LIKE"] = "%" . $data[$field['name']] . "%";
		}
		return $conditions;
	}
	


	//オリジナル
	//チェックボックスのデータをlike句で検索する

	protected function _addLikeOr(Model $model, &$conditions, $data, $field) {
		
		$fieldName = $field['name'];
		if (isset($field['field'])) {
			$fieldName = $field['field'];
		}
		if (strpos($fieldName, '.') === false) {
			$fieldName = $model->alias . '.' . $fieldName;
		}
		
		
		if (!empty($data[$field['name']])) {
			$conditions['or'] = '';
			$ar = explode('|',$data[$field['name']]);
		
			foreach($ar as $v){
				$conditions['or'][][$fieldName . " LIKE"] = "%" . $v . "%";
			}
			
		}
		
		return $conditions;
	}

	
/**
 * オリジナル。チェックボックスの配列データをorで検索
 *
 * @param AppModel $model Reference to the model
 * @param array $conditions existing Conditions collected for the model
 * @param array $data Array of data used in search query
 * @param array $field Field definition information
 * @return array of conditions.
 * @access protected
 */
	protected function _addOrValue(Model $model, &$conditions, $data, $field) {
		
		if(!empty($data[$field['name']]) && preg_match('/\|/',$data[$field['name']])){
			$data[$field['name']] = explode('|',$data[$field['name']]);
		}
		
		$fieldName = $field['name'];
		if (isset($field['field'])) {
			$fieldName = $field['field'];
		}
		if (strpos($fieldName, '.') === false) {
			$fieldName = $model->alias . '.' . $fieldName;
		}
		if (!empty($data[$field['name']]) || (isset($data[$field['name']]) && (int)$data[$field['name']] === 0)) {
			$conditions[$fieldName] = $data[$field['name']];
		}
		return $conditions;
	}
	
/**
 * Add Conditions based on exacltly comparrison
 *
 * @param AppModel $model Reference to the model
 * @param array $conditions existing Conditions collected for the model
 * @param array $data Array of data used in search query
 * @param array $field Field definition information
 * @return array of conditions.
 * @access protected
 */
	protected function _addCondValue(Model $model, &$conditions, $data, $field) {
		$fieldName = $field['name'];
		if (isset($field['field'])) {
			$fieldName = $field['field'];
		}
		if (strpos($fieldName, '.') === false) {
			$fieldName = $model->alias . '.' . $fieldName;
		}
		if (!empty($data[$field['name']]) || (isset($data[$field['name']]) && (int)$data[$field['name']] === 0)) {
			$conditions[$fieldName] = $data[$field['name']];
		}
		return $conditions;
	}

/**
 * Add Conditions based query to search conditions.
 *
 * @param Object $model  Instance of AppModel
 * @param array $conditions Existing conditions.
 * @param array $data Data for a field.
 * @param array $field Info for field.
 * @return array of conditions modified by this method.
 * @access protected
 */
	protected function _addCondQuery(Model $model, &$conditions, $data, $field) {
		if ((method_exists($model, $field['method']) || $this->__checkBehaviorMethods($model, $field['method'])) && !empty($data[$field['name']])) {
			$conditionsAdd = $model->{$field['method']}($data);
			$conditions = array_merge($conditions, (array)$conditionsAdd);
		}
		return $conditions;
	}

/**
 * Add Conditions based expressions to search conditions.
 *
 * @param Object $model  Instance of AppModel
 * @param array $conditions Existing conditions.
 * @param array $data Data for a field.
 * @param array $field Info for field.
 * @return array of conditions modified by this method.
 */
	protected function _addCondExpression(Model $model, &$conditions, $data, $field) {
		$fieldName = $field['field'];
		if ((method_exists($model, $field['method']) || $this->__checkBehaviorMethods($model, $field['method'])) && !empty($data[$field['name']])) {
			$fieldValues = $model->{$field['method']}($data, $field);
			if (!empty($conditions[$fieldName]) && is_array($conditions[$fieldName])) {
				$conditions[$fieldName] = array_unique(array_merge(array($conditions[$fieldName]), array($fieldValues)));
			} else {
				$conditions[$fieldName] = $fieldValues;
			}
		}
		return $conditions;
	}

/**
 * Add Conditions based subquery to search conditions.
 *
 * @param Object $model  Instance of AppModel
 * @param array $conditions Existing conditions.
 * @param array $data Data for a field.
 * @param array $field Info for field.
 * @return array of conditions modified by this method.
 * @access protected
 */
	protected function _addCondSubquery(Model $model, &$conditions, $data, $field) {
		$fieldName = $field['field'];
		if ((method_exists($model, $field['method']) || $this->__checkBehaviorMethods($model, $field['method'])) && !empty($data[$field['name']])) {
			$subquery = $model->{$field['method']}($data);
			$conditions[] = array("$fieldName in ($subquery)");
		}
		return $conditions;
	}

/**
 * Helper method for getQuery.
 * extension of dbosource method. Create association query.
 *
 * @param AppModel $model
 * @param array $queryData
 * @param integer $recursive
 * @access private
 */
	private function __queryGet(Model $model, $queryData = array(), $recursive = null) {
		$db =& ConnectionManager::getDataSource($model->useDbConfig);
		$db->__scrubQueryData($queryData);
		$null = null;
		$array = array();
		$linkedModels = array();
		$db->__bypass = false;
		$db->__booleans = array();

		if ($recursive === null && isset($queryData['recursive'])) {
			$recursive = $queryData['recursive'];
		}

		if (!is_null($recursive)) {
			$_recursive = $model->recursive;
			$model->recursive = $recursive;
		}

		if (!empty($queryData['fields'])) {
			$db->__bypass = true;
			$queryData['fields'] = $db->fields($model, null, $queryData['fields']);
		} else {
			$queryData['fields'] = $db->fields($model);
		}

		foreach ($model->__associations as $type) {
			foreach ($model->{$type} as $assoc => $assocData) {
				if ($model->recursive > -1) {
					$linkModel =& $model->{$assoc};

					$external = isset($assocData['external']);
					if ($model->alias == $linkModel->alias && $type != 'hasAndBelongsToMany' && $type != 'hasMany') {
						if (true === $db->generateSelfAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
							$linkedModels[] = $type . '/' . $assoc;
						}
					} else {
						if ($model->useDbConfig == $linkModel->useDbConfig) {
							if (true === $db->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
								$linkedModels[] = $type . '/' . $assoc;
							}
						}
					}
				}
			}
		}
		return $db->generateAssociationQuery($model, $null, null, null, null, $queryData, false, $null);
	}

/**
 * Check if model have some method in attached behaviors
 *
 * @param Model $Model
 * @param string $method
 * @return boolean, true if method exists in attached and enabled behaviors
 **/
	private function __checkBehaviorMethods(Model $Model, $method) {
		$behaviors = $Model->Behaviors->enabled();
		$count = count($behaviors);
		$found = false;
		for ($i = 0; $i < $count; $i++) {
			$name = $behaviors[$i];
			$methods = get_class_methods($Model->Behaviors->{$name});
			$check = array_flip($methods);
			$found = isset($check[$method]);
			if ($found) {
				return true;
			}
		}
		return $found;
	}

}
?>


○prg.php コンポーネント
<?php
/**
 * CakePHP Tags Plugin
 *
 * Copyright 2009 - 2010, Cake Development Corporation
 *                        1785 E. Sahara Avenue, Suite 490-423
 *                        Las Vegas, Nevada 89104
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright 2009 - 2010, Cake Development Corporation (http://cakedc.com)
 * @link      http://github.com/CakeDC/Search
 * @package   plugins.search
 * @license   MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

/**
 * Post-Redirect-Get: Transfers POST Requests to GET Requests
 *
 * @package		plugins.search
 * @subpackage	plugins.search.controllers.components
 */
class PrgComponent extends Object {
/**
 * Actions used to fetch the post data
 *
 * Maps the action that takes the post data and processes it by using this 
 * component and maps it to another action that is accessed by a redirect which
 * has the post data attached as get data now
 *
 * array('search' => 'results');
 * array('search' => array('controller' => 'results');
 *
 * @var array actions
 * @access public
 */
	public $actions = array();

/**
 * Intialize Callback
 *
 * @param object Controller object
 * @access public
 */
	public function initialize(&$controller) {
		$this->controller = $controller;
	}

/**
 * Poplulates controller->data with allowed values from the named/passed get params
 *
 * Fields in $controller::$presetVars that have a type of 'lookup' the foreignKey value will be inserted
 * 
 * 1) 'lookup'
 *    Is used for autocomplete selectors
 *    For autocomplete we have hidden field with value and autocomplete text box
 *    Component fills text part on id from hidden field
 * 2) 'value'
 *    The value as it is entered in form
 * 3) 'checkbox'
 *    Allows to pass several values internaly encoded as string
 *
 * 1 use field, model, formField, and modelField
 * 2, 3 need only field parameter
 *
 * @param array
 * @access public
 */
	public function presetForm($model) {
		$data = array($model => array());
		$args = $this->controller->passedArgs;

		foreach ($this->controller->presetVars as $field) {
			if ($field['type'] == 'lookup') {
				if (isset($args[$field['field']])) {
					$searchModel = $field['model'];
					$this->controller->loadModel($searchModel);
					$this->controller->{$searchModel}->recursive = -1;
					$result = $this->controller->{$searchModel}->findById($args[$field['field']]);
					$data[$model][$field['field']] = $args[$field['field']];
					$data[$model][$field['formField']] = $result[$searchModel][$field['modelField']];
				}
			}
	
			if ($field['type'] == 'checkbox') {
				if (isset($args[$field['field']])) {
					$values = split('\|', $args[$field['field']]);
					$data[$model][$field['field']] = $values;
				}
			}

			if ($field['type'] == 'value') {
				if (isset($args[$field['field']])) {
					$data[$model][$field['field']] = $args[$field['field']];
				}
			}
		}

		$this->controller->data = $data;
		$this->controller->parsedData = $data;
	}

/**
 * Restores form params for checkboxs and other url encoded params
 * 
 * @param array
 * @access public
 */
	public function serializeParams(&$data) {
		
		foreach ($this->controller->presetVars as $field) {
			if ($field['type'] == 'checkbox') {
				if (!empty($data[$field['field']]) && is_array($data[$field['field']])) {
					$values = join('|', $data[$field['field']]);
				} else {
					$values = '';
				}
				$data[$field['field']] = $values;
			}
		}
		return $data;
	}

/**
 * Connect named arguments
 *
 * @param array $data
 * @param array $exclude
 * @return void
 * @access public
 */
	public function connectNamed($data = null, $exclude = array()) {
		if (!isset($data)) {
			$data = $this->controller->passedArgs;
		}

		if (!is_array($data)) {
			return;
		}

		foreach ($data as $key => $value) {
			if (!is_numeric($key) && !in_array($key, $exclude)) {
				Router::connectNamed(array($key));
			}
		}
	}

/**
 * Exclude 
 * 
 * Removes key/values from $array based on $exclude 

 * @param array Array of data to be filtered
 * @param array Array of keys to exclude from other $array
 * @return array
 * @access public
 */
	public function exclude($array, $exclude) {
		$data = array();
		foreach ($array as $key => $value) {
			if (!is_numeric($key) && !in_array($key, $exclude)) {
				$data[$key] = $value;
			}
		}
		return $data;
	}

/**
 * Common search method
 * 
 * Handles processes common to all PRG forms
 *
 * - Handles validation of post data
 * - converting post data into named params
 * - Issuing redirect(), and connecting named parameters before redirect
 * - Setting named parameter form data to view
 *
 * @param string $modelName Name of the model class being used for the prg form
 * @param array $options Optional parameters:
 *  - string form Name of the form involved in the prg
 *  - string action The action to redirect to. Defaults to the current action
 *  - mixed modelMethod If not false a string that is the model method that will be used to process the data 
 * @return void
 * @access public
 */
	public function commonProcess($modelName = null, $options = array()) {
		$defaults = array(
			'form' => null,
			'keepPassed' => true,
			'action' => null,
			'modelMethod' => 'validateSearch');
		extract(Set::merge($defaults, $options));

		if (empty($modelName)) {
			$modelName = $this->controller->modelClass;
		}

		if (empty($formName)) {
			$formName = $modelName;
		}

		if (empty($action)) {
			$action = $this->controller->action;
		}

		if (!empty($this->controller->data)) {
			$this->controller->{$modelName}->data = $this->controller->data;
			$valid = true;
			if ($modelMethod !== false) {
				$valid = $this->controller->{$modelName}->{$modelMethod}();
			}

			if ($valid) {
				$passed = $this->controller->params['pass'];
				$params = $this->controller->data[$modelName];
				$params = $this->exclude($params, array());
				if ($keepPassed) {
					$params = array_merge($passed, $params);
				}
				$this->serializeParams($params);
				$this->connectNamed($params, array());
				$params['action'] = $action;
				$params = array_merge($this->controller->params['named'], $params);
				$this->controller->redirect($params);
			} else {
				$this->controller->Session->setFlash(__('Please correct the errors below.', true));
			}
		}

		if (empty($this->controller->data) && !empty($this->controller->passedArgs)) {
			$this->connectNamed($this->controller->passedArgs, array());
			$this->presetForm($formName);
		}
	}

}
?>



んで使い方は

○適当なモデル
<?
	class Searchtest extends AppModel {
		
		var $actsAs = array(
			'Search.Searchable'
			);
		
		var $filterArgs = array(
			array('name' => 'vip', 'type' => 'or'),
		);		
		
	}
?>


コントローラーのタイプは checkbox 。
モデルは type or を指定。

すると

□夢
□希望

のまま検索すれば夢も希望も無い人だけ取得。

■夢
■希望

とすれば、夢か希望を持っているユーザーのみ取得できますよーん。








関連するタグ: PHP cakephp
あなたにとって有用な記事でしたか?是非ブックマークしておくことをおすすめします。
 




ライブラリを配布しちゃったり
webデザイン
Fireworks
HTMLコーディング
CSS
Dreamweaver
携帯サイト
webプログラム
PHP
正規表現
cakephp
MYSQL
javascript
webマーケティング
adwords
analytics
windows7
ショートカットキー
おすすめ情報
サイト
facebook
ライブラリ
配布
アプリ
iphone
ipad
サーバー
さくらサーバー
全ての記事を読む




トップ - 最新の記事一覧 - お問い合わせ