Performant zend pagination without zend_db_select - php

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

Related

Lithium Framework MySQL raw query

I am fairly new to Lithium PHP Framework but I do know how to use the basic database abstraction method. I am using a plugin to build menu trees and it works great, although I want to use Lithium's abstraction layer for the functions. I'm stuck on this:
public function get_menuItems($menu_id) {
// retrieve the left and right value of the $root node
$result = $this->database->query("SELECT * FROM ".$this->tbl_menu_items." WHERE menu_id = '$menu_id'");
if ($this->database->num_rows($result)) {
$right = array();
$result = $this->database->query("SELECT node.title, node.type, node.class_name, node.content, node.id AS id, node.lft AS lft, node.rgt AS rgt, (COUNT(parent.title) - 1) AS depth FROM ".$this->tbl_menu_items." AS node CROSS JOIN ".$this->tbl_menu_items." AS parent WHERE node.menu_id = '$menu_id' AND parent.menu_id = '$menu_id' AND node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.id ORDER BY node.lft");
$tree = array();
while ($row = mysql_fetch_assoc($result)) {
$tree[] = $row;
}
} else {
$tree = false;
}
return $tree;
}
More so the $result part. I don't know how Lithium could handle all the "AS"'s and whatnot. I tried a few different ways to execute the query as is but no luck.
Any help would be greatly appreciated and I hope I am explaining it well enough.
If your php version is 5.4 you can use li3_tree behavior. Take a look at it, as it is a good example on how to implement such recursive behavior.
Also, I would strongly suggest you to take a look at "Using Models" and "Adding functionality to Lithium models" as your example code could benefit a lot.

symfony2 doctrine fetch while without creating complete array of results

I am looking to export to a csv file using doctrine. However the data is likely to be quite large. Therefore I dont want to ouput to the results to a complete array. I want to traverse the results iteratively.
I have tried looking here
doctrine docs
The PHP looks something like this
$result = mysql_query("SELECT * FROM bigtable");
while($row = mysql_fetch_assoc($result)) {
// do code iteratively here
}
Not sure how you do the same thing in doctrine for symfony2
This is from the doctrine documentation about batch processing:
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
foreach ($iterableResult AS $row) {
// do stuff with the data in the row, $row[0] is always the object
// detach from Doctrine, so that it can be Garbage-Collected immediately
$this->_em->detach($row[0]);
}
'select u ....is the equivalent to your SELECT * ...
$result = mysql_query("SELECT * FROM bigtable");
while($row = mysql_fetch_assoc($result)) {
// do code iteratively here
}
is Flat PHP and symfony2/doctrine is designed to avoid it. http://symfony.com/doc/2.0/book/from_flat_php_to_symfony2.html
kudos to Dirk his answer is correct. If you want to iterat eover entity models then thats the way to go. Sometimes you may not have a model mapped setup for the sql results you are returning. If you want to do it over a raw sql file then this is how I did it. I would be interested to hear more thoughts.
You pass the doctrine connection into the class via your controller
Controller
function indexAction(){
$className = new ClassName($this->getDoctrine()->getEntityManager());
}
You can then create raw sql lookup
Entity
function __construct($entity){
$this->connection = $entity->getConnection();
}
function saveToCSV
{
$stmt = $this->connection->prepare("SELECT * FROM bitTableExample ");
$stmt->execute();
while($row = $stmt->fetch()){
// append to csv file
}
}

Fetching Collection of SugarCRM Beans

