Magento Module Development Series – Part7 – Database and SQL Operations

In this blog we will see advanced operations in model and collection, to execute complex sql queries. We will look into collections and details of resource models.

First let’s look at model and collection architecture in magento. If you look at a magento model class it extends Mage_Core_Model_Abstract which intern extends Varien_Object.The class Mage_Core_Model_Abstract has function like getId(), load(), save(), delete() etc which we have seen in previous post.

SQL and Mysql4 Resource Files

Mysql4 resource files, as I mentioned earlier are used to perform database queries on tables. Below will list how we do sql operations inside resource files.

SELECT QUERY
Suppose we have the file Excellence_Test_Model_Mysql4_Test resource file.

<?php
class Excellence_Test_Model_Mysql4_Test extends Mage_Core_Model_Mysql4_Abstract
{
    public function _construct()
    {    
        $this->_init('test/test', 'test_id');
    }
    public function loadByField($field,$value){
    	$table = $this->getMainTable();
    	$where = $this->_getReadAdapter()->quoteInto("$field = ?", $value);
    	$select = $this->_getReadAdapter()->select()->from($table,array('test_id'))->where($where);
    	$id = $this->_getReadAdapter()->fetchOne($sql);
    	return $id;
    }
}

And here is the model file Excellence_Test_Model_Test

<?php
class Excellence_Test_Model_Test extends Mage_Core_Model_Abstract
{
	public function _construct()
	{
		parent::_construct();
		$this->_init('test/test');
	}
	public function loadByField($field,$value){
		$id = $this->getResource()->loadByField($field,$value);
		$this->load($id);
	}
}

In this file there are many new functions which have been used. Let’s take them one by one.
In our model file, we have used this function

$id = $this->getResource()->loadByField($field,$value);

$this->getResource() function returns the resource model’s object. So we are simply calling the loadyByField($field,$value) function in the resource model.
getTable() function
Now in our Mysql4 file, in the loadByField() function we have used $table = $this->getMainTable(); .This function returns the table name of the current model, i.e the one defined in the class’s constructor. If you want to get table name of another sql table in magento, we need to the getTable() call e.g

$table = $this->getTable(‘newsletter/subscribe’);

_getReadAdapter() function
Next we have: $where = $this->_getReadAdapter()->quoteInto(“$field = ?”, $value);
Here the first function too see is the $this->_getReadAdapter() function. This function return an object which has function to perform database query, like fetch(), fetchAll(), query(), etc.
Next, in read adapter, we can perform operations like:

  1. fetchAll() : returns entire database table in multiple dimensional array as per the select query. e.g if we are executing an sql like “select * from table_name”, we would use the fetchAll() function to get all the values.
  2. fetchRow(): returns a single row of the database table as per the select query. e.g if we are executing sql like “select * from table where id = ‘1’” would use fetchRow() to get the single row.
  3. fetchCol(): returns single column of a database table as per the select query. e.g if query is a select name from table” we would use fetchCol() function.
  4. fetchOne(): return since value, i.e single row and single column. e.g if sql query “select count(*) from table” then we would use fetchOne()

The read adapter has many other functions as well, but above are the important functions used.
_quoteInfo() function
Another function we have used is quoteInto(): This function is used to create our where conditions. In this the actual value is substituted into the question mark we have written. For e.g $this->_getReadAdapter()->quoteInto(“user_id = ?”, 3); translates to
“user_id = 3”. Another frequently used variation of quoteInfo() is

$where = $this->_getReadAdapter()->quoteInto("$field IN(?)", array(1,2,3));

this translates to “field in (1,2,3)”

Getting deeper into the quoteInto() function: If we have multiple where conditions we can use

$where = $this->_getReadAdapter()->quoteInto("$field = ? AND ", $value).$this->_getReadAdapter()->quoteInto("$field1 = ? OR ", $value1).$this->_getReadAdapter()->quoteInto("$field2 = ?", $value2);

So this way, we can have multiple where conditions.

Continuing on the loadByField function in our resource model, next we have
$select = $this->_getReadAdapter()->select()->from($table,array(‘test_id’))->where($where);
Here we have created the select query using the select(), and on that called the from() function.
from() function
The from() function takes many different parameters, if you want to fire a simple select * query, you need to only pass the table name like from($table).
But right now, I wanted to select just a single column that is test_id, so I passed an array with column name to select. i.e array(‘test_id’). If we want to select multiple column, we need to array(‘test_id’,’col1’,’col2’). This will create a sql like
“select test_id,col1,col2 from $table”. But support we want a sql query like
“select test_id as id,col1 as column1 from $table” , i would call

from($table,array('id'=>'test_id','column1'=>'col1'));


Note:
If you want to see the exact sql that is generated by your function, you can any time do this

<?php echo $select; ?>

Again, back to the loadByField() function. The last line

<?php
	$id = $this->_getReadAdapter()->fetchOne($sql);
?>

This is already explained, above.

Select Object

The select() function we used above return a select object which provides us with many more functions to perform all operations on a sql query. The class for the select object is Varien_Db_Select, where you can look into more details for all functions. Let look at a sample php code below.

public function getAll(){
		$table = $this->getMainTable();
		$where = $this->_getReadAdapter()->quoteInto("id = ?", 123);
$select = $this->_getReadAdapter()->select()->from($table)->columns(array('test_id'))->where($where)->limit(10,5)->order('created_time')->group('list_id')->having('list_id > ?',10);		
echo $select;
	}

