Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

emptyValue enhancement and default settings handling via Configure() #71

Merged
merged 2 commits into from

3 participants

Mark S. Rachman Chavik Florian Krämer
Mark S.
  • default settings handling via Configure()
  • emptyValue default values to allow search for "not any of the below"
  • code fixes and corrections
  • more tests

Configure::read('Search') is quite useful, as you can set (DRY) app wide global settings, while still applying quite different ones throughout the dozens of controllers:

$config['Search'] = array(
'Prg' => array(
    'commonProcess'=>array('paramType' => 'querystring', 'filterEmpty' => true),
    'presetForm' =>array('paramType' => 'querystring')
),
'Searchable' => array(),
);

Let me know what you think about it. I could also split them. Just squashed and rebased as it was quickest.

Florian Krämer burzum merged commit d57194b into from
Mark S. dereuromark deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
28 Controller/Component/PrgComponent.php
View
@@ -77,8 +77,13 @@ class PrgComponent extends Component {
*/
public function __construct(ComponentCollection $collection, $settings) {
$this->controller = $collection->getController();
- $this->_defaults = Set::merge($this->_defaults, $settings);
- // fix for not throwing warning
+
+ $this->_defaults = Set::merge($this->_defaults, array(
+ 'commonProcess' => (array)Configure::read('Search.Prg.commonProcess'),
+ 'presetForm' => (array)Configure::read('Search.Prg.presetForm'),
+ ), $settings);
+
+ // fix for not throwing warnings
if (!isset($this->controller->presetVars)) {
$this->controller->presetVars = array();
}
@@ -89,7 +94,7 @@ public function __construct(ComponentCollection $collection, $settings) {
}
if ($this->controller->presetVars === true) {
- // auto-set the presetVars based on search defitions in model
+ // auto-set the presetVars based on search definitions in model
$this->controller->presetVars = array();
$filterArgs = $this->controller->$model->filterArgs;
foreach ($filterArgs as $key => $arg) {
@@ -173,6 +178,10 @@ public function presetForm($options) {
$data[$model][$field['field']] = $args[$field['field']];
}
+ if ($data[$model][$field['field']] === '' && isset($field['emptyValue'])) {
+ $data[$model][$field['field']] = $field['emptyValue'];
+ }
+
if (isset($data[$model][$field['field']]) && $data[$model][$field['field']] !== '') {
$this->isSearch = true;
}
@@ -319,6 +328,17 @@ public function commonProcess($modelName = null, array $options = array()) {
if ($filterEmpty) {
$params = Set::filter($params);
}
+ foreach ($this->controller->presetVars as $presetVar) {
+ $field = $presetVar['name'];
+ if (!isset($params[$field])) {
+ continue;
+ }
+ if (!isset($presetVar['emptyValue']) || $presetVar['emptyValue'] !== $params[$field]) {
+ continue;
+ }
+ $params[$field] = '';
+ }
+
$this->connectNamed($params, array());
} else {
$searchParams = array_merge($this->controller->request->query, $searchParams);
@@ -326,8 +346,8 @@ public function commonProcess($modelName = null, array $options = array()) {
if ($filterEmpty) {
$searchParams = Set::filter($searchParams);
}
- $params['?'] = $searchParams;
$this->connectNamed($params, array());
+ $params['?'] = $searchParams;
}
$params['action'] = $action;
14 Model/Behavior/SearchableBehavior.php
View
@@ -50,6 +50,7 @@ class SearchableBehavior extends ModelBehavior {
* @param array $config
*/
public function setup(Model $Model, $config = array()) {
+ $this->_defaults = array_merge($this->_defaults, (array)Configure::read('Search.Searchable'));
$this->settings[$Model->alias] = array_merge($this->_defaults, $config);
if (empty($Model->filterArgs)) {
return;
@@ -322,7 +323,7 @@ protected function _connectedLike($value, $field, $fieldName) {
/**
* Add Conditions based on exact comparison
*
- * @param \Model $Model Reference to the model
+ * @param Model $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
@@ -337,8 +338,15 @@ protected function _addCondValue(Model $Model, &$conditions, $data, $field) {
if (strpos($fieldName, '.') === false) {
$fieldName = $Model->alias . '.' . $fieldName;
}
- if (!empty($fieldValue) || (String)$fieldValue !== '') {
+ if ((String)$fieldValue !== '') {
$cond[$fieldName] = $fieldValue;
+ } elseif (isset($data[$field['name']]) && !empty($field['allowEmpty'])) {
+ $schema = $Model->schema($field['name']);
+ if ($schema) {
+ $cond[$fieldName] = $schema['default'];
+ } else {
+ $cond[$fieldName] = $fieldValue;
+ }
}
}
if (count($cond) > 1) {
@@ -515,4 +523,4 @@ protected function _checkBehaviorMethods(Model $Model, $method) {
return $found;
}
-}
+}
149 Test/Case/Controller/Component/PrgComponentTest.php
View
@@ -481,6 +481,41 @@ public function testCommonProcessFilterEmpty() {
}
/**
+ * testCommonProcessSpecialChars
+ *
+ * @return void
+ */
+ public function testCommonProcessSpecialChars() {
+ $this->Controller->request->params = array_merge($this->Controller->request->params, array(
+ 'named' => array(),
+ 'lang' => 'en',
+ ));
+
+ $this->Controller->presetVars = array();
+ $this->Controller->action = 'search';
+ $this->Controller->request->data = array(
+ 'Post' => array(
+ 'title' => 'test/slashes?!',
+ 'foo' => '',
+ 'bar' => ''));
+
+ $this->Controller->Prg->commonProcess('Post', array(
+ 'form' => 'Post',
+ 'modelMethod' => false,
+ 'filterEmpty' => true,
+ 'allowedParams' => array('lang')));
+ $expected = array(
+ 'title' => 'test/slashes?!',
+ 'action' => 'search',
+ 'lang' => 'en');
+ $this->assertEquals($expected, $this->Controller->redirectUrl);
+
+ $url = Router::url($this->Controller->redirectUrl);
+ $expected = '/search/title:test%2Fslashes%3F%21/lang:en';
+ $this->assertEquals($expected, $url);
+ }
+
+/**
* testCommonProcessQuerystring
*
* @return void
@@ -512,6 +547,42 @@ public function testCommonProcessQuerystring() {
}
/**
+ * testCommonProcessQuerystringSpecialChars
+ *
+ * @return void
+ */
+ public function testCommonProcessQuerystringSpecialChars() {
+ $this->Controller->request->params = array_merge($this->Controller->request->params, array(
+ 'named' => array(),
+ 'lang' => 'en',
+ ));
+
+ $this->Controller->presetVars = array();
+ $this->Controller->action = 'search';
+ $this->Controller->request->data = array(
+ 'Post' => array(
+ 'title' => 'test/slashes?!',
+ 'foo' => '',
+ 'bar' => ''));
+
+ $this->Controller->Prg->commonProcess('Post', array(
+ 'form' => 'Post',
+ 'modelMethod' => false,
+ 'filterEmpty' => true,
+ 'paramType' =>'querystring',
+ 'allowedParams' => array('lang')));
+ $expected = array(
+ '?' => array('title' => 'test/slashes?!'),
+ 'action' => 'search',
+ 'lang' => 'en');
+ $this->assertEquals($expected, $this->Controller->redirectUrl);
+
+ $url = Router::url($this->Controller->redirectUrl);
+ $expected = '/search/lang:en?title=test%2Fslashes%3F%21';
+ $this->assertEquals($expected, $url);
+ }
+
+/**
* testCommonProcessQuerystringPagination
*
* @return void
@@ -726,4 +797,82 @@ public function testPresetFormWithEncodedParams() {
$this->assertTrue($this->Controller->Prg->isSearch);
}
+/**
+ * testCommonProcessGetWithEmptyValue
+ *
+ * @return void
+ */
+ public function testCommonProcessGetWithEmptyValue() {
+ $this->Controller->request->params = array_merge($this->Controller->request->params, array(
+ 'named' => array(),
+ 'category_id' => '0',
+ ));
+
+ $this->Controller->presetVars = array(
+ array(
+ 'field' => 'category_id',
+ 'name' => 'category_id',
+ 'type' => 'value',
+ 'allowEmpty' => true,
+ 'emptyValue' => '0',
+ ),
+ array(
+ 'field' => 'checkbox',
+ 'name' => 'checkbox',
+ 'type' => 'checkbox'
+ ),
+ );
+ $this->Controller->action = 'search';
+ $this->Controller->request->data = array(
+ 'Post' => array(
+ 'category_id' => '0',
+ 'foo' => ''));
+
+ $this->Controller->Prg->commonProcess('Post', array(
+ 'form' => 'Post',
+ 'modelMethod' => false,
+ 'filterEmpty' => true));
+ $expected = array(
+ 'action' => 'search',
+ 'category_id' => '');
+ $this->assertEquals($expected, $this->Controller->redirectUrl);
+ }
+
+/**
+ * testPresetFormWithEmptyValue
+ *
+ * @return void
+ */
+ public function testPresetFormWithEmptyValue() {
+ $this->Controller->presetVars = array(
+ array(
+ 'field' => 'category_id',
+ 'type' => 'value',
+ 'allowEmpty' => true,
+ 'emptyValue' => '0',
+ ),
+ array(
+ 'field' => 'checkbox',
+ 'type' => 'checkbox'
+ ),
+ );
+ $this->Controller->passedArgs = array(
+ 'category_id' => '',
+ 'checkbox' => $this->_urlEncode('test|test2|test3'),
+ );
+ $this->Controller->beforeFilter();
+
+ $this->Controller->Prg->encode = true;
+ $this->Controller->Prg->presetForm(array('model' => 'Post'));
+ $expected = array(
+ 'Post' => array(
+ 'category_id' => '0',
+ 'checkbox' => array(
+ 0 => 'test',
+ 1 => 'test2',
+ 2 => 'test3')));
+ $this->assertEquals($this->Controller->request->data, $expected);
+ $this->assertTrue($this->Controller->Prg->isSearch);
+ }
+
}
28 readme.md
View
@@ -1,6 +1,6 @@
# Search Plugin for CakePHP #
-Version 2.1 for cake 2.x
+Version 2.2 for cake 2.x
The Search plugin allows you to make any kind of data searchable, enabling you to implement a robust searching rapidly.
@@ -8,6 +8,10 @@ The Search plugin is an easy way to include search into your application, and pr
It supports simple methods to search inside models using strict and non-strict comparing, but also allows you to implement any complex type of searching.
+## UPDATE for 2.2 - 2013-01-16 Mark Scherer
+
+* `emptyValue` is now available for fields to make it work with "default NULL" fields and `allowEmpty` set to true. See example below.
+
## Sample of usage ##
An example of how to implement complex searching in your application.
@@ -115,6 +119,27 @@ In this example on model level shon example of search by OR condition. For this
'search_with_connectors' => array('type' => 'like', 'field' => 'Article.title', 'connectorAnd' => '+', 'connectorOr' => ',')
);
+### `emptyValue` default values to allow search for "not any of the below"
+
+Let's say we have categories and a dropdown list to select any of those or "empty = ignore this filter". But what if we also want to have an option to find all non-categorized items?
+With "default 0 NOT NULL" fields this works as we can use 0 here explicitly:
+
+ $categories = $this->Model->Category->find('list');
+ array_unshift($categories, '- not categorized -'); // before passing it on to the view (the key will be 0, not '' as the ignore-filter key will be)
+
+But for char36 foreign keys or "default NULL" fields this does not work. The posted empty string will result in the omitting of the rule.
+That's where `emptyValue` comes into play.
+
+ public $presetVars = array(
+ 'category_id' => array(
+ 'allowEmpty' => true,
+ 'emptyValue' => '0',
+ );
+ );
+
+This way we assign '' for 0, and "ignore" for '' on POST, and the opposite for presetForm().
+
+Note: This only works if you use `allowEmpty` here. If you fail to do that it will always trigger the lookup here.
## Full example for model/controller configuration with overriding
@@ -150,7 +175,6 @@ In this example on model level shon example of search by OR condition. For this
// search example with wildcards in the view for field `search`
20??BE* => matches 2011BES and 2012BETR etc
-
## Behavior and Model configuration ##
All search fields need to be configured in the Model::filterArgs array.
Something went wrong with that request. Please try again.