Programmatically speaking, is there a way to fetch an array or collection of SugarCRM bean objects?
That is, let's say I wanted to fetch a number of account rows that included the word Bank in their name. With raw SQL, I'd do something like this
SELECT *
FROM Accounts
WHERE name LIKE '%Associates%`;
Is there a way using the SugarCRM ORM to so something similar? If not, how do SugarCRM programmers typically handle this situation? I realize I could hack something together by selecting a list of IDs from the database
$db = DBManagerFactory::getInstance();
$result = $db->query('SELECT id FROM Accounts where name LIKE "%Banking%"');
$accounts = array();
while($row = $db->fetchRow($result))
{
$accounts[] = BeanFactory::getBean('Accounts', $row['id']);
}
but in most ORM's that would be considered inefficient, and bad practice. Is there a better way?
(Perfectly ready for the answer to be "No, there's not way to do that". I'm new to the platform and trying to get my bearings)
Rather use
$bean = BeanFactory::getBean('Accounts');
$account_list = $bean->get_full_list("", "accounts.name like '%Associates%'");
As get_list will give you what you have defined for list_max_entries_per_page.
Here is a great resource for different ways to use the standard SugarBean versus SQL: here
For your example:
$bean = BeanFactory::getBean('Accounts');
$account_list = $bean->get_list("", "accounts.name like '%Associates%'");
This is an old question but for future readers, SugarBean methods get_list() and get_full_list() seem to be deprecated and it is advised to use SugarQuery instead.
$bean = BeanFactory::getBean('Accounts');
$query = new SugarQuery();
$query->from($bean, array('team_security' => false));
$query->where()->contains('name', 'Associates');
$account_list = $query->execute();

Select ignores where clause using Zend_Db_Select

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

How do you embedded your sql queries in php scripts (coding-style)?

how do you embed your sql scripts in php? Do you just write them in a string or a heredoc or do you outsource them to a sql file? Are there any best practices when to outsource them ? Is there an elegant way to organize this?
Use a framework with an ORM (Object-Relational Mapping) layer. That way you don't have to put straight SQL anywhere. Embedded SQL sucks for readability, maintenance and everything.
Always remember to escape input. Don't do it manually, use prepared statements. Here is an example method from my reporting class.
public function getTasksReport($rmId, $stage, $mmcName) {
$rmCondition = $rmId ? 'mud.manager_id = :rmId' : 'TRUE';
$stageCondition = $stage ? 't.stage_id = :stageId' : 'TRUE';
$mmcCondition = $mmcName ? 'mmcs.username = :mmcName' : 'TRUE';
$sql = "
SELECT
mmcs.id AS mmc_id,
mmcs.username AS mmcname,
mud.band_name AS mmc_name,
t.id AS task_id,
t.name AS task,
t.stage_id AS stage,
t.role_id,
tl.id AS task_log_id,
mr.role,
u.id AS user_id,
u.username AS username,
COALESCE(cud.full_name, bud.band_name) AS user_name,
DATE_FORMAT(tl.completed_on, '%d-%m-%Y %T') AS completed_on,
tl.url AS url,
mud.manager_id AS rm_id
FROM users AS mmcs
INNER JOIN banduserdetails AS mud ON mud.user_id = mmcs.id
LEFT JOIN tasks AS t ON 1
LEFT JOIN task_log AS tl ON tl.task_id = t.id AND tl.mmc_id = mmcs.id
LEFT JOIN mmc_roles AS mr ON mr.id = t.role_id
LEFT JOIN users AS u ON u.id = tl.user_id
LEFT JOIN communityuserdetails AS cud ON cud.user_id = u.id
LEFT JOIN banduserdetails AS bud ON bud.user_id = u.id
WHERE mmcs.user_type = 'mmc'
AND $rmCondition
AND $stageCondition
AND $mmcCondition
ORDER BY mmcs.id, t.stage_id, t.role_id, t.task_order
";
$pdo = new PDO(.....);
$stmt = $pdo->prepare($sql);
$rmId and $stmt->bindValue('rmId', $rmId); // (1)
$stage and $stmt->bindValue('stageId', $stage); // (2)
$mmcName and $stmt->bindValue('mmcName', $mmcName); // (3)
$stmt->execute();
return $stmt->fetchAll();
}
In lines marked (1), (2), and (3) you will see a way for conditional binding.
For simple queries I use ORM framework to reduce the need for building SQL manually.
It depends on a query size and difficulty.
I personally like heredocs. But I don't use it for a simple queries.
That is not important. The main thing is "Never forget to escape values"
You should always really really ALWAYS use prepare statements with place holders for your variables.
Its slightly more code, but it runs more efficiently on most DBs and protects you against SQL injection attacks.
I prefer as such:
$sql = "SELECT tbl1.col1, tbl1.col2, tbl2.col1, tbl2.col2"
. " FROM Table1 tbl1"
. " INNER JOIN Table2 tbl2 ON tbl1.id = tbl2.other_id"
. " WHERE tbl2.id = ?"
. " ORDER BY tbl2.col1, tbl2.col2"
. " LIMIT 10, 0";
It might take PHP a tiny bit longer to concatenate all the strings but I think it looks a lot nicer and is easier to edit.
Certainly for extremely long and specialized queries it would make sense to read a .sql file or use a stored procedure. Depending on your framework this could be as simple as:
$sql = (string) View::factory('sql/myfile');
(giving you the option to assign variables in the view/template if necessary). Without help from a templating engine or framework, you'd use:
$sql = file_get_contents("myfile.sql");
Hope this helps.
I normally write them as function argument:
db_exec ("SELECT ...");
Except cases when sql gonna be very large, I pass it as variable:
$SQL = "SELECT ...";
$result = db_exec ($SQL);
(I use wrapper-functions or objects for database operations)
$sql = sprintf("SELECT * FROM users WHERE id = %d", mysql_real_escape_string($_GET["id"]));
Safe from MySQL injection
You could use an ORM or an sql string builder, but some complex queries necessitate writing sql. When writing sql, as Michał Słaby illustrates, use query bindings. Query bindings prevent sql injection and maintain readability. As for where to put your queries: use model classes.
Once you get to a certain level, you realise that 99% of the SQL you write could be automated. If you write so much queries that you think of a properties file, you're probably doing something that could be simpler:
Most of the stuff we programmers do is CRUD: Create Read Update Delete
As a tool for myself, I built Pork.dbObject. Object Relation Mapper + Active Record in 2 simple classes (Database Abstraction + dbObject class)
A couple of examples from my site:
Create a weblog:
$weblog = new Weblog(); // create an empty object to work with.
$weblog->Author = 'SchizoDuckie'; // mapped internally to strAuthor.
$weblog->Title = 'A test weblog';
$weblog->Story = 'This is a test weblog!';
$weblog->Posted = date("Y-m-d H:i:s");
$weblog->Save(); // Checks for any changed values and inserts or updates into DB.
echo ($weblog->ID) // outputs: 1
And one reply to it:
$reply = new Reply();
$reply->Author = 'Some random guy';
$reply->Reply = 'w000t';
$reply->Posted = date("Y-m-d H:i:s");
$reply->IP = '127.0.0.1';
$reply->Connect($weblog); // auto-saves $reply and connects it to $weblog->ID
And, fetch and display the weblog + all replies:
$weblog = new Weblog(1); //Fetches the row with primary key 1 from table weblogs and hooks it's values into $weblog;
echo("<h1>{$weblog->Title}</h1>
<h3>Posted by {$weblog->Author} # {$weblog->Posted}</h3>
<div class='weblogpost'>{$weblog->Story}</div>");
// now fetch the connected posts. this is the real magic:
$replies = $weblog->Find("Reply"); // fetches a pre-filled array of Reply objects.
if ($replies != false)
{
foreach($replies as $reply)
{
echo("<div class='weblogreply'><h4>By {$reply->Author} # {$reply->Posted}</h4> {$reply->Reply}</div>");
}
}
The weblog object would look like this:
class Weblog extends dbObject
{
function __construct($ID=false)
{
$this->__setupDatabase('blogs', // database table
array('ID_Blog' => 'ID', // database field => mapped object property
'strPost' => 'Story', // as you can see, database field strPost is mapped to $this->Story
'datPosted' => 'Posted',
'strPoster' => 'Author',
'strTitle' => 'Title',
'ipAddress' => 'IpAddress',
'ID_Blog', // primary table key
$ID); // value of primary key to init with (can be false for new empty object / row)
$this->addRelation('Reaction'); // define a 1:many relation to Reaction
}
}
See, no manual SQL writing :)
Link + more examples: Pork.dbObject
Oh yeah i also created a rudimentary GUI for my scaffolding tool: Pork.Generator
I like this format. It was mentioned in a previous comment, but the alignment seemed off to me.
$query = "SELECT "
. " foo, "
. " bar "
. "FROM "
. " mytable "
. "WHERE "
. " id = $userid";
Easy enough to read and understand. The dots line up with the equals sign keeping everything in a clean line.
I like the idea of keeping your SQL in a separate file too, although I'm not sure how that would work with variables like $userid in my example above.

Categories