Select ignores where clause using Zend_Db_Select - php

$table = new Zend_Db_Table(array('name'=>'rules'));
$select = $table->select();
$select->setTable($table);
$select->setIntegrityCheck(false);
$select = $select
->from(array('ru'=>'rules'),array('ru.*'))
->join(array('ro'=>'roles'),'ro.id=ru.role_id',array('role_id'=>'ro.id'))
->join(array('g'=>'groups'),'ro.group_id=g.id',array('group_id'=>'g.id'))
->join(array('ug'=>'user_groups'),"ug.group_id=g.id",array('user_group_id'=>'ug.id'))
->where("ug.user_id={$userId}")
->where("ru.resource='{$resource}'")
->where("ru.privilege='{$privilege}'");
echo "select: ".$select->__toString();
$row = $table->fetchAll();
I have the preceding code,but when I try fetchAll() it returns all rows in the table, ignoring the where clause, when I use fetchRow() it returns the first row it finds, ignoring the where clause, I printed the SQL statement and run it separately and it executes correctly
any clue ?

This is how you would create a db select object correctly
$db = Zend_Db::factory( ...options... );
$select = new Zend_Db_Select($db);
Or you use the database adapter's select() method
$db = Zend_Db::factory( ...options... );
$select = $db->select();
And you can add clauses
// Build this query:
// SELECT *
// FROM "table1"
// JOIN "table2"
// ON "table1".column1 = "table2".column1
// WHERE column2 = 'foo'
$select = $db->select()
->from('table1')
->joinUsing('table2', 'column1')
->where('column2 = ?', 'foo');
Have a look at the Zend_Db Reference Guide for more information

#ArtWorkAD is right in a certain way. But in your case you're not just using a Zend_Db_Select. You tried to extend a Zend_Db_Select obtained from a Zend_Db_Table (well, you should try to handle a Singleton pattern with Zend_Db_Table but this is another problem). Your current problem (if we except the fact you are certainly reading documentation too fast) is that this line was correct:
$select->setIntegrityCheck(false);
It make your 'select-from-a-zend-db-table' not anymore restricted to the Active Record Mode, and available for extra joins.
But just after that you make a:
$select = new Zend_Db_Select($table);
This is the complete creation of a new object, that you put into your variable. Nothing is kept from previous variable value. You could add a $select=null; just before it would be the same. So this is just canceling the 3 previous lines.
In quite the same confusion mode this line:
$select->setTable($table);
Is not necessary as you're already taking the select from a Zend_Db_Table so the table is already there.
EDIT
And your last and bigger error is:
$table->fetchAll()
You do not use your built $select but your $table, so effectively everything done in your $select is ignored :-) . Fecthing from the $select shoudl give you better results

This should work. Just tested it.
$table = new Zend_Db_Table('rules');
$select = $table->getAdapter()->select();
$select->from(array('ru' => 'rules'), array('ru.*'))
->join(array('ro'=>'roles'), 'ro.id = ru.role_id', array('role_id'=>'ro.id'))
->join(array('g'=>'groups'), 'ro.group_id = g.id', array('group_id'=>'g.id'))
->join(array('ug'=>'user_groups'),"ug.group_id=g.id",array('user_group_id'=>'ug.id'))
->where('ug.user_id = ?', $userId)
->where('ru.resource = ?', $resource)
->where("ru.privilege = ?", $privilege);
echo (string)$select;

Related

Writing PDO DELETE query values