As we can see all function in the select object, return the select object itself. Due to this we are able to chain the functions.
In this example, I have used almost all the sql select parameters you might require expect joins, which will be covered later.
Let’s take each function one at a time.

  1. select() : used to initialize the sql query. This returns the Select object, which is the object on which we do all our operations.
  2. from(): table name on which select operation is done. we have seen this in detail above.
  3. columns() : here we can specify exactly which columns to select in the query
  4. where() : used to give the where condition
  5. limit(): takes to parameters. First are how many rows to return and second is offset from where to start. So in current case, it means return 10rows, leaving first 5rows.
    this uses the LIMIT 10 OFFSET 5 syntax in sql query, rather than LIMIT 10,5
  6. order(): you can set how you want to order the result set. Default is ascending order. But if you want to mentioned descending order you do order(array(‘created_time Zend_Db_Select::SQL_DESC))
  7. group(): put group by clause in your select query in the specified field.
    Having(): put having condition for group by clause.
  8. JOINS
    Let look at sample function.

    public function joinUs()
    {
    		$table = $this->getMainTable();
    		$table2 = $this->getTable('customer/entity');
    		$cond = $this->_getReadAdapter()->quoteInto('t1.id = t2.customer_id','');
    		$where = $this->_getReadAdapter()->quoteInto('t1.list_id = "?"',123);
    		$select = $this->_getReadAdapter()->select()->from(array('t1'=>$table))->join(array('t2'=>$table2), $cond)->where($where);
    		echo $select."<br/>";
    		$select = $this->_getReadAdapter()->select()->from(array('t1'=>$table))->joinLeft(array('t2'=>$table2), $cond)->where($where);
    		echo $select."<br/>";
    		$select = $this->_getReadAdapter()->select()->from(array('t1'=>$table))->joinRight(array('t2'=>$table2), $cond)->where($where);
    		echo $select."<br/>";
    		echo $select."<br/>";
    }
    

    This is the output I got in the my pc

    SELECT `t1`.*, `t2`.* FROM `aws` AS `t1` INNER JOIN `customer_entity` AS `t2` ON t1.id = t2.customer_id WHERE (t1.list_id = "123")
    SELECT `t1`.*, `t2`.* FROM `aws` AS `t1` LEFT JOIN `customer_entity` AS `t2` ON t1.id = t2.customer_id WHERE (t1.list_id = "123")
    SELECT `t1`.*, `t2`.* FROM `aws` AS `t1` RIGHT JOIN `customer_entity` AS `t2` ON t1.id = t2.customer_id WHERE (t1.list_id = "123")
    

    So, here you can see clearly how to use joins. I don’t think much explanation is required, the function call are self-explanatory. We can also use joinCross, joinFull, joinNatural as well, if you require it in any query. Please look at Varien_Db_Select class for details.

    COUNT,MAX
    Again, lets take another sample code

    public function countUs(){
    		$table = $this->getMainTable();
    		$where = $this->_getReadAdapter()->quoteInto("id = ?", 123);
    		$select = $this->_getReadAdapter()->select()->from($table)->reset('columns')->columns(new Zend_Db_Expr('count(*)'));
    		echo $select.'<br>';
    		$select = $this->_getReadAdapter()->select()->from($table)->reset('columns')->columns(new Zend_Db_Expr('max(list_id)'));
    		echo $select.'<br>';
    	}
    

    This is the output.

    SELECT count(*) FROM `aws`
    SELECT max("list_id") FROM `aws`
    

    As you can see here, using Expr classes, we do mysql averaging functions.

    query() function
    In the end, if you have a very complex sql query, you want to execute you can simple use the query function.
    $sql = ‘…complex sql…’;
    $this->_getReadAdapter()->query($sql);

    UPDATE, DELETE Query
    public function updateUs(){
    		$table = $this->getMainTable();
    		$where = $this->_getWriteAdapter()->quoteInto('id = ?', 1);
    $query = $this->_getWriteAdapter()->update($table, array('product_id'=>2,'file_id'=>3),$where);	
    }
    

    Here you can see how to execute the update query.

    UPDATE `aws` SET `product_id` = ?, `file_id` = ? WHERE (id = 1)
    

    For delete query, it’s very simple

    $this->_getWriteAdapter()->delete($table,$where)->limit(1);
    

    Important Note: Its considered good practice in magento, to execute these sql query in the resource files only. You can get the read adaptor object in model, phtml or any other files and do the above operations in any file. But as good practices, always try to do the query in resource/mysql4 files only.

    Exercise:
    Well, it would best if can try out all the queries mentioned above yourself, to get a hold on all the syntax.
  • Thanks for this. I was able to optimize some of the code in a module to take rendering time down from 9.1 seconds to .2 seconds by doing a query instead of using a Mage::helper item.

  • carlosalvet

    I am using the Module created by Module Creator but i try to do the same as you in that an then use that search in a grid, but when a colled that method in my grid, throws me an error cannot convert my model in collection, ¿ Need you  to do some else in the collection file?

  • Kalashri

    Hello sir my select query is not running. please tell me where to put the code of select.because i am trying to run it from PHTML file. but not getting any thing. please tell me what to do

  • It is very complete and good article for me, thank you

  • Perfect description. Short, straightforward informations, perfect. Thank you.