I am coding a search module. I do not know if I am ina good way but ti sound good for now excepted one point.
With the following code I can search only one word.
<?php
class SearchesController extends AppController{
function index(){
if($this->request->is('put') || $this->request->is('post') ){
// Save the send data
$search = $this->request->data['Search']['key'];
//$nbWords = str_word_count($search);
/*
THIS DOES NOT WORKS
if($nbWords > 1){
$searchTerms = explode(' ', $search);
$searchTermBits = array();
foreach ($searchTerms as $term) {
$term = trim($term);
if (!empty($term)) {
$searchTermBits[] = "Page.name LIKE '%$term%' AND Page.content LIKE '%$term%'";
}
}
}else{
$term = $search;
$searchTermBits[] = "Page.name LIKE '%$term%' AND Page.content LIKE '%$term%'";
}
*/
// SEARCH IN Pages TABLE
$this->loadModel('Page');
// SAVE THE RESULT IN
$d['pages'] = $this->Page->find('all', array(
'conditions' => array(
'online'=>'1',
'type'=>'page',
'created <= NOW()',
'id > 1',
'OR' => array(
//implode(' AND ', $searchTermBits)
'Page.name LIKE'=>'%'.$search.'%',
'Page.content LIKE'=>'%'.$search.'%'
)
),
'fields' => array('name','content','created','id','type')
));
//SEARCH IN Posts TABLE
$this->loadModel('Post');
$d['posts'] = $this->Post->find('all', array(
'conditions' => array(
'online'=>'1',
'type'=>'post',
'created <= NOW()',
'OR' => array(
'Post.name LIKE'=>'%'.$search.'%',
'Post.content LIKE'=>'%'.$search.'%'
)
),
'fields'=> array('name','content','created','id','type','slug')
));
// SEND THE VALUES TO THE VIEW
$this->set($d);
}else{
echo 'e';
}
}
}
The problem, I would like to be able to search with different words, for exeple "red car", "house monrain","web html5 development"
With my code, if I enter one word, it return a result, but if I add second word to the first (with a space) , it retuirn no result.
How can I change this
'OR' => array(
'Post.name LIKE'=>'%'.$search.'%',
'Post.content LIKE'=>'%'.$search.'%'
)
to make it able to search in 'name' and 'content' several words?
Thak for your help
Cheers
you can try this:
'OR' => array(
'Post.name' IN ('name1', 'name2'),
'Post.content' IN ('content1', 'content2')
)
Obviously you can put your value in array like this:
$name_arr = array();
$name_arr[] = 'name1';
$name_arr[] = 'name2';
and after:
'OR' => array(
'Post.name' IN ($name_arr),
'Post.content' IN ('content1', 'content2')
)
Change your $search variable
$search = $this->request->data['Search']['key'];
// if keywords "red car" then it will pass to your query %red%car%;
$search = str_replace(" ","%",$search);
Now it will search records having both words.
If you want to search with many words separated by a space, you can do this.
$searches = explode(" ", $search);
foreach ($searches as $search) {
//put your search condition in here
}
You might look into fulltext searching. If you setup your indices correctly, you can run queries such as:
SELECT id, MATCH (title,body) AGAINST ('Tutorial') FROM articles;
Related
please help solve one trivial task for writing results:
tables:
flats :: id, dateadd
flatparams :: id, title
flatsoptions :: id, flatid, paramid, value
SQL DOWNLOAD HERE :: http://yadi.sk/d/034so9jIDgfoz
CONTROLLER CODE DOWNLOAD HERE :: http://yadi.sk/d/CtGAGZzyDghp7
I need to find flats that are in a region, city, street, apartment - for example:
russia, moscow, papanina, 8
My code looks for all the apartments, which have at least one of these parameters (if OR), and if the AND, then no result because trying to find a field in one table flatsoptions immediately all kinds of parameters. i would like to understand how to look flat on several parameters simultaneously. Thank you!
UPDATED :: 02-12-2013 22-00
$criteria = new CDbCriteria();
$criteria->together = true;
$i = 0;
foreach ($arr as $key => $item) {
if (in_array($key, $pos)) {
$criteria->join = 'LEFT JOIN flatsoptions AS flatsoptions'.$i.' ON flatsoptions'.$i.'.flatid = t.id';
$criteria->addColumnCondition(
array(
'flatsoptions'.$i.'.paramid' => $params[$key]['id'],
'flatsoptions'.$i.'.value' => $item
),
'AND',
'OR'
);
$i++;
}
}
$flats = Flats::model()->findAll($criteria);
And this code is not working - they not find the alias 'flatsoptions0' in 'where clause';
Updated :: SOLUTION
count($keys) = count($values);
This is a Mysql solution::
$criteria = new CDbCriteria();
$criteria->select = array('flatid');
$criteria->addInCondition('paramid', $keys);
$criteria->addInCondition('value', $values);
$criteria->group = 'flatid';
$criteria->having = 'count(*) = '.count($values);
$flats = Flatsoptions::model()->findAll($criteria);
In AND case you can join flatoptions N times (how many params passed), and use the code similar to:
$criteria->addColumnCondition(
array(
'flatsoptions1.paramid' => $params[$key1]['id'],
'flatsoptions1.value' => $item1
),
'AND',
'AND'
);
$criteria->addColumnCondition(
array(
'flatsoptions2.paramid' => $params[$key2]['id'],
'flatsoptions2.value' => $item2
),
'AND',
'AND'
);
...
$criteria->addColumnCondition(
array(
'flatsoptionsN.paramid' => $params[$keyN]['id'],
'flatsoptionsN.value' => $itemN
),
'AND',
'AND'
);
This is just an example. You need first define $criteria->with dynamically() with different aliases for the same relation and then put each addColumnCondition inside the loop.
LINKS:
CDbCriteria->with()
CActiveRecord->relations()
I have a model 'Mix' which has the fields (which I'd like to be searchable) 'name' 'description' and 'tag_words', as well as a boolean field 'published'. Users should be able to enter a multi-word search term and get results back for any of their words appearing in any of the searchable fields providing the 'published' field is set to 1
So far with the help of google and reading around on here I've got:
if ($this->request->is('post')) {
$conditions = array();
$search_terms = explode(' ', $this->request->data['Mix']['search']);
foreach($search_terms as $search_term){
$conditions[] = array('Mix.name Like' =>'%'.$search_term.'%');
$conditions[] = array('Mix.description Like' =>'%'.$search_term.'%');
$conditions[] = array('Mix.tag_words Like' =>'%'.$search_term.'%');
}
$searchResults = $this->paginate('Mix', array('conditions' => array('Mix.published'=>1, 'AND'=>array('OR' => $conditions))));
}
But since it returns nothing but loads of errors I can guess it's totally wrong. I've very confused, what is the correct syntax?
You don't use the AND index, it is already implied:-
$searchResults = $this->paginate('Mix', array('conditions' => array('Mix.published'=>1, 'OR' => $conditions)));
I want to be able to search all data based on multiple or conditions using foreach like below:
function index() {
if ($this->Session->read('Auth.User.group_id') != 1) {
$commune_id = $this->Session->read('Auth.User.commune_id');
$commune_id = $this->Petition->Commune->findbyId($commune_id);
$commune_id = $this->Petition->Commune->find('all',array('conditions' => array('group' => $commune_id['Commune']['group'])));
$count = count($commune_id);
$i=1;
foreach($commune_id as $commune_ids){
if($i != $count){
$this->paginate = array(
'or' => array(
array('Petition.commune_id LIKE' => $commune_ids['Commune']['id'] . ","),
),
'limit' => 10
);
}
$i++;
}
}
$this->Petition->recursive = 0;
$petitions = $this->paginate();
$this->set('petitions', $petitions);
}
I was able to get possible number of matches but was wondering how I can use this array to get results with multiple or conditions with paginator function.
To do pagination in custom situations like this, one normally has to override Model::paginate() and Model::paginateCount() as detailed in the book under Custom Query pagination.
Alternatively, you could use a plugin that already has features such as pagination implemented, such as CakeDC's Search plugin.
Having some problems here, I think I am just overlooking something really simple...
I have a CMS that have multiple categories.
How do I create a variable or array that has the included categories groups that I want to use in my SHOW IF STATEMENT ??
So for example:
<?php
$catsrow = array(
'cat_1' => '41','46','62',
'cat_2' => '41','45','63',
'cat_3' => '41','43','65'
);
?>
<?php if
(catsrow[0] || catsrow[1] || catsrow[2]) == ($row_DetailRS1['category'])
{ echo 'do work' }
else { ?>
Thanks in advance!!
I guess what I am asking is, how do I compare an array with multiple groups inside. I need to compare different grouped categories..
Like $catsArray = ARRAY(cat_1 => '2,3,4' , cat_2 => '5,6,7' , cat_3 => '8,9,10')
if $row['cat_from_page'] == $catsArray (any of the groups) then SHOW THIS { }
????
you may need to explode the parts of the array
Kinda like $parts = explode(',' , $cat);
http://php.net/manual/en/function.explode.php
but you will need to implode the whole thing into one array
Like implode (',', $parts);
http://php.net/manual/en/function.implode.php
My Best guess at what you're trying to do:
$categories = array(
'cat_1' => array(
'41','46','62'
),
'cat_2' => array(
'41','45','63'
),
'cat_3' => array(
'41','43','65'
)
);
$row_DetailRS1['category'] = '41';
foreach($categories as $category => $items) {
foreach($items as $item) {
if($row_DetailRS1['category'] == $item) {
echo "Item: ".$item." found in Category: ".$category."\n";
}
}
}
I need to selectively flatten an array in PHP, but do it selectively. If a key matches a pattern then all sub elements below that key should be included in the 'flat' output.
SO If I had a catalogue of music:
=> array of albums => each of which is an array of song titles
Then I could search for a string, and would get a flat array in reponse. SO if I searched for 'sun' then I would get the entire catalogue for any artist with 'sun' in their name, plus the albums for other artists where 'sun' was in the album name.
Hopefully that makes sense.
Anyone got any thoughts?
Is there a reason you're not using a database to store what sounds like a significant amount of info? It would be fairly simple to write a query in SQL to pull the data out that you want.
Ok, I'm going to assume your data looks like this:
$data = array(
"Bill Withers" => array (
"Lovely Day",
"Use Me",
"Ain't No Sunshine"
),
"Fleet Foxes" => array (
"Sun It Rises",
"White Winter Hymnal"
),
"Billy Joel" => array (
"Piano Man"
)
);
...and that given the input "Bill", you want the output: ["Lovely Day", "Use Me", "Ain't No Sunshine", "Piano Man"]. Here's one way you could do it.
function getSongs($data, $searchTerm) {
$output = array();
foreach ($data as $artist => $songs) {
if (stripos($artist, $searchTerm) !== false)) {
$output = array_merge($output, $songs);
}
}
return $output;
}
...I'll also assume you've got a good reason to not use a database for this.
Do you mean something like that?
$a = array(
"Sunny" => "Bla-bla1",
"Sun" => "Bla-bla2",
"Sunshine" => "Bla-bla3",
);
foreach ($a as $k => $v)
{
// check here whenever you want for $k
// apply something to $v
if ( ... )
$b[$i++] = $v;
}
$sample_data = array(
'artist_name' => array(
'album_name' => array(
'song_name',
'other_mp3_id_info'
)
)
);
function s2($needle,$haystack) {
$threads = array();
foreach ($haystack as $artist_name => $albums) {
if (stripos($artist_name, $needle) !== false)) {
$threads[] = $haystack[$artist_name]; //Add all artist's albums
} else {
foreach ($albums as $album_name => $songs) {
if (stripos($album_name, $needle) !== false)) {
$threads[$artist_name][] = $haystack[$album_name]; //add matching album
}
}
}
}
return $threads;
}
To build off NickF's work, assuming your data looks like his, I'd write the function this way.
function getSongs($data, $searchTerm) {
foreach ($data as $artist => $songs) {
if (stripos($artist, $searchTerm) !== false)) {
$output[$artist] = $songs;
}
}
return $output or null;
}
The results of this function, when no matches are found will obviously return null instead of a blank array; when several matches are found they will then be grouped by their artist. I find direct assignment, $output[$artist] = $songs, to provide more predictable results than array_merge in my own experience. (This also preserves the artist for outputting that data.)
Like NickF said, I would assume you've good reason to not do this with a database? SQL for this would be very simple, such as,
SELECT artist, song FROM songs WHERE artist LIKE '%Bill%' GROUP BY artist;
You can use preg_grep for the searches, be sure to sanitize the input, though:
$matchingArtists = preg_grep('/' . preg_quote($searchString) . '/', array_keys($data));
$matchingSongs = array();
foreach ($data as $artist => $songs) {
$matchingSongs = array_merge($matchingSongs, preg_grep('/' . preg_quote($searchString) . '/', $songs));
}