Reputation: 409
I'm trying to search with a keyword but only in the rows where business_type is manufacturer but it's not working and it's getting all the rows. This is the method in my model:
public function search($keyword) {
$this->db->like('category',$keyword);
$this->db->or_like('keyword',$keyword);
$this->db->or_like('products_deals_with',$keyword);
$this->db->or_like('buisness_name',$keyword);
$params['conditions'] = array(
'business_type' => 'manufacturer'
);
$query = $this->db->get_where('business_listings', $params['conditions']);
return $query->result_array();
}
Generated query is:
SELECT * FROM `business_listings`
WHERE `category` LIKE '%%' ESCAPE '!'
OR `keyword` LIKE '%%' ESCAPE '!'
OR `products_deals_with`LIKE '%%' ESCAPE '!'
OR `buisness_name` LIKE '%%' ESCAPE '!'
AND `business_type` = 'manufacturer'
Upvotes: 3
Views: 15766
Reputation: 48070
In one of my projects (running CI 3.1.11), I noticed the following wrapper methods for the protected _like()
method in the query builder class:
like()
not_like()
or_like()
or_not_like()
All of these wrapper methods have the exact same method signature:
mixed $field,
string $match = '',
string $side = 'both',
?bool $escape = null
See some online examples of the query building method structures at rhivent/master_codeigniter2 and sanchit237/codeigniter-3.1.11
Pay particular attention to the first parameter -- $field
is mixed
(not merely a string). Actually, the parameter could be more explicitly/meaningfully typed as string|array
. The starting portion of the internal method body looks like this:
protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL)
{
if (!is_array($field))
{
$field = array($field => $match);
}
This means that if the first parameter is an array (an associative array is expected), it will be iterated to build a dynamic number of LIKE conditions. In such a case, the second parameter is completely ignored.
If additional conditions will be involved, grouping of the OR conjugated LIKE conditions is necessary.
This discovery reveals that searching for rows which contain a nominated string in one of any nominated columns can be done with the following minimized script:
public function search(string $keyword): array
{
return $this->db
->group_start()
->or_like(
array_fill_keys(
['category', 'keyword', 'products_deals_with', 'buisness_name'],
$keyword
)
)
->group_end()
->get_where(
'business_listings',
['business_type' => 'manufacturer']
)
->result_array();
}
The built SQL (quoting may vary based on your db dialect / configuration; I've adjusted the formatting for readability):
SELECT *
FROM "business_listings"
WHERE (
"category" LIKE '%foo%' ESCAPE '!'
OR "keyword" LIKE '%foo%' ESCAPE '!'
OR "products_deals_with" LIKE '%foo%' ESCAPE '!'
OR "buisness_name" LIKE '%foo%' ESCAPE '!'
)
AND "business_type" = 'manufacturer'
If there were no additional WHERE conditions, then the group_start()
and group_end()
calls could be omitted.
There is no need for a select()
call because when omitted, the default rendering is SELECT *
.
There is no from()
call because the table declaration is made as the first parameter of get_where()
.
Due to the impossibility of duplicate keys in one level of an array, I assume the only way to use a column more than once would be "trickery" like adding irrelevant parentheses.
[
'category' => 'foo',
'(category)' => 'bar',
'((category))' => 'buzz'
]
The _like()
method is capable of suitably omitting identifier quoting when the column/key declaration is something like REPLACE(category, '''', '')
In my CI project (PDO & PostGres), there are dialect-specific customizations to implement ILIKE
comparisons for case-insensitive matching -- these calls often require the appending of a collation after each non-deterministic column (COLLATE "en-AU-x-icu"
)."{$prefix} {$k} {$not} ILIKE {$v} COLLATE \"en-AU-x-icu\""
Upvotes: 0
Reputation: 11
Just add $this->db->group_start();
and $this->db->group_end();
$this->db->group_start();
$this->db->like('category',$keyword);
$this->db->or_like('keyword',$keyword);
$this->db->or_like('products_deals_with',$keyword);
$this->db->or_like('buisness_name',$keyword);
$this->db->group_end();
$params['conditions'] = array(
'business_type' => 'manufacturer'
);
$query = $this->db->get_where('business_listings', $params['conditions']);
return $query->result_array();
Upvotes: 1
Reputation: 409
I've found the solution. I've to use $this->db->group_start(); and $this->db->group_end();
public function search($keyword) {
$this->db->select('*');
$this->db->where("business_type = 'manufacturer'");
$this->db->group_start();
$this->db->like('category',$keyword);
$this->db->or_like('keyword',$keyword);
$this->db->or_like('products_deals_with',$keyword);
$this->db->or_like('buisness_name',$keyword);
$this->db->group_end();
$query = $this->db->get('business_listings');
// echo $this->db->last_query();
return $query->result_array();
}
Generated Query:
SELECT * FROM `business_listings`
WHERE `business_type` = 'manufacturer'
AND (
`category` LIKE '%%' ESCAPE '!'
OR `keyword` LIKE '%%' ESCAPE '!'
OR `products_deals_with` LIKE '%%' ESCAPE '!'
OR `buisness_name` LIKE '%%' ESCAPE '!' )
Upvotes: 16