I need to run a query in zend frawork. The query is as given below.
select * from users where role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com');
I am passing the string
$whereString = role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com');
to
$this->fetchList($whereString) // where $this is object of users
But fetchList() function executes only the first part i.e role_id in (3,5)
but not the second part i.e emailID not in ('abc#abc.com', 'cba#cba.com');
The result from fetchList() contains 'abc#abc.com' and 'cba#cba.com'
fetchList() is:
public function fetchList($where=null, $order=null, $count=null, $offset=null)
{
$resultSet = $this->getDbTable()->fetchAll($where, $order, $count, $offset);
$entries = array();
foreach ($resultSet as $row)
{
$entry = new Application_Model_Users();
$entry->setId($row->id)
->setName($row->name)
->setEmail($row->email)
$entries[] = $entry;
}
return $entries;
}
fetchAll():
/**
* Fetches all rows.
*
* Honors the Zend_Db_Adapter fetch mode.
*
* #param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
* #param string|array $order OPTIONAL An SQL ORDER clause.
* #param int $count OPTIONAL An SQL LIMIT count.
* #param int $offset OPTIONAL An SQL LIMIT offset.
* #return Zend_Db_Table_Rowset_Abstract The row results per the Zend_Db_Adapter fetch mode.
*/
public function fetchAll($where = null, $order = null, $count = null, $offset = null)
{
if (!($where instanceof Zend_Db_Table_Select)) {
$select = $this->select();
if ($where !== null) {
$this->_where($select, $where);
}
if ($order !== null) {
$this->_order($select, $order);
}
if ($count !== null || $offset !== null) {
$select->limit($count, $offset);
}
} else {
$select = $where;
}
print_r($select);
$rows = $this->_fetch($select);
$data = array(
'table' => $this,
'data' => $rows,
'readOnly' => $select->isReadOnly(),
'rowClass' => $this->getRowClass(),
'stored' => true
);
$rowsetClass = $this->getRowsetClass();
if (!class_exists($rowsetClass)) {
require_once 'Zend/Loader.php';
Zend_Loader::loadClass($rowsetClass);
}
return new $rowsetClass($data);
}
Any body knows what wrong did I do.
I'm going out on a bit of limb, because I'm not sure. This feels like it might be a quote issue. Maybe try passing your query string like:
//use Zend_Db_Statement to process raw SQL.
//get default adapter
$db = Zend_Db_Table::getDefaultAdapter();
//write SQL statement
$sql = "select * from users where role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com')";
//assuming MySql as DB instantiate object
$stmt = new Zend_Db_Statement_Mysqli($db, $sql);
//Execute SQL Statement accepts optional parameter for array() of values to be bound
$stmt->execute();
or you might like to build the query using the select() object:
//get default adapter
$db = Zend_Db_Table::getDefaultAdapter();
//instantiate the select() object
$select = new Zend_Db_Select($db);
$select->from('users');
//two where will build an AND query use $select->orWhere() to build an OR query
$select->where('role_id in (3,5)');
$select->where("emailID not in ('abc#abc.com', 'cba#cba.com')");
$result = $model->fetchList($select);
and it might work if $whereString were a valid PHP string, beacuse fetchAll() accepts a string or an instance of Zend_Db_Select:
$whereString = "role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com')";
Hope this helps some... I took a shot.
Related
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.
I've researched illegal offset types but can't seem to get to the bottom of this one.
I have a script that selects id from table linkages column id and returning an array $resultid.
In the second part of my script I have a loop that is selecting content from latestRevision where $linktagId is equal to $id.
When I declare $id = $resultid and the $resultid has more than one value, I get Warning:
Illegal offset type ... on line 252
line 252 :
$result[$lId] = $stmt->fetch();
But if i limit the values in the original array to one by changing fetchAll to Fetch it runs fine.
Any help would be much appreciated. Here is my code:
public function testAction()
{
//Return list of tags for the defined user and make default type 10
$u = 2;
$t = 10;
$resultid = array();
//Connect to database
$db = Zend_Db_Table::getDefaultAdapter();
$select = $db->select()
->from(array('lt' => 'Linktags'),
array('id'))
->where('userId = ?', $u)
->where('type = ?', $t);
$stmt = $select->query();
$resultid = $stmt->fetchAll();
//print_r($resultid);
//Get latest revision from database and loop through $id's
$id = $resultid;
//print_r($id);
//setup array and loop
$result = array();
foreach($id as $lId) {
//Connect to database
$db = Zend_Db_Table::getDefaultAdapter();
//Perform Query
$select = $db->select('')
->from(array('lr'=>'LinktagRevisions'),
array('content'))
->where('linktagId = ?', $lId)
->order('updated DESC')
->limit(1);
$stmt = $select->query();
$result[$lId] = $stmt->fetch();
}
$this->_helper->json($result,true);
}
If I'm not mistaken, fetchAll will return an array of arrays.
So $lId is an array.
Try something like this :
foreach($id as $lId_all) {
$lId = $lId_all[0];
....
Or
foreach($id as $lId_all) {
foreach($lId_all as $lId) {
....
I'm trying to fix this butchered bit of code - as you might have guessed, I'm cocking up the bind param syntax. In fact, I'm not even sure what I'm trying to do is even possible. Here's the class method...
/***
*
* #select values from table
*
* #access public
*
* #param string $table The name of the table
*
* #param array $fieldlist Fields to return in results, defaults null
*
* #param array $criteria Search criteria by keyed by fieldname
*
* #param int $limit Limit of records to return, defaults 10
*
* #return Array on success or throw PDOException on failure
*
*/
public function dbSearch($table, $fieldList = null, $criteria = null, $limit = 10)
{
// setup $this->db to point to a PDO instance
$this->conn();
// build fieldlist
if( is_null($fieldList) OR !is_array($fieldList) OR count($fieldList) == 0) {
$returnFields = '*';
} else {
$returnFields = "'".implode("', '", $fieldList)."'";
}
// build criteria
if( is_null($criteria) OR !is_array($criteria) OR count($criteria) == 0) {
$whereClause = '';
} else {
$whereClause = array();
foreach ($criteria as $key => $value){
$bind_name = 'bind_'.$key; //generate a name for bind1, bind2, bind3...
$$bind_name = $value; //create a variable with this name with value in it
$bind_names[] = & $$bind_name; //put a link to this variable in array
$whereClause[] = "'$key' = :$bind_name";
}
$whereClause = count($whereClause) > 0 ? ' WHERE '.implode( ' AND ' , $whereClause ) : '';
}
$sql = "SELECT $returnFields FROM '$table' $whereClause LIMIT $limit";
$stmt = $this->db->prepare($sql);
if( $whereClause != '') {
call_user_func_array(array(&$stmt, 'bindParam'), $bind_names);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
... which at some point I want to call using something along these lines...
// look for users in database...
$user_recs = $crud->dbSearch('user', array('user_name'), array('user_name'=> $_POST['username']));
$users = $user_recs->fetchAll(PDO::FETCH_ASSOC);
How bonkers is this? Is it possible? Do I need to pass in the param types as well somehow? Any help gratefully received!
Actually, the problem was using bound parameters as opposed to bound values... doh!
Given an SQL statement and some values in an associative array, e.g.
$sql = "SELECT * FROM event
WHERE eventdate >= :from
AND eventdate <= :until
AND ( user_name LIKE :st OR site_name LIKE :st )
ORDER BY eventdate, start_time LIMIT 100";
$values = array( 'st' => '%'.$searchterm.'%',
'from' => $fromdate,
'until' => $untildate, );
then this class method ( but it could easily by a plain function) did the trick:
public function dbBoundQuery($sql, $values, $types = false) {
$this->conn();
$stmt = $this->db->prepare($sql);
foreach($values as $key => $value) {
if($types) {
$stmt->bindValue(":$key",$value,$types[$key]);
} else {
if(is_int($value)) { $param = PDO::PARAM_INT; }
elseif(is_bool($value)) { $param = PDO::PARAM_BOOL; }
elseif(is_null($value)) { $param = PDO::PARAM_NULL; }
elseif(is_string($value)) { $param = PDO::PARAM_STR; }
else { $param = FALSE;}
if($param) $stmt->bindValue(":$key",$value,$param);
}
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
Hope this helps someone else.
I really don't understand how this function is better than conventional
$stmt = $db->prepare("SELECT user_name FROM user WHERE user_name = ?");
$stmt->execute($_POST['username']);
$users = $stmt->fetchAll();
Mind you,
it keeps your query flexible. LIMIT ?,? is possible
it keeps your query readable. Almost natural English of SQL stays in place. You still can tell what does your query do, without the need of learning some brain-damaging language. As a side effect, any other developer can comprehend this code too.
Well, there are quite a few things that can go wrong with the code.
For once, I don't see any AND / OR between WHERE clauses - which is probably why it doesn't work.
Secondly, it doesn't allow you to use SQL functions. Let's say you need to write a query like this:
SELECT * FROM `table` WHERE UNIX_TIMESTAMP(date_added) < ...;
You get the idea.
I would suggest to either use an existing ORM (Doctrine, Propel, etc), or stick to PDO.
Here is an example of how I would use PDO for a User class:
class User
{
protected $data;
public function __get($key) {
return $this->data[$key];
}
public function __set($key, $value) {
$this->data[$key] = $value;
}
/**
* #param $value
* #param $field
* #return $this
*/
public function loadBy($value, $field)
{
$db = DbFactory::getInstance();
$query = "SELECT * FROM users WHERE $field = :$field LIMIT 1";
$stmt = $db->prepare($query);
$stmt->execute(array(":$field" => $value));
$result = $stmt->fetch();
foreach ($result as $key => $value) {
$this->$key = $value;
}
return $this;
}
}
You can create such functions for your entities, this you will have functions that are specialized, and efficient in what they do, and that are easy to test.
PS:
Ignore the issue that appears when you have a field named data :)
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
I am bit confused as when try the code below I receive the desired result.
include_once('config.class.php');
$db = Core::getInstance();
$whr = 'test#nannex.com';
$inv = $db->dbh->prepare("SELECT * FROM ruj_users WHERE email=:whr");
$inv->execute(array(":whr"=>$whr));
$row = $inv->fetch(PDO::FETCH_ASSOC);
echo $row['email'];
echo $row['full_name'];
However, when I run the following code it returns 1 not the desired result.
include_once('config.class.php');
$db = Core::getInstance();
$whr = 'test#nannex.com';
function fetchUser($whr){
$db = Core::getInstance();
$inv = $db->dbh->prepare("SELECT * FROM ruj_users WHERE :whr");
$inv->execute(array(':whr'=>$whr));
$res = $inv->fetch(PDO::FETCH_ASSOC);
return $res;
}
$row = fetchUser("email = '".$whr."' ");
echo $row['email'];
echo $row['full_name'];
This query:
SELECT * FROM ruj_users WHERE :whr
When expanded:
SELECT * FROM ruj_users WHERE 'email = \'test#nannex.com\''
The expression email = \'test#nannex.com\' will be evaluated by MySQL as a boolean and is always truthy, so it will return all rows in ruj_users.
If you want custom conditions, you can do something like this:
function fetchUser(array $conditions)
{
// ...
$sql = 'SELECT * FORM ruj_users WHERE';
$params = array();
foreach ($conditions as $column => $value) {
if (preg_match('/^[a-z]+$/', $column)) {
$sql .= "`$column` = ?";
$params[] = $value;
}
}
$inv = $db->dbh->prepare($sql);
$inv->execute(array_values($params));
// ...
}
fetchUser(array(
'email' => 'test#nannex.com',
'status' => 23,
));
You have an error in your query in the function:
$inv = $db->dbh->prepare("SELECT * FROM ruj_users WHERE :whr");
Should be:
$inv = $db->dbh->prepare("SELECT * FROM ruj_users WHERE email=:whr");
Edit: If you want to pass column names as well, you would have to add another variable to your function:
function fetchUser($column, $value) {
Note that only the value can be bound in a prepared statement, the column variable you would have to check against a white-list to avoid sql injection and hard-code in the query, like ... WHERE $column = :whr ...