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)
Related
I am using the search plugin for CakePHP.
I try to dynamically setup the $filterArgs array that is used for filtering the input from the user. The reason why I want to do this is because my customers can create customer specific input fields. I want to make them filterable and searchable.
Customer.php:
public function beforeFind($queryData) {
$this->filterArgs['garantie'] = array(
'type' => 'subquery',
'method' => 'findCustomerCustomFieldsByText',
'field' => array('Customer.id'),
'encode' => true
);
}
Debugging $filterArgs shows that the entry was made:
array(
'garantie' => array(
'type' => 'subquery',
'method' => 'findCustomerCustomFieldsByText',
'field' => array(
(int) 0 => 'Customer.id'
),
'encode' => true
)
)
Unfortunately the method findCustomerCustomFieldsByText() is not called.
It looks like beforeFind() might not be the correct method to call. What callback-method should I use so I can use dynamic creation of $filterArgs?
Edit:
My question is not identical to the linked question because it is about the callback methods that prevent the filter from working.
Is it possible to dynamically create the $filterArgs array in CakePHP when using the search plugin?
My customers are able to create their own input fields (customer specific) and I want to make all of them searchable. But for this I have to map them in the $filterArgs array.
E.g.:
public $filterArgs = array(
'input_filter' => array(
'type' => 'subquery',
'method' => 'findCustomerCustomFieldsByText',
'field' => 'Customer.id',
'encode' => true
)
);
Just add them conditionally as you need to the filterArgs array.
if ($someFieldIsPresentCheckHere) {
$this->Model->filterArgs['someThing'] = [ /* settings go here */ ];
}
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.
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