PDO FETCH_CLASS with joined tables - php

Let's say I have 2 php objects:
<?php
class Post {
public $id;
public $text;
public $user_id;
}
?>
and
<?php
class User {
public $id
public $name
}
?>
Every post has a unique constraint with 1 user in the database.
I want to fill data into the "Post"-object with PDOs "FETCH_CLASS" method which works for all the "Post" attributes but how do I fill the attributes in "User"?
My SQL-statement looks like this:
SELECT post.id,
post.text,
post.user_id,
user.id,
user.name
FROM POST INNER JOIN User on post.user_id = user.id
Thanks!
UPDATE:
ATM I fill my "Post"-class like this:
$statement = $db -> prepare($query);
$statement -> execute();
$statement -> setFetchMode(PDO::FETCH_CLASS, 'Post');
$posts = $statement -> fetchAll();
So how would I have to change that for also filling the other class "User"?
SOLUTION:
$statement = $db -> prepare($query);
$statement -> execute();
$posts = array();
while (($row = $statement->fetch(PDO::FETCH_ASSOC)) !== false) {
$post = new Post();
$post->id = $row['post_id'];
$post->text = $row['post_text'];
$post->created = $row['post_created'];
$post->image = $row['post_image'];
$post->url = $row['post_url'];
$post->weight = $row['post_weight'];
$post->likes = $row['post_likes'];
$user = new User();
$user->id = $row['user_id'];
$user->nickname = $row['user_nickname'];
$user->created= $row['user_created'];
$user->locked = $row['user_locked'];
$post->user = $user;
$posts[] = $post;
}
return $posts;

You can try using __set method like this:
<?php
include 'connection.php';
class Post {
public $id;
public $text;
public $user;
public function __construct() {
$this->user = new User();
}
public function __set($name, $value) {
if (array_key_exists($name, get_object_vars($this->user))) {
$this->user->$name = $value;
} else {
$this->$name = $value;
}
}
}
class User {
public $id;
public $name;
}
$statement = $pdo->prepare("SELECT * FROM post "
. "LEFT JOIN user "
. "ON post.user_id = post.id");
$statement->execute();
$result = $statement->fetchAll(\PDO::FETCH_CLASS | \PDO::FETCH_PROPS_LATE, Post::class);
echo "<pre>";
var_dump($result);

