mysql query LIMIT use attribute :variable [duplicate] - php

This question already has answers here:
How to apply bindValue method in LIMIT clause?
(11 answers)
Closed 5 years ago.
i am getting a Syntax error or access violation, when i use the below format in the query.
$limit = $request->getAttribute('limit');
$sql = "SELECT * FROM users WHERE status = 1 ORDER BY date DESC LIMIT :limit";
try{
$db = new db();
$db = $db->connect();
$stmt = $db->query($sql);
$stmt->bindParam(":limit", $limit);
$users = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
if(empty($users)) {
$response->getBody()->write
('
{
"error":
{
"message":"Invalid Request"
}
}');
} else {
$response->getBody()->write(json_encode($users));
}
} catch(PDOException $e) {}
how can i call the attribute for the limit within the query ?

You need do it like below (changes are commented):-
$limit = $request->getAttribute('limit');
$sql = "SELECT * FROM users WHERE status = 1 ORDER BY date DESC LIMIT :limit";
try{
$db = new db();
$db = $db->connect();
$stmt = $db->prepare($sql); //prepare sql first
$stmt->bindValue(':limit',(int)$limit,PDO::PARAM_INT);//tell value is integer
$users = $stmt->fetchAll(PDO::FETCH_OBJ);
if(count($users) ==0) {
$response->getBody()->write('{"error":{"message":"No More Users Exist"}}');
} else {
$response->getBody()->write(json_encode($users));
}
$db = null;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}

The syntax error you see occurs because when you bindParam(), PDO by default inserts your parameter as a string, so the DB engine sees something like LIMIT "10".
So you have 2 choices: you could tell PDO to insert the value as an integer (see #Alive to Die answer) or, more simply, you can cast it to an integer and put it right in the query. Since you're casting first, there is no risk of SQL injection:
$limit = (int) $request->getAttribute('limit');
$sql = "SELECT * FROM users WHERE status = 1 ORDER BY date DESC LIMIT $limit";

Symply put your $limit variable in query
$limit = $request->getAttribute('limit');
$sql = "SELECT * FROM users WHERE status = 1 ORDER BY date DESC LIMIT $limit";

In PDO define it as an INT
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);

It's better to use prepare and execute statement like :
$limit=10
$req=$db->prepare('SELECT * FROM users WHERE status = ? ORDER BY date DESC LIMIT ?');
$req->execute(array(1, $limit));
I never use bindParam statement.

Related

PHP Prepared Object Query Not Returning Any Data [duplicate]

This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 3 years ago.
I have some PHP code that takes a post request as input to a prepared statement.
It is not returning any information.
There are no errors.
I have tried hard coding the variables that are passed to the prepared object to no avail.
If I manually query the database with the desired query, output is received.
What am I missing here? What can I do to get output?
Heres my code:
<?php
$username = "user";
$password = "ultrasecurepassword";
try {
$pdo = new PDO('mysql:unix_socket=/run/mysql/mysql.sock;dbname=news', $username, $password);
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
$query = "SELECT * FROM newsdb ORDER BY pubdate DESC LIMIT 250";
if(!empty($_POST['search'])){
$termobusca = htmlspecialchars($_POST['search']);
$tipobusca = htmlspecialchars($_POST['searchtype']);
if($tipobusca == "title"){
$stmt = $pdo->prepare("SELECT * from newsdb where title like '%:term%' ORDER BY pubdate DESC limit 5000;");
}
else {
$stmt = $pdo->prepare("SELECT * from newsdb where pubdate like '%:term%' ORDER BY pubdate DESC limit 5000;");
}
$stmt->bindParam(1, $termobusca);
}
else {
$stmt = $pdo->prepare("SELECT * FROM newsdb ORDER BY pubdate DESC LIMIT 250");
}
$stmt->execute();
while($row = $stmt->fetch()){
print_r($row);
}
$pdo = null;
?>
I have seen this question before somehow
try this please
if($tipobusca == "title"){
$stmt = $pdo->prepare("SELECT * from newsdb where title like :term ORDER BY pubdate DESC limit 5000;");
}
else {
$stmt = $pdo->prepare("SELECT * from newsdb where pubdate like :term ORDER BY pubdate DESC limit 5000;");
}
$term = '%'.$termobusca.'%';
$stmt->bindParam(':term', $term, PDO::PARAM_STR);

how to separate an sql query into chain methods

I am new in oop and I am trying to change a sql query into 3 chained method and use like this:
$mtag = $mtag->allTags()->orderBy()->lastThree();
public function allTags()
{
$sql = "SELECT tag_id FROM posts_tags ";
$sql .= "GROUP BY tag_id ";
$sql .= "ORDER BY COUNT(*) DESC LIMIT 3 ";
$res = self::builder($sql);
return $res;
}
i want to use each $sql in to a separate chain method .
how can i do this?
i have tried this but it doesnt work
public function allTags()
{
$sql = "SELECT tag_id FROM posts_tags ";
self::builder($sql);
return $this;
}
public function orderBy()
{
$sql = "GROUP BY tag_id ";
self::builder($sql);
return $this;
}
public function lastThree()
{
$sql = "ORDER BY COUNT(*) DESC LIMIT 3 ";
self::builder($sql);
return $this;
}
my builder method looks like this
public function builder(string $sql)
{
$stmt = $this->database->prepare($sql);
$stmt->execute();
if (!$stmt) {
throw new Exception("Query failed . . .");
}
return $stmt;
}
The problem is that the self::builder($sql); method directly prepares the sql statement, instead of waiting for it to be completely constructed:
$mtag = $mtag
/*
the first self::builder($sql) is called,
prepares the statement without the order and group by
*/
->allTags()
/*
the second self::builder($sql) is called from orderBy, prepares "GROUP BY tag_id "
probably a syntax error is hidden here
*/
->orderBy()
/*
the third self::builder($sql) is called from lastThree
it tries to prepare "ORDER BY COUNT(*) DESC LIMIT 3 "
a syntax error is probably hidden here too
*/
->lastThree();
The statement is prepared over and over again. You can avoid this by doing, in that order
defining the statement with all its chained variants
calling a method to fetch data, preparing the statement composed from the various chained method calls.
$stmt = $mtag->allTags()->orderBy()->lastThree();
$result = $stmt->resolve();
where $stmt = $this->database->prepare($sql); only happens in the resolve method.
you have error in return code.
Change to that code!
public function lastThree()
{
$sql = "ORDER BY COUNT(*) DESC LIMIT 3 ";
$res = self::builder($sql);
return $res;
}

Pagination 2nd page not displaying

Pagination works fine when I don't use the WHERE statement in my SELECT statement. For some reason as soon as I add additional requests in the SELECT statement, only the 1st pagination page works. So it seems like the variable data is lost after the first page is displayed. Below is some of the code:-
<?php
include 'database.php';
include 'paginator.php';
$pdo = Database::connect();
$paginator = new Paginator();
$sql = "SELECT count(*) FROM customer_crm ";
$paginator->paginate($pdo->query($sql)->fetchColumn());
$query = $_GET["query"];
if (isset($query)) {
($_GET['query'])?('%'.$_GET['query'].'%'):'%';
$sql = "SELECT * FROM customer_crm WHERE firstname LIKE :query OR email LIKE :query OR telephone LIKE :query ";
}
else {
$start = (($paginator->getCurrentPage()-1)*$paginator->itemsPerPage);
$length = ($paginator->itemsPerPage);
//$sql = "SELECT * FROM customer_crm WHERE customer_group_id = $input OR date_followup= CURDATE() ORDER BY customer_group_id DESC limit $start, $length ";
$sql = "SELECT * FROM customer_crm ORDER BY date_followup DESC limit $start, $length ";
//$sql = "SELECT * FROM customer_crm WHERE customer_group_id = $input ORDER BY date_followup DESC limit $start, $length ";
}
$sth = $pdo->prepare($sql);
$sth->bindParam(':start',$start,PDO::PARAM_INT);
$sth->bindParam(':length',$length,PDO::PARAM_INT);
$sth->bindParam(':query',$query,PDO::PARAM_STR);
$sth->execute();
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
Without knowing which Paginator are we talking about, I could only advise you to do something like
include 'database.php';
include 'paginator.php';
$pdo = Database::connect();
$paginator = new Paginator();
$query = (isset($_GET["query"]) && strlen($_GET["query"])>1)? '%'.$_GET["query"].'%':'%';
$countsql = "SELECT * FROM customer_crm WHERE firstname LIKE :query OR email LIKE :query OR telephone LIKE :query ";
$sthcount = $pdo->prepare($countsql);
$sthcount->bindParam(':query',$query,PDO::PARAM_STR);
$sthcount->execute();
$count=$sthcount->fetchColumn();
$paginator->paginate($count);
$start = (($paginator->getCurrentPage()-1)*$paginator->itemsPerPage);
$length = ($paginator->itemsPerPage);
$sql = $countsql . ' ORDER BY date_followup DESC limit :start, :length ';
$sth = $pdo->prepare($sql);
$sth->bindParam(':start',$start,PDO::PARAM_INT);
$sth->bindParam(':length',$length,PDO::PARAM_INT);
$sth->bindParam(':query',$query,PDO::PARAM_STR);
$sth->execute();
See, you where making two mistakes here:
getting your count value without considering the query. You should set the value of $query regardless of the existance of $_GET['query'], and use it in your count query as well as your results query.
binding parameters whose placeholders and values do not exist in the query you're executing. Make sure your results query contains :query, :start and :length or you will be binding more parameters than the query has.
You should also have wrapped your statements in try/catch blocks so you could debug what was happening.
try {
$sth = $pdo->prepare($sql);
$sth->bindParam(':start',$start,PDO::PARAM_INT);
$sth->bindParam(':length',$length,PDO::PARAM_INT);
$sth->bindParam(':query',$query,PDO::PARAM_STR);
$sth->execute();
} catch(\PDOException $e) {
die('Error in query: '. $e->getMessage());
}
That way you would have known that the query was failing because of
Invalid parameter number: parameter was not defined
NOTE I have no clue about how your paginator will know about the current page, nor can I see where are you setting the itemsPerPage value.

Getting correct Total Row count and Row Numbers for Pagination

Now I have done this easily with PDO and SQL server however I am struggling getting this working correctly for any general Query. Since I do want this to be reusable by our MySQL tables over any project I am turning here for help.
So the way I used it for PDO and SQL server
function pagedQuery($dbh,$q,$sort,$offset,$limit,$params = array()) {
$limit = preg_replace("/[^0-9]]/","",$limit);
$offset = preg_replace("/[^0-9]]/","",$offset);
$q = preg_replace("/^[^\w]*SELECT/i","SELECT COUNT(*) OVER() as num_rows, ROW_NUMBER() OVER(ORDER BY $sort) AS rownum,",$q);
$q = "SELECT * FROM (".$q.") as pagingTable WHERE rownum > $offset AND rownum <= ($offset+$limit)";
## Prepare Query
$stmt = $dbh->prepare($q);
try {
$stmt->execute($params);
} catch(PDOException $e) {
print $e->getMessage();
}
## Return Results
return $stmt->fetchAll();
Now obviously the OVER functionality does not exist and MySQL and this has to be modified. Here is my attempt at a new version:
function mysqlPagedQuery($dbh,$q,$table,$sort,$offset,$limit,$params = array()) {
$limit = preg_replace("/[^0-9]]/","",$limit);
$offset = preg_replace("/[^0-9]]/","",$offset);
$q = preg_replace("/^[^\w]*SELECT/i","SELECT (SELECT COUNT(*) as num_rows FROM ".$table.") AS num_rows, #rowN := #rowN + 1 AS rownum,",$q);
$q = "SELECT * FROM (".$q.") as pagingTable WHERE rownum > $offset AND rownum <= ($offset+$limit)";
## Prepare Query
$stmt = $dbh->prepare($q);
try {
$query = $dbh->query("set #rowN = 0;");
$stmt->execute($params);
} catch(PDOException $e) {
print $e->getMessage();
}
## Return Results
return $stmt->fetchAll();
Now this will "work" given that you give it a table but I soon realized that the num_rows will not return the correct value unless given all of the WHERE clause of the query sent into it. I would have to parse that in an inefficient manner and I feel there is a much easier way to do this than I am making it out to be.
I ended up with this thanks to bobkingof12vs posted link. Can't give answer credit to him directly because it was not posted here :(
function mysqlPagedQuery($dbh,$q,$sort,$offset,$limit,$params = array()){
$limit = preg_replace("/[^0-9]]/","",$limit);
$offset = preg_replace("/[^0-9]]/","",$offset);
//need to use the original query a few times to create paged
$tempQuery = $q;
//replaces query's select with a count to get numrows
$q1 = "SELECT sql_calc_found_rows * FROM (".$tempQuery.") AS tempTable";
## Prepare Query
$stmt1 = $dbh->prepare($q1);
$q2 = preg_replace("/^[^\w]*SELECT/i","SELECT FOUND_ROWS() as num_rows,",$tempQuery);
$q2 .= " ORDER BY ".$sort;
$q2 .= " LIMIT ".$offset.",".$limit;
//print $q2;
## Prepare Query
$stmt2 = $dbh->prepare($q2);
try{
$stmt1->execute();
$stmt2->execute($params);
}
catch(PDOException $e){
print $e->getMessage();
}
## Return Results
return $stmt2->fetchAll();
}

"SELECT * ..." mysqli parametrized query - how to store results without binding?

I want to store mysqli query results to array.
My code so far looks like this:
function get_nearby_users($Id, $MaxDistance, $RowLimit, $RowLimitOffset)
{
try
{
$query = "SELECT
others.*,
Distance(me.Latitude, me.Longitude, others.Latitude, others.Longitude) as Distance
from
Users me
join
Users others
where
me.Id = ?
and
others.Id != ?
having Distance < ?
limit ?,?
";
$stmt = $this->link->prepare($query);
$stmt->bind_param('iiiii', $Id, $Id, $MaxDistance, $RowLimitOffset, $RowLimit);
$stmt->execute();
// how to fill $rows_array?
}
catch(Exception $exc)
{
// ...
}
return $rows_array;
}
How can I put my results to array when my SQL contains something like "SELECT *"?
All tutorials with parametrized queries use bind_result function, but I don't want to create variables for all fields and bind them. There is no other way?
You do not need to use bind_result to store the record set.
Just use fetchAll() to store result set rows as an array.
$rows_array = $stmt->fetchAll();
I have modified your code. Use this:
function get_nearby_users($Id, $MaxDistance, $RowLimit, $RowLimitOffset)
{
try
{
$query = "SELECT
others.*,
Distance(me.Latitude, me.Longitude, others.Latitude, others.Longitude) as Distance
from
Users me
join
Users others
where
me.Id = ?
and
others.Id != ?
having Distance < ?
limit ?,?
";
$stmt = $this->link->prepare($query);
$stmt->bind_param('iiiii', $Id, $Id, $MaxDistance, $RowLimitOffset, $RowLimit);
$stmt->execute();
$rows_array = $stmt->fetchAll(); // store result set rows as an array
}
catch(Exception $exc)
{
// ...
}
return $rows_array;
}

Categories