how to separate an sql query into chain methods - php

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;
}

Related

php function to limit mysql query results

Hi I'm currently using the following code (1) to LIMIT the number of lines for the results to be displayed. But I want to use a different method but it is not working (2) could someone please help me with the correct syntax?
Code (1) Current.
function find_all_limit10() {
global $db;
$sql = "SELECT * FROM stock ";
$sql .=" ORDER BY po_date DESC";
$sql .=" LIMIT 10";
return find_by_sql($sql);
}
Code (2) Desired but missing LIMIT 10 and ORDER BY
function find_all_limit10($table) {
global $db;
if (tableExists($table)) {
return find_by_sql("SELECT * FROM {$db->escape($table)}");
}
}
Any help will be much appreciated.
Thank you
I think this is what you are trying to do. In PHP you can join strings using . So we will append the result of the $db->escape($table) string to the end of the SELECT Statement.
function find_all_limit10($table) {
global $db;
$sql = "SELECT * FROM ". $db->escape($table);
$sql .=" ORDER BY po_date DESC";
$sql .=" LIMIT 10";
return find_by_sql($sql);
}
Not sure if this is what you are after...
function find_all_limit10($table) {
global $db;
if (tableExists($table)) {
$data = find_by_sql("SELECT * FROM " . $db->escape($table) );
// assuming $data is an array of rows
// keep just 10
return array_slice($data, 0, 10);
}
}

mysql query LIMIT use attribute :variable [duplicate]

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.

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();
}

MySQLi prepared statements: Short versions in one line

In PHP, I use a lot of one liners like:
Fetch value:
$sql->query("SELECT ID FROM table WHERE condition = 1")->fetch_object()->ID;
Fetch row:
$sql->query("SELECT * FROM table WHERE condition = 1")->fetch_assoc();
Fetch rows:
$sql->query("SELECT * FROM table WHERE condition > 1")->fetch_all(MYSQLI_ASSOC);
Insert row:
$sql->query("INSERT INTO table(`row1`,`row2`) VALUES('".$data1."','".$data2."')");
Delete row:
$sql->query("DELETE FROM table WHERE condition = 1");
Are there also beautiful one liners for prepared statements?
This fails:
$sql->prepare("SELECT ID FROM table WHERE condition = ?")->bind_param("i", $a=1)->execute()->fetch_object()->ID;
This works:
$query = $sql->prepare("SELECT ID FROM table WHERE condition = ?");
$query->bind_param("i", $a=1);
$query->execute();
$query->get_result()->fetch_object()->ID;
Directly - there is no way. But you may use a simple overlay class which will allow you to do so.
class PrepareOverlay {
private $stmt;
private $lastResult = null;
public function __construct(PDOStatement $stmt) {
$this->stmt = $stmt;
}
public function __call($name, $arguments) {
$this->lastResult = call_user_func_array([$this->stmt, $name], $arguments);
return is_bool($this->lastResult) ? $this : $this->lastResult;
}
public function getLastResult() {
return $this->lastResult;
}
public static function prepare($sql, $query) {
return new self($sql->prepare($query));
}
}
And use it like this:
PrepareOverlay::prepare($sql, 'SELECT * FROM table WHERE cond = ?')->bindParam('i', $a=1)->execute()->fetchObject()->ID;
Although you will need some phpdoc to make code completion work.
if you consider using a framework like codeigniter you will be able to enjoy the Active record class, and you will be able to use something elegant like those lines :
$query = $this->db->get('mytable');
// Produces: SELECT * FROM mytable
or
$query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);
or
$this->db->select('*');
$this->db->from('blogs');
$this->db->join('comments', 'comments.id = blogs.id');
$query = $this->db->get();
// Produces:
// SELECT * FROM blogs
// JOIN comments ON comments.id = blogs.id
you can also use chaining like this
$this->db->select('title')->from('mytable')->where('id', $id)->limit(10, 20);
$query = $this->db->get();
or
echo $this->db->count_all('my_table');
// Produces an integer, like 25
or
$this->db->delete('mytable', array('id' => $id));
// Produces:
// DELETE FROM mytable
// WHERE id = $id
and much more ... take a look at the documentation for a full documentation about active record
cheers !

Prepared default query with result to prepared query with bindings

I have a query that is working but is does not have binded values:
$this->_db->query("SELECT * from posts WHERE post_title LIKE '%$this->search_keywords%' OR post_content LIKE '%$this->search_keywords%' LIMIT 100");
$this->rows_results_found = $this->_db->resultset();
$this->results_found_num = sizeof($this->rows_results_found);
I am rewriting it to this one but with no luck:
$this->_db->query('SELECT * from posts WHERE post_title LIKE :search_keywords OR post_content LIKE :search_keywords LIMIT 100');
$this->_db->bind(':search_keywords', '%$this->search_keywords%');
$this->rows_results_found = $this->_db->resultset();
$this->results_found_num = sizeof($this->rows_results_found);
$this->results_found_num appears always to be an empty array.
This is what I have in the resultset() (It is from another outer class):
public function resultset() {
$this->execute();
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
What I am missing here?
Thank you in advance!
Assuming you have a valid connection adjust your function to the following:
public function get_posts($conn) {
$query = 'SELECT *
FROM posts
WHERE post_title LIKE :search_keywords1 OR
post_content LIKE :search_keywords2 LIMIT 100';
$stmt = $conn->prepare($query);
$stmt->bindValue(':search_keywords1', '%'.$this->search_keywords.'%');
$stmt->bindValue(':search_keywords2', '%'.$this->search_keywords.'%');
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
Usage:
$posts = $this->_db->get_posts($conn);
//var_dump($post)
$this->rows_results_found = count($post);

Categories