Theres no support for the directly in PDO as far as I'm aware. Typically if you need to create a complex object graph from the result of query thats the responsibility of an ORM.
If you need this functionality i wold recommend using Doctrine or Propel as opposed to writing something yourself. There are others too that may be lighter weight, but i have no experience with them.
EDIT:
I think maybe i misunderstood the question as im sure others might. I think the real question was how to get access to the joined columns, not cessarially how to create an object from them.
In that case simply using a standard arry fethc method like PDO::FETCH_ASSOC, PDO::FETCH_NUMERIC or PDO::FETCH_BOTH will give you all the columns you queried.
So if you want to turn that into an "object graph" you have to do it manually not by using PDO::FETCH_CLASS.
For example:
//$db is pdo:
// also notice im aliase the columns prefixing the name so that we can tell what belongs to
// post and what belongs to user, an alternative approach would be to use FETCH_NUMERIC,
// which just uses the column positions from the seelct statement as the keys
// so in this case post.id would be in the array as key 0, and user.name would be in the
// array as key 4
$stmt = $db->prepare('SELECT post.id as p_id,
post.text as p_text,
post.user_id as p_user_id,
user.id as u_id,
user.name as u_name
FROM POST INNER JOIN User on post.user_id = user.id');
$stmt->execute();
while (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
print_r($row);
/* will output:
Array (
'p_id' => 'value'
'p_text' => 'value'
'p_user_id' => 'value'
'u_id' => 'value',
'u_name' => 'value'
)
So now you need to decide how to create your objects with the information returned
*/
}

Not really a response for the OQ, but because it keeps popping on Google (yes I know its over a year old). You'll find that it is AMAZINGLY faster to just skip loops and query each table separately.
SELECT post.id,
post.text,
post.user_id,
FROM POST INNER JOIN User on post.user_id = user.id
$statement = $db -> prepare($query);
$statement -> execute();
$statement -> setFetchMode(PDO::FETCH_CLASS, 'Post');
$posts = $statement -> fetchAll();
SELECT user.id,
user.name
FROM POST INNER JOIN User on post.user_id = user.id
$statement = $db -> prepare($query);
$statement -> execute();
$statement -> setFetchMode(PDO::FETCH_CLASS, 'User');
$users = $statement -> fetchAll();

Maybe use PDO::FETCH_NAMED if you work multiple tables. Or use PDO::ATTR_FETCH_TABLE_NAMES.

My approach for solution:
function groupQueryJoinClasses(\PDOStatement $stmt, $joinInfo = [], $idProperty = 'id')
{
$result = [];
$records = $stmt->fetchAll();
if ( !empty($joinInfo) ) {
foreach ($records as $record) {
if ( !isset($result[$record->$idProperty]) ) {
$result[$record->$idProperty] = $record;
}
foreach ($joinInfo as $target => $classInfo) {
$vars = get_object_vars($record);
$class = new $classInfo['class']();
foreach ($vars as $key => $value) {
$keyData = explode('.', $key);
if ( $keyData[0] == $classInfo['prefix']) {
$class->$keyData[1] = $value;
unset($result[$record->$idProperty]->$key);
}
}
if ( !is_array( $result[$record->$idProperty]->$target) ) {
$result[$record->$idProperty]->$target = [];
}
$targetArray = &$result[$record->$idProperty]->$target;
$targetArray[] = $class;
}
}
} else {
$result = $records;
}
return $result;
}
function getModel($query, $data, $entryClass, $joinInfo, $idProperty = 'id') {
$pdo = new PDO(...);
$stmt = $pdo->prepare($query);
$stmt->execute($data);
$stmt->setFetchMode(\PDO::FETCH_CLASS, $entryClass);
return groupQueryJoinClasses($stmt, $joinInfo , $idProperty);
}
// Sample request
$query =
'SELECT
u.id as "id",
p.id as "Post.id",
p.name as "Post.name"
FROM `user` u
LEFT JOIN `posts` p ON p.user_id = u.id
where id = :id'
;
$data = [ ':id' => 1 ];
$joinInfo = [
'posts' => [
'class' => Post::class,
'prefix'=> 'Post'
]
];
$flowRules = getModel($query, $data, User::class, $joinInfo);
Maybe interesting for anyone, or maybe someone would see issue in such approach

Related

Adding elements from an array to a sql query in PHP

I'm trying to put an array with values returned from a function to a sql query in PHP. Looking for information on how to do this, I tried to change the format of the array with the parameters fetch_assoc and fetch array, but it didn't help. I also tried to do a foreach loop for this array and execute sql with each iteration, but it also didn't do anything. How can I do this ?
This is my function which returns me an array of values:
public static function getLilanteProductCategoryForEmpik() {
$returnedArray = [];
$pdo = PDOConnector::getConnection();
echo $query = "SELECT product_type FROM `shopifyProductsLilante`
INNER JOIN offers_import_error_report_3839_14794292
WHERE shopifyProductsLilante.sku=offers_import_error_report_3839_14794292.sku";
$stmt = $pdo->prepare($query);
$stmt->execute( );
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$returnedArray[] = $row;
}
var_dump($returnedArray);
return $returnedArray;
}
and this is the function where I want to put the values from the array in the sql query:
public static function getEmpikCategoryToCSVFile() {
$returnedArray = [];
$lilanteProductCategory = MysqlProvider::getLilanteProductCategoryForEmpik();
$pdo = PDOConnector::getConnection();
foreach($lilanteProductCategory as $lilanteCategory)
{
echo $query = "SELECT empik_category FROM `empik_categories` INNER JOIN shopifyProductsLilante
WHERE $lilanteCategory=empik_categories.lilante_category";
}
$stmt = $pdo->prepare($query);
$stmt->execute( );
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$returnedArray[] = $row;
}
print_r($returnedArray);
return $returnedArray;
}

