I've set a simple search engine on my CakePHP project which looks like that :
<?php
echo $this->Form->create("Post", array(
"action" => "search",
"id" => "searchForm"
));
echo $this->Form->input("keyword", array(
"label" => "",
"type" => "search",
"placeholder" => "Recherche..."
));
echo $this->Form->end();
?>
Here is the controller :
function search() {
$keyword = $this->request->data;
$keyword = $keyword["Post"]["keyword"];
$cond = array("OR" => array(
"Post.title LIKE '%$keyword%'",
"Post.description LIKE '%$keyword%'"
));
$posts = $this->Post->find("all", array("conditions" => $cond));
$this->set(compact("posts", "keyword"));
}
And it works great. The only problem is when I want to paginate the results. I simply add :
$posts = $this->paginate();
And here is the problem. When I add this, CakePHP give me all the posts and not only the ones that match the keyword.
So, if you would have a solution, it would be nice :)
According to the CakePHP book you should be able to do
$this->paginate('Post', array(
'OR' => array(
'Post.title LIKE' => "%$keyword%",
'Post.description LIKE' => "%$keyword%"
)
));
Or you can do it like this ( from the cakephp site ).
public function list_recipes() {
$this->paginate = array(
'conditions' => array('Recipe.title LIKE' => 'a%'),
'limit' => 10
);
$data = $this->paginate('Recipe');
$this->set(compact('data'));
);
Source:
http://book.cakephp.org/2.0/en/core-libraries/components/pagination.html
Paginate does its own lookup on the data I believe. The find you are calling previously has no effect on the paginate.
Try this:
$this->paginate = array( 'conditions' => $cond, ));
you can use Session for storing conditions
the first, when you submit form you store conditions into Session
and then(paginate) you can read conditions from Session
example i want to search the products:
Products/search.ctp
<?php
echo $this->Form->create('Product');
echo $this->Form->input('keyword');
echo $this->Form->end(__('Search'));
?>
ProductsController.php
<?php
class ProductsController extends AppController{
public function search() {
if ($this->request->is('post')) {
$keyword = $this->request->data['Product']['keyword'];
$this->paginate = array(
'fields' => array('Product.name', 'Product.price', 'Product.created'),
'order' => array('Product.created' => 'DESC', 'Product.price' => 'DESC'),
'limit' => 30,
'conditions' => array('Product.name LIKE' => '%' . $keyword . '%')
);
// store array $this->paginate into Session
$this->Session->write('paginate', $this->paginate);
}
$this->paginate = $this->Session->read('paginate');
$this->set('products', $this->paginate('Product'));
}
}
Related
I've tried every simple cakedc search setup, and followed code samples from previous posts on this but it just won't seem to cooperate for me. I'm not sure, but there doesn't seem to be any search query generated with the search string, judging from a query log I have running on my database.
When I try to search, from /books/search, I get no results on a title I know is in the database and the URL changes to /books/index/title:sweet (I'm not sure if that is relevant).
Any help would be much appreciated.
Model
public $actsAs = array('Search.Searchable');
public $primaryKey = 'isbn13';
public $hasMany = array(
'Contributor' => array (
'className' => 'Contributor',
'foreignKey' => 'isbn13'
),
'Override' => array (
'className' => 'Override',
'foreignKey' => 'isbn13'
)
);
public $filterArgs = array(
'title' => array('type' => 'query', 'method' => 'filterTitle'),
'main_desc' => array('type' => 'value')
);
public function filterTitle($data, $field = null) {
if (empty($data['title'])) {
return array();
}
$titleField = '%' . $data['title'] . '%';
return array(
'OR' => array(
$this->alias . '.title LIKE' => $titleField,
));
}
Controller
class BooksController extends AppController {
public $helpers = array ('Html', 'Form');
public $paginate = array();
public $components = array('Search.Prg');
public $presetVars = true;
public function index() {
//get books
$this->set('books', $this->Book->find('all'));
//search
/* $this->Prg->commonProcess();
$this->paginate['conditions'] = $this->Book->parseCriteria($this->Prg->parsedParams());
$this->set('books');
*/
}
public function search($books = NULL) {
$this->Prg->commonProcess();
$this->paginate['conditions'] = $this->Book->parseCriteria($this->passedArgs);
$this->set('books', $this->paginate());
}
View
<h1>Search Results</h1>
<?php
echo $this->Form->create('Book' , array(
'url' => array_merge(array('action' => 'search'), $this->params['pass'])
));
echo $this->Form->input('title', array('div' => false, 'empty' => true)); // empty creates blank option.
echo $this->Form->submit(__('Search', true), array('div' => false));
echo $this->Form->end();
Routes
Router::connect('/', array('controller' => 'books', 'action' => 'index'));
Router::connect('/index/*', array('controller' => 'books', 'action' => 'search'
));
Router::connect('/view/:url', array('controller' => 'books', 'action' => 'view', array('url' => '[\w\W]*')
));
Router::connect('/edit/:url', array('controller' => 'books', 'action' => 'edit', array('url' => '[\w\W]*')
));
Router::connect('/whatsnew/*', array('controller' => 'books', 'action' => 'whatsnew'
));
Router::connect('/search/*', array('controller' => 'books', 'action' => 'search'
));
I've renamed the search-master folder to Search and put it in my plugins directory and In my bootstrap.php I've loaded the plugin
CakePlugin::load('Search');
Fixed! Needed
$this->set('books', $this->paginate($this->Book->parseCriteria($this->passedArgs)));'
instead of
$this->set('books', $this->paginate());'
in the controller. Not sure why though..
I am using the search plugin on https://github.com/cakedc/search. I am trying to search for records using the ID field only, that is if i type in 1 on my form input it must show a record with user ID 1. The challenge that i am having now is when i specify that the input should be the id field, the input field for the search functionality disappears on my index view, strange thing is when i specify a different field name the input field shows.
Below is my code for my Model
public $actsAs = array('Search.Searchable');
public $filterArgs = array(
'id' => array('type' => 'like', 'field' => 'ItSupportRequest.id'),
);
public function findByTags($data = array()) {
$this->Tagged->Behaviors->attach('Containable', array('autoFields' => false));
$this->Tagged->Behaviors->attach('Search.Searchable');
$query = $this->Tagged->getQuery('all', array(
'conditions' => array('Tag.name' => $data['tags']),
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
}
public function orConditions($data = array()) {
$filter = $data['filter'];
$cond = array(
'OR' => array(
$this->alias . '.id LIKE' => '%' . $filter . '%',
));
return $cond;
}
and here is my controller code.
public $components = array('Search.Prg');
public $presetVars = true; // using the model configuration
public function find() {
$this->Prg->commonProcess();
$this->paginate['conditions'] = $this->ItSupportRequest->parseCriteria($this->passedArgs);
$this->set('articles', $this->paginate());
}
and in my index.ctp file i have this code.
<?php
echo $this->Form->create('ItSupportRequest', array(
'url' => array_merge(array('action' => 'find'), $this->params['pass'])
));
echo $this->Form->label('Query ID:') . "<br/>";
echo $this->Form->input('name', array('div' => false, 'label' => false));
echo $this->Form->submit(__('Search'), array('div' => false));
echo $this->Form->end();
?>
Thanks in advance.
You should not call it id as id will be hidden (because cake assumes this is a primary key here).
Either name it something else or manually overwrite this behavior using
$this->Form->input('id', array('type' => 'text'));
But I would go with sth like "search" and
$this->Form->input('search', array('placeholder' => 'ID to look for'));
and
public $filterArgs = array(
'search' => array('type' => 'like', 'field' => 'ItSupportRequest.id'),
);
How can I keep the same checkboxes checked after submit? All the other input fields on the form automatically keeps the values. I thought this would also go for checkboxes, but nope.
echo $this->Form->input('type_id', array(
'multiple' => 'checkbox',
'options' => array(
'1' => 'Til salgs',
'2' => 'Ønskes kjøpt',
'3' => 'Gis bort'
),
'div' => false,
'label' => false
));
I believe this can be done in the controller, but how?
Edit:
Since I posted this question I've changed to CakeDcs Search plugin, because I've gotten this to work with that before. Still... I can't get it to work this time.
Adding model and controller code:
AppController
public $components = array('DebugKit.Toolbar',
'Session',
'Auth' => array(
'loginAction' => '/',
'loginRedirect' => '/login',
'logoutRedirect' => '/',
'authError' => 'Du må logge inn for å vise denne siden.',
'authorize' => array('Controller'),
),
'Search.Prg'
);
public $presetVars = true; //Same as in model filterArgs(). For Search-plugin.
AdsController
public function view() {
$this->set('title_for_layout', 'Localtrade Norway');
$this->set('show_searchbar', true); //Shows searchbar div in view
$this->log($this->request->data, 'debug');
//Setting users home commune as default filter when the form is not submitted.
$default_filter = array(
'Ad.commune_id' => $this->Auth->user('User.commune_id')
);
$this->Prg->commonProcess(); //Search-plugin
$this->paginate = array(
'conditions' => array_merge($default_filter, $this->Ad->parseCriteria($this->passedArgs)), //If Ad.commune_id is empty in second array, then the first will be used.
'fields' => $this->Ad->setFields(),
'limit' => 3
);
$this->set('res', $this->paginate());
}
Model
public $actsAs = array('Search.Searchable');
public $filterArgs = array(
'search_field' => array('type' => 'query', 'method' => 'filterSearchField'),
'commune_id' => array('type' => 'value'),
'type_id' => array('type' => 'int')
);
public function filterSearchField($data) {
if (empty($data['search_field'])) {
return array();
}
$str_filter = '%' . $data['search_field'] . '%';
return array(
'OR' => array(
$this->alias . '.title LIKE' => $str_filter,
$this->alias . '.description LIKE' => $str_filter,
)
);
}
/**
* Sets the fields which will be returned by the search.
*
* #access public
* #return array Database table fields
* #author Morten Flydahl
*
*/
public function setFields() {
return array(
'Ad.id',
'Ad.title',
'Ad.description',
'Ad.price',
'Ad.modified',
'User.id',
'User.first_name',
'User.middle_name',
'User.last_name',
'User.link',
'User.picture_url',
'Commune.name',
'Type.id',
'Type.name'
);
}
You have to set manually the selected option of the input, as an array with "keys = values = intval(checkbox id)"
I cannot explain why this format, but this is the only way I get it to work.
Here is my code:
echo $this->Form->create('User');
// Read the submitted value
$selected = $this->Form->value('User.Albums');
// Formats the value
if (empty($selected)) {
$selected = array(); // avoid mess
} else {
$selected = array_map('intval', $selected);
$selected = array_combine ($selected, $selected);
}
// Renders the checkboxes
echo $this->Form->input('Albums',array(
'type' => 'select',
'multiple' => 'checkbox',
'options' => $albums, // array ( (int)id => string(label), ... )
'selected' => $selected, // array ( (int)id => (int)id, ... )
));
Hope this helps.
++
For some reason i'm really having a hard time wrapping my head around HABTM associations. I learn best by watching someone do something and explaining why. Anyways, I have 2 tables I want associated, Drugs, and SideEffects. I've created the intermediate table drugs_side_effects(has no data right now). Does cake put the data in that automatically or do I need to do something? The 3.7.6.5 hasAndBelongsToMany (HABTM) from the book didn't specify.
I've set up the models correctly(I think) and am not sure how to proceed at this point. It seems pretty simple. I need to display side_effect from the SideEffects table in a Drug view. I think in the edit_french controller function i'll need something like
$side_effect = $this->Drug->SideEffect->read(
array('SideEffect.id','SideEffect.side_effect'), $id);
$this->set('side_effect',$side_effect);
but I feel like that won't work as expected. Or maybe there's a more efficient way? Any advice or help is appreciated.
Drug Model:
var $hasAndBelongsToMany = array(
'SideEffect' => array(
'className' => 'SideEffect',
'joinTable' => 'drug_side_effects',
'foreignKey' => 'drug_id',
'associationForeignKey' => 'side_effect_id'
)
);
}
?>
SideEffect Model:
var $hasAndBelongsToMany = array(
'Drug' => array(
'className' => 'Drug',
'joinTable' => 'drug_side_effects',
'foreignKey' => 'side_effect_id',
'associationForeignKey' => 'drug_id'
)
);
}
?>
Drugs Controller:
<?php
class DrugsController extends AppController {
var $name = 'Drugs';
var $helpers = array('Html','Form','Paginator');
var $paginate = array(
//'contain' => array('SideEffect'),
//'fields' => array('Drug.id', 'Drug.generic'),
'fields' => array('Drug.id', 'Drug.generic','Drug.date_altered'),
'limit' => 50,
'order' => array(
'Drug.generic' => 'asc'
)
);
function index() {
$data = $this->paginate('Drug');
$alldrugs = $this->set('drugs', $this->Drug->find('all'));
$this->set('drugs', $data);
$this->set('alldrugs', $data);
//$this->set('lessdrugs', $this->paginate());
$this->set('title_for_layout','List of all current drugs');
}
function edit_french($id = null) {
$this->Drug->id = $id;
$drug = $this->Drug->read(
array(
'Drug.id','Drug.generic','Drug.ahl','Drug.aap','Drug.rid','Drug.oral','Drug.mw','Drug.clinical_recommendations'
),
$id
);
$this->set('title_for_layout', 'Translate clinical recommendations - ' . $drug['Drug']['generic']);
$this->set('drug',$drug);
if (empty($this->data)) {
$this->data = $this->Drug->read();
} else {
if ($this->Drug->save($this->data)) {
$this->Session->setFlash('The drug has been updated.');
$this->redirect(array('action' => 'index'));
}
}
}
}
?>
You can pull the related data using ContainableBehavior. To do so, simply run a find on the Drug model and tell it to contain the associated SideEffect data.
$drug = $this->Drug->find('first', array(
'conditions' => array(
'Drug.id' => $id
),
'contain' => array(
'SideEffect'
)
));
You can also set the contain before using read() if you prefer.
$this->Drug->contain(array('SideEffect'));
$drug = $this->Drug->read(null, $id);
Using Containable allows you to gather all associated data in a single find() request.
I have several models that all share one fields (say name) but differ in other fields. Is there any wan to find all of ModelX ModelY and ModelZ that have name 'foo'. Thanks for any suggestions.
Something like this ?
$models = array('Post', 'Comment', 'Article');
$results = array();
foreach($models as $model) {
$this->loadModel($model);
$results[$model] = $this->{$model}->find('all', array(
'conditions' => array(
$model'.name' => $name
)
));
}
If the they are related you should be able to specify the Model followed by '.' within the find.
$this->find('all', array(
'conditions' => array(
'OR' => array(
'Article.field' => 'foo',
'Comment.field' => 'foo',
'Post.field' => 'foo',
)
),
'fields' => array('Article.field', 'Comment.field', 'Post.field')
));