I was wondering if we can still use values from a freshly DELETE row as a SELECT or do we really need to SELECT it before ?
Example :
Transform this
$foo = $db->prepare("SELECT * FROM table WHERE id= :id");
$foo->execute(array(
"id" => $table_id
));
$foo = $foo->fetch(PDO::FETCH_ASSOC);
$delete_foo = $bdd->prepare("DELETE FROM table WHERE id = :id");
$delete_foo->execute(array(
"id" => $table_id
));
echo $foo['name'] . " has been deleted !";
Into this :
$delete_foo = $bdd->prepare("DELETE FROM table WHERE id = :id");
$delete_foo->execute(array(
"id" => $table_id
));
$delete_foo = $delete_foo->fetch(PDO::FETCH_ASSOC);
echo $delete_foo['name'] . " has been deleted !";
It would be easier. I was just wondering, I use the 1st method but it just went in mind and I don't find answers.
In postgresql, there is a proprietary extension to the delete statement called RETURNING. Sql Server provides something similar, they call it OUTPUT
For example, OUTPUT DELETED.* in the following DELETE statement
returns all columns deleted from the ShoppingCartItem table:
DELETE Sales.ShoppingCartItem
OUTPUT DELETED.*;
Unfortunately, mysql does not have anything like the above. If you delete a row it's gone (unless you roll back the transaction instead of commiting). If you want to Select the data, you need to execute the SELECT before the DELETE
DELETE queries wont return any results (besides rows affected), so a PDO::query wont have any usable data to fetch.
For the example provided, an extra select query just makes no sense. As you have your $value already.
I would rather say that you need to simplify your PDO code at whole. Compare the below code snippet with yours
$foo = $db->run("SELECT foo FROM table WHERE value = ?", [$value])->fetchColumn();
$db->run("DELETE FROM table WHERE value = ?", [$value]);
echo "$foo has been deleted!";
the run() function can be achieved by a very small PDO modification:
class MyPDO extends PDO
{
public function run($sql, $args = NULL)
{
$stmt = $this->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
the code is taken from my article, Simple yet efficient PDO wrapper

How to use ParseObject update field?

How to use Extend to update the field?
Example SQL: UPDATE Empresa SET categorias = 'cat_01' WHERE id_user = 'dlkj83d'
$query = new ParseQuery('Empresa');
$query->equalTo("users", Session::get('user'));
$query->add("categorias", 'cat_01'); <- ERROR
$query->save();
Test
$query = new ParseQuery('Empresa');
$query->equalTo("users", Session::get('user'));
$empresa = new ParseObject($query);
$empresa->add('categorias', array('cat_01'));
$empresa->save();
Does not work.
:(
There is no such ParseQuery::add() function. You are trying to cobble an UPDATE into a SELECT... you have to first find() the resulting ParseObjects and iterate through each one:
$query = new ParseQuery('Empresa');
$query->equalTo('users', Session::get('user'));
$results = $query->find(); // Returns an array of ParseObjects
foreach ($results as $res) {
$res->add('categorias', 'cat_01');
$res->save(); // Make sure you write Exception handling for this in the future
}
There is also a ParseObject::set and setArray function that will achieve the same thing, as add may be getting deprecated.
Perfect.
$query = new ParseQuery('Restaurante');
$query->equalTo("users", Session::get('user'));
$results = $query->first();
$results->add('categorias', array('cat_02'));
$results->save();
I still have some difficulties regarding the method NoSQL. Knowing that the reasoning is different.
Thank you for your attention.
You are part of the development team?

Can raw SQL be parsed into a Zend\Db\Sql\Select object?

is there a way to parse a SQL statement string into a Zend\Db\Sql\Select object?
something along the lines of:
$sql = 'SELECT * FROM table WHERE col1 = "x"';
$select = new Select();
$select->parseSql($sql);
I have many uses for this, basically maintaining the use of a Select object when executing a raw SQL statement, for example to pass to Zend\Paginator\Adapter\DbSelect without having to write a custom Zend\Paginator\Adapter\DbSelect Class.
got one answer through a twitter response, specific to the Paginator use case:
$resultSet = iterator_to_array($adapter->query($sql_statement)->execute());
$iteratorAdapter = new ArrayAdapter($resultSet);
$paginator = new Paginator($iteratorAdapter);

Performant zend pagination without zend_db_select

This is just an example, but most of the queries in my application are like this:
public function getCommentsByPost($postid)
{
$db = Zend_Registry::get('db');
$sql = 'SELECT * FROM comments c
LEFT JOIN users u ON u.user_id = c.comment_userid
WHERE c.comment_postid = ?';
$statement = $db->query($sql, array($postid));
$rows = $statement->fetchAll();
$comment = null;
foreach($rows as $row){
$comment = new Model_Comment();
$comment->populate($row);
$this->list[] = $comment;
}
return $this->getList();
}
I don't use zend_db_select because usually there are a lot of joins involved and the queries are quite complicated. But I need to add pagination for my page listing, and I'm afraid that using the array adapter won't be very effective, because Zend will select every row from my 'posts' table. Are there any ways around this, or should I implement my own pagination functionality?
There isn't a way of getting an SQL string into the Zend Select object. You'd need an SQL parser for that and ZF doesn't have that.
You'd have to refactor the statements into the Zend Select object, this can be very annoying and difficult at times, I didn't do it on my first ZF project. I wish I had done now as it is the best way of doing it and ensures you can take advantage of all the extras, E.g. pagination.
I use something similar to the following with a zend_db_select model and it works with zend pagination
$select = $this->model->select(Zend_Db_Table::SELECT_WITH_FROM_PART)
->setIntegrityCheck(false);
$select->where('table1.field = ?', $field)
->join('table2','table1.field = table2.feild')
->where('table2.field = ?', $field)
->order('field DESC');
$object = $this->model->fetchAll($select);

How to get "field names" using PHP ADOdb?

I'm using PHP ADOdb and I can get the result set:
$result = &$db->Execute($query);
How do I get the field names from that one row and loop through it?
(I'm using access database if that matters.)
It will depend on your fetch mode - if you setFetchMode to ADODB_FETCH_NUM (probably the default) each row contains a flat array of columns. If you setFetchMode to ADODB_FETCH_ASSOC you get an associative array where you can access each value by a key. The following is taken from ADODB documentation - http://phplens.com/lens/adodb/docs-adodb.htm#ex1
$db->SetFetchMode(ADODB_FETCH_NUM);
$rs1 = $db->Execute('select * from table');
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs2 = $db->Execute('select * from table');
print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1')
print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1')
To loop through a set of results:
$result = &$db->Execute($query);
foreach ($result as $row) {
print_r($row);
}
Small improvement to the solution posted by #thetaiko.
If you are ONLY needing the field names, append LIMIT 1 to the end of your select statement (as shown below). This will tell the server to send you a single row with column names, rather than sending you the entire table.
SELECT * FROM table LIMIT 1;
I'm working with a table that contains 9.1M records, so this minor change speeds up the query significantly!
This is a function I use to return a field array - I've stripped out some extra stuff that, for example, allows it to work with other DBs than MySQL.
function getFieldNames($strTable, $cn) {
$aRet = array();
# Get Field Names:
$lngCountFields = 0;
$strSQL = "SELECT * FROM $strTable LIMIT 1;";
$rs = $cn->Execute($strSQL)
or die("Error in query: \n$strSQL\n" . $cn->ErrorMsg());
if (!$rs->EOF) {
for ($i = 0; $i < $rs->FieldCount(); $i++) {
$fld = $rs->FetchField($i);
$aRet[$lngCountFields] = $fld->name;
$lngCountFields++;
}
}
$rs->Close();
$rs = null;
return $aRet;
}
Edit: just to point out that, as I say, I've stripped out some extra stuff, and the EOF check is therefore no longer necessary in the above, reduced version.
I initally tried to use MetaColumnNames, but it gave differing results in VisualPHPUnit and actual site, while running from the same server, so eventually
I ended up doing something like this:
$sql = "select column_name, column_key, column_default, data_type, table_name, table_schema from information_schema.columns";
$sql .= ' where table_name="'.$table.'" and table_schema="'.$database_name.'"';
$result = $conn->Execute($sql);
while($row = $result->fetchRow()) {
$out[] = strToUpper($row['column_name']);
}
I think it should work with mysql, mssql and postgres.
The benefit of doing it like this, is that you can get the column names, even if a query from a table returns an empty set.
If you need the Coloumn names even for empty tables or for joins about multiple tables use this:
$db->Execute("SELECT .......");
// FieldTypesArray - Reads ColoumnInfo from Result, even for Joins
$colInfo = $res->FieldTypesArray();
$colNames = array();
foreach($colInfo as $info) $colNames[] = $info->name;
The OP is asking for a list of fieldnames that would result of executing an sql statement stored in $query.
Using $result->fetchRow(), even with fetch mode set to associative, will return nothing if no records match the criteria set by $query. The $result->fields array would also be empty and would give no information for getting the fieldnames list.
Actually, we don't know what's inside the $query statement. Besides, setting limit to 1 may not compatible with all database drivers supported by PHP ADOdb.
Answer by Radon8472 is the right one, but the correct code could be:
$result = $db->Execute($query);
// FieldTypesArray - an array of ADOFieldObject Objects
// read from $result, even for empty sets or when
// using * as field list.
$colInfo = [];
if (is_subclass_of($result, 'ADORecordSet')){
foreach ($result->FieldTypesArray() as $info) {
$colInfo[] = $info->name;
}
}
I have the habit of checking the class name of $result, for as PHP ADOdb will return false if execution fails.

Categories