How to implement prepared statement in Zend Framework Database Query Functions

I am learning the Zend Framework. Now I need to attach a prepared statement to prevent SQL injection in the Zend Framework.
So I am sharing some functions here which I am using so if you can tell me how I can attach a prepared statement in these Zend Database Query Functions it will be helpful.
public function getRowByID($id) {
$row = $this->fetchRow("id = '$id'");
if (!$row) {
return false;
}
return $row;
}
public function getbyProjectID($projectid) {
$query = "SELECT * FROM auth where projectid = '$projectid'";
$result = $this->getAdapter()->query($query);
return $result->fetchAll();
}
public function updateRowByUserProject($username, $projectid) {
$query = "UPDATE auth SET iscurrent=0 WHERE username = '$username'";
$result = $this->getAdapter()->query($query);
$query1 = "UPDATE auth SET iscurrent=1 WHERE username = '$username' AND projectid = '$projectid'";
$result1 = $this->getAdapter()->query($query1);
$affectedRow = $result1->rowCount();
if($affectedRow == 1){
return true;
}else{
return false;
}
}
For fetching you could use Zend_Db_Select Class methods for preparing a query and executing it, passing variables in questionmark places (placeholders which will be escaped from special characters) after comma (possible multiple questionmarks, passing variables from left to right):
public function getRowByID($id) {
$table = $this->getTable();
$select = $table->select();
$select->where('id = ?', $id);
$row = $table->fetchRow($select);
if (!$row) {
return false;
}
return $row;
}
For your second method getByProjectId() it depends if you are in proper model (like Auth_Model_Auth) or you want to access data from another table
public function getbyProjectID($projectid) {
$table = $this->getTable();
$select = $table->select();
$select->where('projectid = ?', $projectid);
$result = $table->fetchAll($select);
return $result;
}
And for updating you can pass an array to 'update' method in same style like for fetching data. Key of an array must be a column name of your table.
public function updateRowByUserProject($username, $projectid) {
$table = $this->getTable();
// Data you want to insert/update
$data = [
'iscurrent' => 0
];
// Where you want to update it
$where = [
'username = ?' => $username
]
$result = $table->update($data, $where);
$data1 = [
'iscurrent' => 1
]
$where1 = [
'username = ?' => $username,
'projectid = ?' => $projectid
]
$result1 = $table->update($data1, $where1);
}
EDIT:
For both questions from comments you could achieve this by using quoteInto method, which also escapes data from special chars.
In first case you prepare a $where variable, which contains what record you want to delete:
$table = $this->getTable();
$where = $table->getAdapter()->quoteInto('projectid = ?', $projectid);
$isDeleted = $table->delete($where);
In second case you can do exactly the same:
$query = "SELECT COUNT(*) AS total FROM applications WHERE projectid IN (SELECT projectid FROM auth WHERE projectid = ?)";
$query = $this->getAdapter()->quoteInto(?, $projectid):
...
But you should try to avoid writing big queries in one variable and then executing them. I would suggest you to get to know with this:
https://framework.zend.com/manual/1.11/en/zend.db.select.html
Really well explained how to use Zend methods for this purpose.

Multiple Result Data in json Response

