Forming find conditions from multi word search query in CakePHP - search

I have products with multiple fields than can be searched (name, text, product code, etc).
What I want to do is explode the search string and make sure that every word is found somewhere at the record.
With one word I would normally do like:
'OR'=>array(
'name LIKE'=>'%' . $oneword . '%',
'text LIKE'=>'%' . $oneword . '%',
'code LIKE'=>'%' . $oneword . '%',
)
Now I feel that I have to make as many OR-arrays as search string has words and include them all in AND-array. I just don't know how to code it.

Something like this should do it
$conditions = array();
$search_terms = explode(' ', $search_string);
foreach($search_terms as $search_term){
$conditions[] = array('Model.name Like' =>'%'.$search_term.'%');
$conditions[] = array('Model.text Like' =>'%'.$search_term.'%');
$conditions[] = array('Model.code Like' =>'%'.$search_term.'%');
}
$products = $this->paginate('Product', array('conditions' => array('OR' => $conditions)));
Edit: If all your search term must be present in any of the fields, it would be something like this:
$conditions = array();
$search_terms = explode(' ', $search_string);
foreach($search_terms as $search_term){
$conditions[] = array('OR' => array('Model.name Like' =>'%'.$search_term.'%',
'Model.text Like' =>'%'.$search_term.'%',
'Model.code Like' =>'%'.$search_term.'%',
)
);
}
$products = $this->paginate('Product', $conditions);

$search_terms = explode(' ', $search_text);
foreach ($search_terms as $search_term)
{
$conditions[] = ['OR' => [
'cohortes.titre LIKE' => '%' . $search_term . '%',
'cohortes.code LIKE' => '%' . $search_term . '%',
],
];
}

Related

I want to de-spaghettify this function

How can I make this function better? I don't like the fact I have to use a forEach to define manipulable content. I was thinking of running a reduce on find, but It's all too confusing for me.
_replace (content, find, replace, delimiter = '', findDelimiter = '') {
if (!Array.isArray(find)) find = [find]
let result = content
find.forEach(ref => {
result = result.split(findDelimiter).reduce((a, e) => a + delimiter + (this._reference[replace][this._reference[ref].indexOf(e)] || e), '')
})
return result
}

cakephp pagination with AND OR condition inside loop

I am working with cakephp pagination i am using 2 group of check box for filter materialtype and occasion so i wrote following code
public function index($category) {
if(!empty($this->params['url']['data']['filter']['materialtype']))
{
foreach ($this->params['url']['data']['filter']['materialtype'] as $v){
$conditions1[] ="(Product.materialtype LIKE '%$v%')";
}
$conditions[] = implode(' OR ', $conditions1);
}
if(!empty($this->params['url']['data']['filter']['occasion']))
{
foreach ($this->params['url']['data']['filter']['occasion'] as $v){
$conditions2[] ="`Product`.`occasion` LIKE '%$v%'";
}
$conditions[] = implode(' OR ', $conditions2);
}
}
My following code is generating this sql query
SELECT `Product`.`id`, `Product`.`category`, `Product`.`name`, FROM mydb`.`products` AS `Product` WHERE `Product`.`category` = 'Necklace' AND (`Product`.`materialtype` LIKE '%Yellow Gold%') OR (`Product`.`materialtype` LIKE '%White Gold%') AND (`Product`.`occasion` LIKE '%Traditional%') OR (`Product`.`occasion` LIKE '%Modern%')
But I am looking for this output.
SELECT `Product`.`id`, `Product`.`category`, `Product`.`name`, FROM mydb`.`products` AS `Product` WHERE `Product`.`category` = 'Necklace' AND ((`Product`.`materialtype` LIKE '%Yellow Gold%') OR (`Product`.`materialtype` LIKE '%White Gold%')) AND ((`Product`.`occasion` LIKE '%Traditional%') OR (`Product`.`occasion` LIKE '%Modern%'))
You need to remove inner bracket from the inner statement inside the foreach. and add it at with outside like below.
if(!empty($this->params['url']['data']['filter']['materialtype']))
{
foreach ($this->params['url']['data']['filter']['materialtype'] as $v){
$conditions1[] = "Product.materialtype LIKE '%$v%'";
}
$str_cond = implode(' OR ', $conditions1);
$conditions[] = '(' . $str_cond . ')';
}
if(!empty($this->params['url']['data']['filter']['occasion']))
{
foreach ($this->params['url']['data']['filter']['occasion'] as $v){
$conditions2[] = "Product.occasion LIKE '%$v%'";
}
$str_cond = implode(' OR ', $conditions2);
$conditions[] = '(' . $str_cond . ')';
}

Extending Drupal 7 search

