This Dynamic Query code, where is the problem(php)? - php

I have this codes for Dynamic Query on a search box but i don't have any result for a search word(this word must return several records) while i have output for string($q),appreciate that help me for finding the problem.
<?php
error_reporting(E_ALL);
//include("../mahal/mahal.php");
include("../mahal/mahalmag.php");
$textfieldstring = mysql_real_escape_string($_REQUEST['Textfield1']);
$connmag = mysql_connect($dbhostmag, $dbusermag, $dbpassmag) or die ('Error connecting to Database,this is maybe due to heavy traffic,please refresh your page.');
$dbname = 'magazine';
mysql_select_db("$dbname");
mysql_query('SET NAMES utf8',$connmag); //for utf charachter
$q = strip_tags(trim($textfieldstring));
if($q) {
$search_words = explode(' ', strtolower($q));
for($i = 0; $i < count($search_words); $i++) {
if($i) {
$str_search .= "OR title LIKE '%".mysql_real_escape_string($search_words[$i])."%' OR articlebody LIKE '%".mysql_real_escape_string($search_words[$i])."%' ";
} else {
$str_search = "OR title LIKE '%".mysql_real_escape_string($search_words[$i])."%' OR articlebody LIKE '%".mysql_real_escape_string($search_words[$i])."%' ";
}
}
}
$search_data = "WHERE (".$str_search.")";
$query_search = "SELECT a.articleid, a.title, a.state, SUBSTR(a.articlebody,1,300) AS description1,
p.photofilename, a.deletitem, ac.catid,c.parentid,a.articlebody
FROM tbarticles a
INNER JOIN tbphotos p ON (p.articleid = a.articleid)
INNER JOIN tbarticlecategories ac ON (ac.articleid = a.articleid)
INNER JOIN tbcategories c ON (c.catid = ac.catid)
$search_data AND a.deletitem = '1'";
$result_search = mysql_query($query_search);
$num_rows = mysql_num_rows($result_search);
$query_row = mysql_fetch_assoc($result_search);
echo "$num_rows";
echo "$q";
?>
Update: the extra "OR" has been removed from the case where $i is 0.
When the search term is (for example) "cold", the generated SQL query is:
SELECT a.articleid, a.title, a.state, SUBSTR(a.articlebody,1,100) AS description1,
p.photofilename, a.deletitem, ac.catid,c.parentid,a.articlebody
FROM tbarticles a
INNER JOIN tbphotos p ON (p.articleid = a.articleid)
INNER JOIN tbarticlecategories ac ON (ac.articleid = a.articleid)
INNER JOIN tbcategories c ON (c.catid = ac.catid)
WHERE ( title LIKE '%cold%' OR articlebody LIKE '%cold%' )
AND a.deletitem = '1'
With a search term of "cold play", the query is:
SELECT a.articleid, a.title, a.state, SUBSTR(a.articlebody,1,100) AS description1,
p.photofilename, a.deletitem, ac.catid,c.parentid,a.articlebody
FROM tbarticles a
INNER JOIN tbphotos p ON (p.articleid = a.articleid)
INNER JOIN tbarticlecategories ac ON (ac.articleid = a.articleid)
INNER JOIN tbcategories c ON (c.catid = ac.catid)
WHERE ( title LIKE '%cold%' OR articlebody LIKE '%cold%'
OR title LIKE '%play%' OR articlebody LIKE '%play%' )
AND a.deletitem = '1' but lot of records are not related to search word