I am new in PHP and trying to build one API which provide me json response of required data. There one table called user and I need email, username and user_type from it. I have coded like below for do it
$result = array();
$users = getOnlineUsers($conn);
$userinfo['email'] = $users['email'];
$userinfo['username'] = $users['username'];
$userinfo['user_type'] = $users['user_type'];
$result['status'] ="success";
$result['userData'] = $userinfo;
And function is like below
function getOnlineUsers($conn)
{
$q = $conn->prepare("SELECT * FROM table_users WHERE online_status = 1");
// $q->bind_param("s", $email);
$q->execute();
$result = $q->store_result();
$metaResults = $q->result_metadata();
$fields = $metaResults->fetch_fields();
$statementParams='';
foreach($fields as $field){
if(empty($statementParams)){
$statementParams.="\$column['".$field->name."']";
}else{
$statementParams.=", \$column['".$field->name."']";
}
}
$statment="\$q->bind_result($statementParams);";
eval($statment);
$q->fetch();
return $column;
}
Its working fine but giving me only one row in response. I want get all row instead of one. I am getting response like this
{"status":"success","userData":{"email":"abc#gmail.com","username":"rajrathodbvn","user_type":0}}
Let me know if someone can help me for solve my issue.
Thanks
That's a lot of code for something so simple. Select the columns you want:
function getOnlineUsers($conn) {
$q = $conn->prepare("SELECT email, username, user_type
FROM table_users
WHERE online_status = 1");
$q->execute();
return $q->fetchAll(PDO::FETCH_ASSOC);
}
Then assign:
$result['status'] = 'success';
$result['userData'] = getOnlineUsers($conn);
Or:
$result = ['status' => 'success', 'userData' => getOnlineUsers($conn)];

how to randomise questions with corresponding answers from the database

I have been working on making a quiz app and i am stuck at one point. I am using PHP and MySQL to make it. So, i want now to randomise the retrieval of questions from the database . But , when i try to use the rand() with the questions, it's choices are different from what it should be. What should i do to sync the randomness of the questions with the answers.
class Quiz {
protected $_db;
protected $_answers = array();
protected $_questions = array();
protected $_question;
protected $_users;
protected $_leaderboard;
protected $_currentuser;
public $session;
public function __construct(\Pimple $container)
{
$this->_currentuser = $container['user'];
$this->session = $container['session'];
$this->_leaderboard = $container['leaderboard'];
$this->_users = $this->_leaderboard->getMembers();
try
{
//$this->_db = new PDO('mysql:host='.Config::$dbhost.';dbname='.Config::$dbname, Config::$dbuser, Config::$dbpassword);
$this->_db = $container['db'];
$this->_populateQuestions();
}
catch (\PDOException $e)
{
return $e;
}
}
public function getAnswers($questionid = false)
{
if ($questionid)
{
//pull answers from db for only this question
$answersql = "SELECT text FROM answers where question_id = :id ORDER BY correct DESC";
$stmt = $this->_db->prepare($answersql);
$stmt->bindParam(':id', $questionid, \PDO::PARAM_INT);
$stmt->execute();
while ($result = $stmt->fetchObject())
{
array_push($this->_answers,$result->text);
}
}
else
{
//pull all answers from db grouped by question
$answersql = "SELECT group_concat( a.text ORDER BY a.correct DESC SEPARATOR '~' ) FROM answers a GROUP BY a.question_id";
$stmt = $this->_db->query($answersql);
$stmt->execute();
$resultset = $stmt->fetchAll(\PDO::FETCH_NUM);
foreach ($resultset as $csv)
{
$tmparray = explode('~', $csv[0]);
array_push($this->_answers,$tmparray);
}
}
return $this->_answers;
}
public function getQuestion($questionid)
{
$questionsql = "select text from questions where id = :id order by rand()";
$stmt = $this->_db->prepare($questionsql);
$stmt->bindParam(':id', $questionid, \PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetchObject();
$this->_question = $row->text;
return $this->_question;
}
public function getQuestions()
{
return $this->_questions;
}
private function _populateQuestions()
{
$questionsql = "select text from questions order by id asc";
$stmt = $this->_db->query($questionsql);
$stmt->execute();
while ($row = $stmt->fetchObject())
{
$this->_questions[] .= $row->text;
}
}
Randomize an array of the same length as your question and answer arrays, with the values as array indexes, then iterate over the quiz questions and answers and change their keys. The order will be the same for each. Here is an example:
$order = [1, 0];
$ques = ['foo', 'bar'];
$ans = ['baz', 'bop'];
$new_ques = [];
$new_ans = [];
foreach($order as $k=>$v){
$new_ques[$v] = $ques[$k];
$new_ans[$v] = $ans[$k];
}
ksort($new_ques);ksort($new_ans);
var_dump($new_ques, $new_ans);
This is slightly inefficient because it creates a new array to put the values on, and then sorts them, but it should work for what you need.

JSON parse Mysql query result in php pdo

I am trying to do following
$statement = $conn->prepare('SELECT * FROM myTable');
$statement->execute();
if(!($row = $statement->fetchAll(PDO::FETCH_ASSOC)))
{
return false;
}
$conn = null;
} catch(PDOException $e) {
throw $e;
return false;
}
return return json_encode(array('Result'=>$row);
Works and fetches all entries in a table make then JSON Array and send them,
However I want to make a query where selected ids must be send in a JSON Array
E.g 10, 20, 30
I assume that this will be done in a For loop perhaps
$statement = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$statement->bindParam(':id', $id, PDO::PARAM_STR);
$statement->execute();
$row = $statement->fetch(PDO::FETCH_OBJ)))
Now suppose i have id's = 10,20,30 i want to append all of them in a JSON Array How can i do that?
just like return json_encode(array('Result'=>$row);
Edited Code
function GetMyMembers()
{
$myId = trim($_REQUEST['myId']);
try {
$conn = $this->GetDBConnection();
$statement = $conn->prepare('SELECT valId FROM memList WHERE myId=:myId' );
$statement->bindParam(':myId', $myId, PDO::PARAM_INT);
$statement->execute();
if(!($row = $statement->fetchAll(PDO::FETCH_ASSOC)))
{
return false;
}
// $row contains ALL THE ID'S
$placeholders = str_repeat('?,', count($row));
$placeholders = substr($placeholders, 0, -1);
$sql = "SELECT id, * FROM players WHERE id IN ($placeholders)";
$statement = $conn->prepare($sql);
$statement->execute($row);
$rows = $sth->fetchAll(PDO::FETCH_ASSOC|PDO::FETCH_GROUP);
$conn = null;
} catch(PDOException $e) {
throw $e;
return false;
}
return $rows;
}
$statement = $conn->prepare('SELECT * FROM myTable');
$statement->execute();
$data = array();
while($row = $statement->fetch(PDO::FETCH_ASSOC)))
{
$data[$row['id']] = $row;
}
return json_encode(array('Result'=>$data));
Btw, using raw API is not convenient. With Database abstraction library your code can be as short as 2 following lines:
$data = $db->getInd("id",'SELECT * FROM myTable');
return json_encode(array('Result'=>$data));
Edit:
if you have an array of ids, you need more complex code
$ids = array(1,2,3);
$data = array();
$statement = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
foreach ($ids as $id) {
$statement->bindValue(':id', $id, PDO::PARAM_STR);
$statement->execute();
$data[] = $statement->fetch(PDO::FETCH_OBJ);
}
return json_encode(array('Result'=>$data));
But while using Database abstraction library, there will be the same 2 lines:
$data = $db->getAll('SELECT * FROM myTable where id IN (?a)', $ids);
return json_encode(array('Result'=>$data));
Edit2:
if you need ids only
$statement = $conn->prepare('SELECT id FROM myTable');
$statement->execute();
$data = array();
while($row = $statement->fetch(PDO::FETCH_ASSOC)))
{
$data[] = $row['id'];
}
return json_encode(array('Result'=>$data));
while using Database abstraction library, it's still 2 lines:
$ids = $db->getCol('SELECT id FROM myTable');
return json_encode(array('Result'=>$ids));
$ids = array(1,2,3);
$placeholders = str_repeat('?,', count($ids));
$placeholders = substr($placeholders, 0, -1);
$sql = "SELECT id, * FROM table WHERE id IN ($placeholders)";
$sth = $dbh->prepare($sql);
$sth->execute($ids);
$rows = $sth->fetchAll(PDO::FETCH_ASSOC|PDO::FETCH_GROUP);
echo json_encode(array('Result' => $rows));
Based on additional comments:
Best option:
$sql = '
SELECT *
FROM table1 AS t1
INNER JOIN table2 t2
ON t2.foreign_key = t1.id
';
or
$sql = 'SELECT id FROM table1';
$sth = $dbh->prepare($sth);
$sth->execute();
$ids = $sth->fetchColumn();
//next look above

Categories