I want to extend default Drupal 7 node search with one additional field.
I alter search form with the following new field:
function mymodule_form_search_form_alter(&$form, &$form_state, $form_id) {
$form['basic']['site'] = array(
'#type' => 'select',
'#options' => array(
'KEY1' => 'TITLE1',
'KEY2' => 'TITLE2',
'KEY3' => 'TITLE3'
)
);
}
I have a field called field_data_field_site.field_site_value which i need to use as a filter in this search.
I've tried to read about hook_search_* functions but didn't get the idea.
My question is the following. How can I extend search form? Anyone have live examples?
The following is the best way I solve this problem.
First of all I need to alter Drupal's search block and search form with my field and define new submit function.
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_search_block_form_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = 'search_form_alter_submit';
$form['site'] = array(
'#type' => 'select',
'#options' => _options(),
'#default_value' => (($_GET['site']) ? $_GET['site'] : '')
);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_search_form_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = 'search_form_alter_submit';
$form['basic']['site'] = array(
'#type' => 'select',
'#options' => _options(),
'#default_value' => (($_GET['site']) ? $_GET['site'] : '')
);
}
function _options() {
return array(
'' => 'Select site',
'site-1' => 'Site 1',
'site-2' => 'Site 2'
);
}
Submit function will forward us to default search/node page but with our query. Page would look like search/node/Our-query-string?site=Our-option-selected.
function search_form_alter_submit($form, &$form_state) {
$path = $form_state['redirect'];
$options = array(
'query' => array(
'site' => $form_state['values']['site']
)
);
drupal_goto($path, $options);
}
Next step is to use hook_search_info (Don't forget to turn it on and set as default on admin/config/search/settings page).
/**
* Implements hook_search_info().
*/
function mymodule_search_info() {
return array(
'title' => 'Content',
'path' => 'node',
'conditions_callback' => '_conditions_callback',
);
}
Conditions callback function defined in hook_search_info. We need to provide additional queries to our search.
function _conditions_callback($keys) {
$conditions = array();
if (!empty($_REQUEST['site'])) {
$conditions['site'] = $_REQUEST['site'];
}
return $conditions;
}
Finally, hook_search_execute will filter our content by our query. I used default code from this hook with modifications I need.
/**
* Implements hook_search_execute().
*/
function mymodule_search_execute($keys = NULL, $conditions = NULL) {
// Build matching conditions
$query = db_select('search_index', 'i', array('target' => 'slave'))
->extend('SearchQuery')
->extend('PagerDefault');
$query->join('node', 'n', 'n.nid = i.sid');
// Here goes my filter where I joined another table and
// filter by required field
$site = (isset($conditions['site'])) ? $conditions['site'] : NULL;
if ($site) {
$query->leftJoin('field_data_field_site', 's', 's.entity_id = i.sid');
$query->condition('s.field_site_value', $site);
}
// End of my filter
$query
->condition('n.status', 1)
->addTag('node_access')
->searchExpression($keys, 'node');
// Insert special keywords.
$query->setOption('type', 'n.type');
$query->setOption('language', 'n.language');
if ($query->setOption('term', 'ti.tid')) {
$query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
}
// Only continue if the first pass query matches.
if (!$query->executeFirstPass()) {
return array();
}
// Add the ranking expressions.
_node_rankings($query);
// Load results.
$find = $query
->limit(10)
->execute();
$results = array();
foreach ($find as $item) {
// Build the node body.
$node = node_load($item->sid);
node_build_content($node, 'search_result');
$node->body = drupal_render($node->content);
// Fetch comments for snippet.
$node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
// Fetch terms for snippet.
$node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
$extra = module_invoke_all('node_search_result', $node);
$results[] = array(
'link' => url("node/{$item->sid}", array('absolute' => TRUE)),
'type' => check_plain(node_type_get_name($node)),
'title' => $node->title,
'user' => theme('username', array('account' => $node)),
'date' => $node->changed,
'node' => $node,
'extra' => $extra,
'score' => $item->calculated_score,
'snippet' => search_excerpt($keys, $node->body)
);
}
return $results;
}
I'd be happy if anyone would give me a better answer.

How to link custom results in Drupal

My first time here an a newbee in Drupal and programming .
So I have a problem I need to some help with.
function query_results($searchstring, $datefrom) {
$tidresult = db_query("SELECT tid FROM {term_data} WHERE LOWER(name) = '%s'", strtolower($searchstring));
$resultarray = array();
while ($obj = db_fetch_object($tidresult)) {
$tid = $obj->tid;
$noderesults = db_query("SELECT n.nid, n.title FROM {node} n
INNER JOIN {term_node} tn ON tn.nid = n.nid
WHERE tn.tid='%s'", $tid);
while ($nodeobj = db_fetch_object($noderesults)) {
$resultarray[$nodeobj->nid] = $nodeobj->title;
}
}
$header = array(
array('data' => 'Nr.'),
array('data' => 'Name'),
);
$rows = array();
$i = 0;
foreach($resultarray as $nid => $title) {
$i++;
$rows[] = array('data' =>
array(
$i,
$title,
),
);
}
$output = theme('table', $header, $rows);
print theme("page", $output);
}
It's driving me crazy , i dint put all of the search code but it takes taxonomy tags from the database ( you type in textbox that has autocomplete, '$searchstring' ) and date ( you choose a time line like one day , yesterday ect. , '$datefrom').
For example reasons lets say it looks like this example when you click search.
I can't post my one pictures but I just gives me the titles ( like above but the are not listed) that I cannot click to lead me to the content.
But I wont it to look like result that is like content ( story ) so you have a clickable Title and some description , like this click to see example
where it says lorem ipsum and that text belowe.
If it is hard to make like in the picture can someone show me just how to make( like in the first picture) the results that are non clickable titles into clickable links that lead me to the content.
To get linked titles you need to use the l() function.
looking at the code you provided, I am not entirely sure how you are getting any results since you save the titles in $resultArray but use $rows when rendering the table.
Unless, $rows is specified somewhere else, $resultarray[$nodeobj->nid] = $nodeobj->title; should become $rows[$nodeobj->nid] = $nodeobj->title;
To make it match your table header, you need to add another 'cell' for the number column
$rows[$nodeobj->nid] = array(
$count++,
l($nodeobj->title, 'node/'.$nodeobj->nid)
);
To provide the excerpt too, you need to join the node_revisions table and get either the body or teaser column, then add it to your rows like this:
$rows[$nodeobj->nid] = array(
$count++,
'<h2>'. l($nodeobj->title, 'node/'.$nodeobj->nid) .'</h2>'. $nodeobj->teaser
);
assuming you get the teaser.
EDIT
the previous answer still holds. You can also simplify the code a bit by processing $rows straight in the $noderesults loop.
function query_results($searchstring, $datefrom) {
$tidresult = db_query("SELECT tid FROM {term_data} WHERE LOWER(name) = '%s'", strtolower($searchstring));
$rows = array();
$count = 0;
while ($obj = db_fetch_object($tidresult)) {
$tid = $obj->tid;
$noderesults = db_query("SELECT n.nid, n.title FROM {node} n "
."INNER JOIN {term_node} tn ON tn.nid = n.nid "
."WHERE tn.tid='%s'", $tid);
while ($nodeobj = db_fetch_object($noderesults)) {
$rows[] = array(
++$count,
l($nodeobj->title, 'node/'. $nodeobj->title)
);
}
}
$header = array(
array('data' => 'Nr.'),
array('data' => 'Name'),
);
$output = theme('table', $header, $rows);
print theme("page", $output);
}
-OR-
move it all in one query (note: I did not get a chance to test this, but I usually get it right the first time):
function query_results($searchstring, $datefrom) {
$rows = array();
$count = 0;
$results = db_query("SELECT n.nid, n.title
FROM {node} n
INNER JOIN {term_node} tn ON tn.nid = n.nid
WHERE tn.tid IN (SELECT tid FROM {term_data} WHERE LOWER(name) = '%s')", strtolower($searchstring));
while ($nodeobj = db_fetch_object($results)) {
$rows[] = array(
++$count,
l($nodeobj->title, 'node/'. $nodeobj->title)
);
}
$header = array(
array('data' => 'Nr.'),
array('data' => 'Name'),
);
$output = theme('table', $header, $rows);
print theme("page", $output);
}

Drupal hook_search function location

I can't for the life of me figure out where the hook_search function in drupal is located. Is it something I need to add to a file to access?
Hook functions don't exist by name -- they indicate a naming convention that can be followed to respond to that particular "hook"...
An example would be the node_search() function. When the search module calls module_invoke_all('search'), all functions named foo_search(), where foo is the name of an enabled module, will be called. The details of the search hook in particular are found on api.drupal.org.
function hook_search($op = 'search', $keys = null) {
switch ($op) {
case 'name':
return t('content');
case 'reset':
variable_del('node_cron_last');
return;
case 'search':
$find = do_search($keys, 'node', 'INNER JOIN {node} n ON n.nid = i.sid '. node_access_join_sql() .' INNER JOIN {users} u ON n.uid = u.uid', 'n.status = 1 AND '. node_access_where_sql());
$results = array();
foreach ($find as $item) {
$node = node_load(array('nid' => $item));
$extra = node_invoke_nodeapi($node, 'search result');
$results[] = array('link' => url('node/'. $item),
'type' => node_invoke($node, 'node_name'),
'title' => $node->title,
'user' => theme('username', $node),
'date' => $node->changed,
'extra' => $extra,
'snippet' => search_excerpt($keys, check_output($node->body, $node->format)));
}
return $results;
}
}

Resources