I am currently trying to figure out a way to implement cakeDC's search plugin within my application, but I am finding it quite difficult to understand the plumbing that needs to be done before I can get it to work(nicely) with my app.
Things to consider:
the search needs to be a 'live search'
Records retrieved need to be paginated
The search will be done using a selected criteria (id,name,etc the actual key not value)
and will require a user entry which we will call 'query' for now..
here is my code so far.
Model Code :
public $filterArgs = array(
'query' => array('type' => 'query', 'method' => 'filterQuery'),
);
public function filterQuery($data = array()) {
$filter = $data['query'];
$criteria = $data['criteria'];
if(empty($filter)){
return array();
}
$cond = array(
'OR' => array(
$this->alias . $criteria. 'LIKE' => '%' . $filter . '%',
//ie. criteria represents a field $ filter is the data to search/match
));
return $cond;
}
So what I am having trouble with is, how will my filterQuery method receive the $data argument.. Is it a normal request data ? I want to access both values submitted.
Here is the relevant code for the view:
<div id="search-container">
<?php
//echo $this->Form->create(false,array('type'=>'post','default'=>false));
echo $this->Form->input('criteria',array(
'label'=>'Search Criteria',
'options' => array(
'id'=> 'By ID',
'name' => 'By Name',
'blood_group_id' => 'By Blood Type',
'type' => 'By Donor Type',
'age' => 'By Age',
'gender' => 'By Gender'
)
));
?>
<?php echo $this->Form->input('query', array('type' => 'text', 'id' => 'query', 'name' => 'query', 'label' => false, 'placeholder' => 'Search')); ?>
[EDIT]
ofcourse in my controller I also have this setup
Search.Prg Component is loaded
public $presetVars = array(
'query' => array('type' => 'value'),
'criteria' => array('type' => 'value'),
);
Any help is appreciated, even if its just a link to a tutorial. Thanks
When I wrote the plugin a lot of useful examples I put directly into test cases of the plugin. So take a look into behavior test file to see how to use query type method.
Related
i'm using cakeDC's search plugin in my app. can;t seem to figure out why the results are empty if i pass no value in the username field i'm searching but have the account type filter set to either admin or user For the record, having the filter set to all with the username field empty will output all the users in the system trying to replicate the behavior of searches having the account type filter set to all with an empty username search field for having the account type filter set to either 2 user types
here's the relevant code if needed:
controller
public $components = array('Paginator', 'Session','Search.Prg');
public $presetVars = array(
array('field' => 'username', 'type' => 'value'),
array('field' => 'account_type', 'type' => 'value'));
public function admin_index() {
$this->Prg->commonProcess();
$this->paginate = array(
'conditions' => $this->User->parseCriteria($this->passedArgs));
$this->set('users', $this->Paginator->paginate(),'session',$this->Session);
model
public $actsAs = array('Search.Searchable');
public $filterArgs = array( array('name' => 'username', 'type' => 'query', 'method' => 'filterName'),
array('name' => 'account_type', 'type' => 'value'),
);
public function filterName($data, $field = null) {
$nameField = '%' . $data['username'] . '%';
return array(
'OR' => array(
$this->alias . '.username LIKE' => $nameField,
));
}
view search form
<div><?php echo $this->Form->create('User', array(
'novalidate' =>true,'url' => array_merge(array('action' => 'index','admin'=> true), $this->params['pass'])
)); ?>
<?php
$account_types = array(
'' => __('All', true),
0 => __('admin', true),
1 => __('user', true),);
echo $this->Form->input('username', array('div' => false, 'empty' => true)); // empty creates blank option.
echo $this->Form->input('account_type', array('label' => 'account_type', 'options' => $account_types));
echo $this->Form->submit(__('Search', true), array('div' => false));
echo $this->Form->end();
?></div>
Try to put 'empty' and allowEmpty in username and account_type like this:
public $filterArgs = array( array('name' => 'username', 'type' => 'query', 'method' => 'filterName', 'empty'=>true, 'allowEmpty'=>true),
array('name' => 'account_type', 'type' => 'value','empty'=>true, 'allowEmpty'=>true),
);
I had this kind of behavior as well from CakeDC/search and this two configs saved me, hope it will do for you as well.
It's a good idea to check the queries being executed as well using DebugKit.Toolbar.
I am trying to implement a search with CakeDC's Search plugin. In this search I have a field which has 'multiple' => 'checkbox' is set. This field allows user to select multiple cities so that they can filter results according to city/cities. What I did in favour to this, I simply specified the 'type' => 'IN' for that field in Searchable Model's $filterArgs. But noting happened it just responded with all result no searching/filtration happened. To get the clear picture of what I have implemented here are the code snippets:
Model.php
public $actsAs = array(
'Search.Searchable'
);
public $filterArgs = array(
'city' => array(
'type' => 'in',
'field' => 'Model.city'
));
search_form.ctp
echo $this->Form->create('Model', array('url' => array('controller' => 'models', 'action' => 'search')));
echo $this->Form->input('city', array(
'multiple' => 'checkbox',
'options' => array(
'city1' => 'city1',
'city2' => 'city2',
'cityn' => 'cityn')
));
echo $this->Form->end('search');
ModelsController.php
public function search() {
$this->layout = 'front_common';
$this->Prg->commonProcess();
$this->Paginator->settings = array(
'conditions' => $this->Model->parseCriteria($this->Prg->parsedParams()),
'limit' => 10
);
$this->set('Data', $this->Paginator->paginate());
}
also once I tried to use a beforeFilter() in ModelsController to implode the city array() with (,) to be used with IN but same all results. I want to ask if there is any other plugin to do this or any hack to do this with cakeDC's search plugin. Please help.
This should work fine, assuming you are passing in the arg to parseCriteria() as a simple array of values.
public $filterArgs = array(
array(
'name' => 'city',
'type' => 'value',
'field' => 'Model.city'
)
);
And you should be able to pass city:houston|dallas|austin in the URL
It should parse that into an array of args => [city => [houston, dallas, austin]]
And parseCriteria() will translate that into the following conditions fragment: [Model.city => [houston, dallas, austin]]
And CakePHP natively translates that into a SQL where fragment: IN(houston, dallas, austin)
Use DebugKit and monitor your SQL... you can debug() at any step of the process, you should get these values.
Here's the full docs on the Search plugin:
https://github.com/CakeDC/search/tree/master/Docs/Documentation
(I use it heavily, and I love how it lets me organize all search filters)
Following up on my previous question, I am supposedly passing the submitted post data as query strings like so:
echo $this->Form->create('Donor',array(
'url' => array_merge(array('action' =>'find'), $this->params['pass'])
));
But when I try the following within my controller's find action :
$this->Paginator->settings['conditions'] = $this->Donor->parseCriteria($this->Prg->parsedParams());
The $this->Prg->parsedParams() only consists of the criteria:
here's the var_dump
array (size=1)
'criteria' => string 'blood_group_id' (length=14)
And here is my view code :
<?php
echo $this->Form->create('Donor',array(
'url' => array_merge(array('action' =>'find'), $this->params['pass'])
));
echo $this->Form->input('criteria',array(
'label'=>'Search Criteria',
'options' => array(
'id'=> 'By ID',
'name' => 'By Name',
'blood_group_id' => 'By Blood Type',
'type' => 'By Donor Type',
'age' => 'By Age',
'gender' => 'By Gender'
)
));
?>
<?php echo $this->Form->input('query', array( 'id' => 'query', 'name' => 'query', 'label' => false, 'placeholder' => 'Search')); ?>
<?php echo $this->Form->end(__('Search'));?>
I believe that I should be receiving all submitted data and not only the criteria's value.. I do not know what is wrong, and frankly this is taking so much time to put together. I cant seem to figure out how to work with this plugin. Is it just me ? Please, if anyone could find the time to help out, I will be grateful!
The Answer was almost under my nose.. I inattentively was naming the 'query' input twice,
<?php echo $this->Form->input('query', array( 'id' => 'query', 'name' => 'query','label' => false, 'placeholder' => 'Search')); ?>
I removed the 'name' key, and everything worked fine! That was one stupid mistake, which wasted about an hour for me, so im hoping this would help someone who comes across something like this! Thanks
Ok, the case is the following:
There is a form that has a some drop downs and multiple selection fields.
All fine by now, except that I have to do the search by property type to be and/or and not only or as for the rest of the form (or at least I believe it is or for the rest).
i.e. property type 1 && property type 2 && (subtype 1 || sybtype 2 || sybtype 3)
Another thing is that when selecting all subtypes, the results shown should be the same as if none are selected.
I really have no idea where to read or look at for more detailed tutorial on the search functionality provided by this plugin in regards to "and" searches.
What I have in my model is the following:
public $filterArgs = array(
//array('name' => 'price', 'type' => 'query', 'method' => 'filterTitle'),
array('name' => 'province_id', 'type' => 'value'),
array('name' => 'city_id', 'type' => 'value'),
array('name' => 'quarter_id', 'type' => 'value'),
array('name' => 'refid', 'type' => 'value'),
array('name' => 'to', 'type' => 'value'),
array('name' => 'price1', 'name2' => 'price2', 'type' => 'expression', 'method' => 'priceRange', 'field' => 'Offer.price BETWEEN ? AND ?'),
array('name' => 'area1', 'name2' => 'area2', 'type' => 'expression', 'method' => 'areaRange', 'field' => 'Offer.area BETWEEN ? AND ?'),
array('name' => 'type_id', 'type' => 'query', 'method' => 'ManyOrConditions'),
array('name' => 'subtype_id', 'type' => 'query', 'method' => 'ManyOrConditions'),
array('name' => 'feature_id', 'type' => 'subquery', 'method' => 'findByTags1', 'field' => 'Offer.id'),
);
public function ManyOrConditions($data = array()) {
//debug($data);
$filter = $data['type_id'];
//not sure if this works
//$subtype = $data['subtype_id'];
$cond = array(
'OR' => array(
$this->alias . '.type_id ' => $filter,
//not sure if the below will work?
//$this->alias . '.subtype_id ' => $subtype
));
//debug($cond);
return $cond;
}
public function orConditions($data = array()) {
//debug($data);
$filter = $data['type_id'];
$cond = array(
'OR' => array(
$this->alias . '.type_id LIKE' => '%' . $filter . '%',
//$this->alias . '.body LIKE' => '%' . $filter . '%',
));
return $cond;
}
Where it as I understand it is creating only "or" searches for the type_id ... sorry guys really am lost in this.
I am new to cakephp and so far have been able to read through thousands of lines of code and do a lot of changes to this system but here I just have no clue what to do due to lack of documentation on this plugin :(
I will appreciate any guidance to tutorials and/or proper documentation - not this one https://github.com/CakeDC/search.
If this is the right one though, please let me know what I am missing.
P.S. If you need any other code here, just let me know and I will provide it.
AND conditions or any other customized query/condition would work exactly the same as orConditions. It is just a callback method in which you can do anything you want to build any kind of query you need.
See how "complex" conditions work: http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#complex-find-conditions
array(
'OR' => array(
array('Company.name' => 'Future Holdings'),
array('Company.city' => 'CA')
),
'AND' => array(
array(
'OR' => array(
array('Company.status' => 'active'),
'NOT' => array(
array('Company.status' => array('inactive', 'suspended'))
)
)
)
)
)
Just create a custom method "myCustomAnd" and put whatever query you need in there using the $data that is passed to the method.
The plugin comes along with a complete example I don't think it is bad documented. It is pretty obvious what you need to do IMHO.
Thank you very much for your directions burzum. I finally understood it. Here is how I did it:
public function andTypeConditions($data = array()){
//The below two variables contain the data received by the submission of the form
$typeID = $data['type_id'];
$subTypeID = $data['subtype_id'];
//Define a variable $conditions to return. The conditions you can build according CakePHP manual for complex find conditions
if (isset($data['subtype_id'])) {
$conditions = array(
'Offer.type_id' => $typeID,
'Offer.subtype_id' => $subTypeID
);
} else {
$conditions = array(
'Offer.type_id' => $typeID
);
}
return $conditions;
}
For anyone that struggles with the same issue to clarify:
In order to do an "AND" search in cakeDC search plugin you need to create a method that you will later on assign to both (in my case two but if you have more , to all) fields like so:
public $filterArgs = array(
array('name' => 'type_id', 'type' => 'query', 'method' => 'andTypeConditions'),
array('name' => 'subtype_id', 'type' => 'query', 'method' => 'andTypeConditions'),
);
... the method itself contains the comments you need to be able to adjust it for your needs.
Here's what I ended up doing for a more complex search. Using the CakeDC Search plugin...this uses 'LIKE OR' instead of 'LIKE AND', but hopefully it sheds some light on the how to do more complex searches using the Search plugin.
User.search is the input element name in my submission form.
class User extends AppModel {
public $filterArgs = array(
'search' => array('type' => 'like', 'field' => array('User.name', 'User.email'))
}
}
class UsersController extends AppController {
public $components = array('Search.Prg');
public function index() {
$this->Prg->commonProcess();
$this->User->data['User'] = $this->passedArgs;
if ($this->User->Behaviors->loaded('Searchable')) {
$query = $this->passedArgs;
$parsedConditions = $this->User->parseCriteria($query);
} else {
$parsedConditions = array();
}
$this->Paginator->settings['User']['conditions'] = $parsedConditions;
$this->set('users', $this->Paginator->paginate());
}
}
I have the following table:
CREATE TABLE "place" (
"id" int8 NOT NULL DEFAULT nextval('place_id_seq'::regclass),
"name" varchar(128) NOT NULL,
"parent" int8,
"description" varchar(100)
)
WITH (OIDS=FALSE);
(It's PostgreSQL but this shouldn't be relevant here)
I created the CRUD through giix and changed a bit the relations to match my needs. So I ended up with:
public function relations() {
return array(
'parent0' => array(self::BELONGS_TO, 'Places', 'parent'),
'places' => array(self::HAS_MANY, 'Places', 'parent'),
);
}
The goal here is that a place can belong to another place and have multiple children places.
My problem is that I need to change the admin action to match the following grid:
Print manipulated by the inspector to reflect the desired behaviour
So my problem is to get the list of all the children objects and display it as a list of links. I tried change the respective admin.php view file for:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'places-grid',
'dataProvider' => new CActiveDataProvider('Places', array('id'=>$model->places)),
'filter' => $model,
'columns' => array(
array(
'name'=>'parent',
'value'=>'GxHtml::valueEx($data->parent0)',
'filter'=>GxHtml::listDataEx(Places::model()->findAllAttributes(null, true)),
),
'name',
'places',
array(
'class' => 'CButtonColumn',
),
),
));
But this, as expected, throughs an error:
htmlspecialchars() expects parameter 1 to be string, array given
Also I don't know this would even work with the built in avanced search.
Could anyone kindly point me into the correct direction here?
After much search I found a way to solve my problem. Not sure if it's the best way but it works nicely:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'places-grid',
'dataProvider' => new CActiveDataProvider('Places', array('id'=>$model->places)),
'filter' => $model,
'columns' => array(
array(
'name'=>'parent',
'value'=>'GxHtml::valueEx($data->parent0)',
'filter'=>GxHtml::listDataEx(Places::model()->findAllAttributes(null, true)),
),
'name',
array(
'header'=>'Children',
'type'=>'html',
'value'=> function($data) {
$placesLinks = array();
foreach ($data->places as $place) {
$placesLinks[] = GxHtml::link(GxHtml::encode(GxHtml::valueEx($place, 'name')), array('places/view', 'id' => GxActiveRecord::extractPkValue($place, true)));
}
return implode(', ', $placesLinks);
}
),
array(
'class' => 'CButtonColumn',
),
),
));
This uses Giix. If you don't use that then you'll have to change the link generation accordingly.
Cheers