You forgot to strip the first OR from $str_search
$search_data = "WHERE (".$str_search.")";
The code should have been
$str_search = '';
for($i = 0; $i < count($search_words); $i++) {
if($i) { $str_search .= " OR "; }
$str_search .= " title LIKE '%".mysql_real_escape_string($search_words[$i])."%' OR articlebody LIKE '%".mysql_real_escape_string($search_words[$i])."%' ";
}
To make it match multiple $search_words where all words must exist in a single articlebody or a single title, then
$str_search_article = '';
$str_search_title = '';
for($i = 0; $i < count($search_words); $i++) {
if($i) {
$str_search_article .= " AND ";
$str_search_title .= " AND ";
}
$search_word = "'%".mysql_real_escape_string($search_words[$i])."%'";
$str_search_title .= " title LIKE " . $search_word;
$str_search_article .= " articlebody LIKE " . $search_word;
}
$search_data = "WHERE ((".$str_search_title.") OR (".$str_search_article."))";

Related

Array from Form Input - Select Statement MySQLi Parameterisation

Turning phrases entered in a Form input into an array to pass into a MySQL select statement where clause using MySQLi. The php code I have achieves this, but I can't workout how to parameterise the query to prevent against sql injection attacks. I've had a look at a few questions on this site, but I'm struggling to relate it to my code.
if(!empty($_POST['Message']))
{
$searchStr = get_post($con,'Message');
$aKeyword = explode(" ", $searchStr);
$query ="SELECT m.ID, m.MessageText FROM MessageMain m LEFT OUTER JOIN Likes l on m.ID = l.PostID WHERE MessageText LIKE '%" . $aKeyword[0] . "%'";
for($i = 1; $i < count($aKeyword); $i++) {
if(!empty($aKeyword[$i])) {
$query .= " OR MessageText like '%" . $aKeyword[$i] . "%'";
}
}
$query .= " GROUP BY m.ID, m.MessageText ORDER BY count(m.id) desc";
$result = $con->query($query);
$rowcount=mysqli_num_rows($result);
If you would like to build the WHERE clause dynamically based on the number of keywords to match you could do it like this:
if (!empty($_POST['Message'])) {
$searchStr = get_post($con, 'Message');
$aKeyword = explode(" ", $searchStr);
$whereClauseArr = [];
foreach ($aKeyword as $keyword) {
if ($keyword) {
$whereClauseArr[] = "MessageText LIKE ?";
$whereValues[] = '%'.$keyword.'%';
}
}
$stmt = $con->prepare(
'SELECT m.ID, m.MessageText
FROM MessageMain m
LEFT OUTER JOIN Likes l on m.ID = l.PostID
WHERE '.implode(' OR ', $whereClauseArr).'
GROUP BY m.ID, m.MessageText ORDER BY count(m.id) desc'
);
$stmt->bind_param(str_repeat('s', count($whereValues)), ...$whereValues);
$stmt->execute();
$result = $stmt->get_result();
}
Although in your case, checking the same column against multiple values would probably be better done with regular expression. This would make your query simpler and potentially also faster depending on the number of keywords you have.
if (!empty($_POST['Message'])) {
$searchStr = get_post($con, 'Message');
$aKeyword = explode(" ", $searchStr);
$aKeyword = array_filter($aKeyword); // Remove empty values
$stmt = $con->prepare(
'SELECT m.ID, m.MessageText
FROM MessageMain m
LEFT OUTER JOIN Likes l on m.ID = l.PostID
WHERE MessageText REGEXP ?
GROUP BY m.ID, m.MessageText ORDER BY count(m.id) desc'
);
$regexString = implode('|', $aKeyword);
$stmt->bind_param('s', $regexString);
$stmt->execute();
$result = $stmt->get_result();
}

MYSQL alias in where clause

i have a query and i want to filter the result set using an ALIASED column in my WHERE condition, ive read that you cannnot do it but i dont have yet an alternative way to do it efficiently since im working on thousands of records.
$str = "SELECT
mainclass.id AS classid,
mainclass.code AS classcode,
Sum(CASE WHEN enroll.acctok = '1' AND enroll.assessed = '1'
THEN enroll.assessed
ELSE 0 end) AS assessedinclass,
Sum(enroll.validated = '1') AS validatedinclass,
section.name AS sectionname,
subject.code AS subcode,
subject.subdesc,
mainclass.units,
sched.name AS schedule,
mainclass.tutorial,
mainclass.dissolved,
mainclass.slots,
mainclass.ismother,
mergeclass.code AS mothercode,
mergeclass.id AS mothercodeid,
mergeclass.slots AS mothercodeslots,
mainclass.mergein,
mainclass.inst,
instructor.lname,
instructor.fname,
instructor.mname,
instructor.suffix
FROM
class AS mainclass
Left Join enrolldet ON mainclass.id = enrolldet.class
Left Join enroll ON enrolldet.enrollno = enroll.enrollno
Inner Join period ON mainclass.period = period.id
Inner Join section ON mainclass.section = section.id
Inner Join subject ON mainclass.subject = subject.id
Left Join sched ON mainclass.sched = sched.id
Left Join class AS mergeclass ON mainclass.mergein = mergeclass.id
Left Join instructor ON mainclass.inst = instructor.userid
Left Join course ON section.course = course.id
WHERE
(period.id = :period OR period.code = :period)";
if($level != ''){
$str .= " AND course.level = '".$level."'";
}
if($dept != ''){
$str .= " AND course.dept = '".$dept."'";
}
if($display != ''){
switch ($display) {
case 'open':
$str .= " AND mainclass.slots > assessedinclass";
break;
case 'dissolved':
$str .= " AND mainclass.dissolved = 1";
break;
case 'tutorial':
$str .= " AND mainclass.tutorial = 1";
break;
case 'closed':
$str .= " AND mainclass.slots <= assessedinclass";
break;
}
}
$str .= "GROUP BY
mainclass.id,
mainclass.code";
$str .= " ORDER BY subject.code, mainclass.id";
return $this->_db->select($str,array(':period' => $period));
im trying to filter it using the ASSESSEDINCLASS WHERE mainclass.slots > ASSESSEDINCLASS means the subject is OPEN
while if mainclass.slots <= ASSESSEDINCLASS it will be considered CLOSED
You can try below. Write an outer select which will have all the required columns for you to process filters.
select
classid,
classcode,
assessedinclass,
validatedinclass,
sectionname,
subcode,
subdesc,
units,
schedule,
tutorial,
dissolved,
slots,
ismother,
mothercode,
mothercodeid,
mothercodeslots,
mergein,
inst,
lname,
fname,
mname,
suffix
from
(select
mainclass.id AS classid,
mainclass.code AS classcode,
Sum(case when enroll.acctok = '1' and enroll.assessed = '1'
then enroll.assessed
else 0
end) as assessedinclass,
Sum(enroll.validated = '1') AS validatedinclass,
section.name AS sectionname,
subject.code AS subcode,
subject.subdesc,
mainclass.units,
sched.name AS schedule,
mainclass.tutorial,
mainclass.dissolved,
mainclass.slots,
mainclass.ismother,
mergeclass.code AS mothercode,
mergeclass.id AS mothercodeid,
mergeclass.slots AS mothercodeslots,
mainclass.mergein,
mainclass.inst,
instructor.lname,
instructor.fname,
instructor.mname,
instructor.suffix
from
class as mainclass
left join
enrolldet on mainclass.id = enrolldet.class
left join
enroll on enrolldet.enrollno = enroll.enrollno
inner join
period on mainclass.period = period.id
inner join
section on mainclass.section = section.id
inner join
subject on mainclass.subject = subject.id
left join
sched on mainclass.sched = sched.id
left join
class as mergeclass on mainclass.mergein = mergeclass.id
left join
instructor on mainclass.inst = instructor.userid
left join
course on section.course = course.id)
where
(period.id = :period OR period.code = :period);
Your final query would be -
select
<req_cols>
from
(select
<agg_cols>
from
table
group by
<grpCols>)
where
<filter on agg cols>
$str = "SELECT
mainclass.id AS classid,
mainclass.code AS classcode,
Sum(CASE WHEN enroll.acctok = '1' AND enroll.assessed = '1'
THEN enroll.assessed
ELSE 0 end) AS assessedinclass,
Sum(enroll.validated = '1') AS validatedinclass,
section.name AS sectionname,
subject.code AS subcode,
subject.subdesc,
mainclass.units,
sched.name AS schedule,
mainclass.tutorial,
mainclass.dissolved,
mainclass.slots,
mainclass.ismother,
mergeclass.code AS mothercode,
mergeclass.id AS mothercodeid,
mergeclass.slots AS mothercodeslots,
mainclass.mergein,
mainclass.inst,
instructor.lname,
instructor.fname,
instructor.mname,
instructor.suffix
FROM
class AS mainclass
Left Join enrolldet ON mainclass.id = enrolldet.class
Left Join enroll ON enrolldet.enrollno = enroll.enrollno
Inner Join period ON mainclass.period = period.id
Inner Join section ON mainclass.section = section.id
Inner Join subject ON mainclass.subject = subject.id
Left Join sched ON mainclass.sched = sched.id
Left Join class AS mergeclass ON mainclass.mergein = mergeclass.id
Left Join instructor ON mainclass.inst = instructor.userid
Left Join course ON section.course = course.id
WHERE
(period.id = :period OR period.code = :period)";
if($level != ''){
$str .= " AND course.level = '".$level."'";
}
if($dept != ''){
$str .= " AND course.dept = '".$dept."'";
}
if($display != ''){
switch ($display) {
case 'open':
$str .= " AND mainclass.slots > assessedinclass";
break;
case 'dissolved':
$str .= " AND mainclass.dissolved = 1";
break;
case 'tutorial':
$str .= " AND mainclass.tutorial = 1";
break;
case 'closed':
$str .= " AND mainclass.slots <= assessedinclass";
break;
}
}
$str .= "GROUP BY
mainclass.id,
mainclass.code";
$str .="HAVING <your condition>";
$str .= " ORDER BY subject.code, mainclass.id";
return $this->_db->select($str,array(':period' => $period));

Join two queries in mysql/php

I need some help with this:
I'm doing a search with several parameters: Category, date(from), date(to)
Here's the code:
if (count($categorias) > 0) {
for ($i = 0; $i < count($categorias); $i++) {
if ($i == 0) {
$query = "WHERE id_subcategoria = " . $categorias[$i] . " ";
} else {
$query .= " OR id_subcategoria = " . $categorias[$i] . " ";
}
}
} else {
$query = "";
}
if (!$sql = $db->sql_query("SELECT * FROM actividades $query ORDER BY id DESC LIMIT " . $pageLimit . "," . PAGE_PER_NO)) {
message_die("DS", TRUE);
}
$total = $db->sql_numrows($sql);
if ($total != 0) {
while ($linha = $db->sql_fetchrow($sql)) {
$SqlEstado = $db->sql_query("SELECT * FROM tarifas WHERE id_actividade = " . $linha['id'] . " AND dia = '$data_min'");
First I get all the products that match the category and then I go to table "tarifas" to check if the product is available to be booked on that day.
So my question is: Can I do it all in one query? selecting the product and in case there's a rule in table "tarifas" fetch that rule.
If it's not clear enough please let me know so I can expose the problem in a different way, i'm really struggling with this.
Yes, you perform a JOIN on the id columns. If you want just those products that are in "tarifas" it will be an INNER JOIN, if you want the products even if they aren't in "tarifas" it will be "actividades LEFT JOIN tarifas ON ...".
Thanks for the help, user3038802!
Here's the code in case it helps anyone with the same problem:
if (!$sql = $db->sql_query("SELECT actividades.nome, actividades.id, actividades.dias, actividades.preco FROM actividades LEFT JOIN tarifas ON actividades.id = tarifas.id_actividade AND tarifas.dia = '$data_min' $query")) {
message_die("DS", TRUE);
}

Number of bound variables does not match number of tokens error when building dynamic query

I keep getting the following error:
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter
number: number of boundvariables does not match number of tokens
when trying to build a dynamic SQL query. Any idea where the damage is being done? I've searched for hours and still no luck
$park = $_POST["park"];
$lecturestyle_id = $_POST["lecturestyle"];
$group_size = $_POST["groupsize"];
$roomstructure_id = $_POST["roomstructure"];
$array = explode(",", $_POST["facilities"]);
// change Mon here
echo '<td class="gridSide">
Mon
';
// build facilities search
for($i = 0; $i < count($array); $i++){
if ($array[$i]!=0) {
$fac .= ' AND facilities_id='.$array[$i].'';
}
else
$fac .= '';
}
echo '</td>';
for ($i = 1; $i <= 9; $i++)
{
// change mon here
echo '<td class="box" id="mon'.$i.'">';
// dynamically build sql query
$sql =
"
SELECT DISTINCT COUNT(*) FROM ts_room rm
LEFT JOIN ts_roomfacilities rf
ON rm.id = rf.room_id
LEFT join ts_facilities f
ON f.id = rf.facilities_id
LEFT JOIN ts_building b
ON rm.building_id=b.id
WHERE capacity>=:group_size";
$sql .= $fac;
if($park!="Any") {
$sql .= " AND b.park_id=:park";
}
if($lecturestyle_id!="Any") {
$sql .= " AND lecturestyle_id=:lecturestyle_id";
}
if($roomstructure_id!="Any") {
$sql .= " AND roomstructure_id=:roomstructure_id";
}
$sql .= " AND rm.id NOT IN
(SELECT COUNT(*)
FROM ts_request rq
LEFT JOIN ts_allocation a ON a.request_id = rq.id
WHERE
day_id=1 AND period_id=:period
OR a.status IS NOT NULL
AND a.status IN ('Pending','Declined','Failed'))";
$stm = $pdo->prepare( $sql );
$stm->execute( array( ':roomstructure_id' => $roomstructure_id, ':lecturestyle_id' => $lecturestyle_id,
':group_size' => $group_size, ':park' => $park, ':period' => $i));
$rows = $stm->fetchColumn();
echo $rows.'<br>free</td>';
echo '</td>';
}
When you perform $stm->exectue, you're providing tokens for ':roomstructure_id', ':lecturestyle_id', ':group_size', ':park' and ':period', regardless of whether they actually exist in the query, i.e. if any of them are equal to 'Any'.
What you should do is build your value array as you're building your query... adding values to it whenever a field's search value is not 'Any'.
eg
if($park!="Any") {
$sql .= " AND b.park_id=:park";
$searchArray[':park'] = $park;
}
Your not assigning :group_size parameter, and $stm->execute() accepts a single dimensional array.
Try this:
<?php
require_once("../resources/php/connection.php");
$park = $_POST["park"];
$lecturestyle_id = $_POST["lecturestyle"];
$group_size = $_POST["groupsize"];
$roomstructure_id = $_POST["roomstructure"];
$array = explode(",", $_POST["facilities"]);
// change Mon here
echo '<td class="gridSide">Mon';
// build facilities search
echo '</td>';
$fac = null;
for($i = 0; $i < count($array); $i++){
if ($array[$i]!=0) {
$fac .= ' AND facilities_id='.$array[$i].'';
}
}
for ($i = 1; $i <= 9; $i++){
// change mon here
echo '<td class="box" id="mon'.$i.'">';
// dynamically build sql query
$sql ="SELECT DISTINCT COUNT(*) FROM ts_room rm
LEFT JOIN ts_roomfacilities rf
ON rm.id = rf.room_id
LEFT join ts_facilities f
ON f.id = rf.facilities_id
LEFT JOIN ts_building b
ON rm.building_id=b.id
WHERE capacity >= :group_size";
$sql .= $fac;
if($park!="Any") {
$sql .= " AND b.park_id=:park";
$searchArray[':park'] = $park;
}
if($lecturestyle_id!="Any") {
$sql .= " AND lecturestyle_id=:lecturestyle_id";
$searchArray[':lecturestyle_id'] = $lecturestyle_id;
}
if($roomstructure_id!="Any") {
$sql .= " AND roomstructure_id=:roomstructure_id";
$searchArray[':roomstructure_id'] = $roomstructure_id;
}
$sql .= " AND rm.id NOT IN
(SELECT COUNT(*)
FROM ts_request rq
LEFT JOIN ts_allocation a ON a.request_id = rq.id
WHERE
day_id=1 AND period_id=:period
OR a.status IS NOT NULL
AND a.status IN ('Pending','Declined','Failed'))";
//Dont forget these
$searchArray[':period'] = $i;
$searchArray[':group_size'] = $group_size;
$stm = $pdo->prepare($sql);
$stm->execute($searchArray);
$rows = $stm->fetchColumn();
echo $rows.'<br>free</td>';
echo '</td>';
}
?>

PHP Filtering search results by category with count

I'm trying to have an advanced sidebar that filters the results when someone uses the search bar. I have the regular search working and also made it display the count of items in a category on another sidebar (i.e. price range, brand, condition) but now i want the user to click a category and narrow down the results like what you see on newegg.com.
This is my search query:
function build_query($product_search) {
$search_query = "SELECT * FROM tbl_product p INNER JOIN tbl_brand b ON p.br_id = b.br_id";
$clean_search = str_replace(',', ' ', $product_search);
$search_word = explode(' ', $clean_search);
$final_search_words = array();
if (count($search_word) > 0) {
foreach ($search_word as $word) {
if (!empty($word)) {
$final_search_words[] = $word;
}
}
}
$where_list = array();
if (count($final_search_words) > 0) {
foreach($final_search_words as $word) {
$where_list[] = "p.pd_name LIKE '%$word%'";
}
}
$where_clause = implode(' OR ', $where_list);
if(!empty($where_clause)) {
$search_query .= " WHERE $where_clause";
}
Same thing for my sidebar that counts the categories:
function narrow_query($product_search) {
$search_query = "SELECT p.pd_name, p.pd_con, b.br_name AS name, c.cat_name AS cat,
COUNT(p.pd_con) AS con, COUNT(b.br_name) AS brand, COUNT(c.cat_name) AS cat_c,
COUNT(CASE WHEN p.pd_price >= '00.01' AND p.pd_price <= '99.99' THEN 1 END) AS cnt1,
COUNT(CASE WHEN p.pd_price >= '100.00' AND p.pd_price <= '199.99' THEN 1 END) AS cnt2,
COUNT(CASE WHEN p.pd_price >= '200.00' AND p.pd_price <= '299.99' THEN 1 END) AS cnt3,
And so on and so on...
FROM tbl_product p JOIN tbl_brand b ON b.br_id = p.br_id
JOIN tbl_category c ON c.cat_id = p.cat_id";
$clean_search = str_repl...
(Same word filter code as above)
Now i had something going that kinda worked by grabbing the $_GET:
"<a href=\"" . $_SERVER['PHP_SELF'] . "?productsearch=" . $product_search . "&narrow=" . $row['cat'] . "$link=1\">"
and using case in my functions:
function any_query($product_search, $link, $narrow) {
previous info from above...
if(!empty($link)) {
switch($link)
case 1:
$search_query .= " AND b.br_name = '$narrow'";
break;
default:
}
}
but it's not doing the job and I'm just lost at this point.
Is there a better way or a different method to achieve this? It doesn't have to use case or $_GET.
Thank you for your time.
The problem is you need to add a parentheses around your OR statements. I would do this:
$search_query .= ' WHERE 1';
if (!empty($where_clause)) {
$search_query .= " AND ($where_clause)";
}
if (!empty($narrow)) {
$search_query .= " AND $narrow";
}

Categories