Zend Framework: Coding by Convention - Part 2
A number of components sometimes accept strings or arrays as parameter. Developers coming from statically typed languages might cringe a bit, but this is really just about taking advantage of the flexibility of the language. With flexibility comes freedom and complexity, so having an established convention helps quite a bit.
One component that makes extensive use of accepting both strings and arrays is Zend_Db_Select. When building queries, it allows you to specify parameter either way:
// SELECT username FROM users WHERE id = 1
$select->from('users', 'username')->where('id = ?', 1);
// Same query again, with array syntax
$select->from(array('users'), array('username'))->where('id = ?', 1);
This can make for much more readable queries when things become more complex:
class Friendships extends Zend_Db_Table_Abstract
{
/**
* Select users suggested as friends
*
* Algorithm picks out your friends' friends that you don't already
* have added to your list, but which at least $threshold friends have in common.
*
* @param string $useruuid Avatar's UUID
* @param int $threshold How many friends must have the person in common
* @return Zend_Db_Table_Rowset_Abstract
*/
public function getSuggested($useruuid, $threshold = 4,
$page = null, $itemsPerPage = null)
{
$select = $this->select()
->setIntegrityCheck(false)
// friends
->from(array('f' => $this->_name),
array()) // no columns
->where('f.useruuid = ? AND f.dateaccepted IS NOT NULL', $useruuid)
// friends' friends that aren't me
->join(array('ff' => $this->_name),
'f.frienduuid = ff.useruuid AND ' .
'ff.frienduuid != f.useruuid AND ' .
'ff.dateaccepted IS NOT NULL',
array()) // no columns
->join(array('u' => 'users'),
'ff.frienduuid = u.uuid',
array('*'))
// my friends again (left joined)
->joinLeft(array('mf' => $this->_name),
'ff.frienduuid = mf.frienduuid AND ' .
'mf.useruuid = f.useruuid AND ' .
'mf.dateaccepted IS NOT NULL',
array()) // no columns
// filter my friends' friends that aren't my friends
->where('mf.frienduuid IS NULL')
->group('ff.frienduuid') // group by suggested friends
->having('COUNT(f.frienduuid) >= ?', $threshold)
->order('COUNT(f.frienduuid) DESC');
if ($page !== null or $itemsPerPage !== null) {
$select->limitPage($page, $itemsPerPage);
}
return $this->fetchAll($select);
}
}
Print This Post
Zend Framework: Coding by Convention
This is really something I've been wanting to point out because, for one, I very much like and agree with the approach, and second, it's something that any developer using the Zend Framework should digest and take into consideration when writing their own code.
There are numerous components that will accept configuration options, and usually that method is called setOptions($array) and it accepts an associative array (key/value pairs) as parameter.
And often, setOptions() iterates over the array and calls setOption($key, $value) as demonstrated by the following example:
public function setOptions($options)
{
foreach ($options as $option => $value) {
$this->setOption($option, $value);
}
return $this;
}
And typically, setOption() takes the first parameter (the key) and checks whether there's a method that matches "set" followed by the key name. For example, setOption('active', true) will end up calling setActive(true). The code often looks similar to the following:
public function setOption($option, $value)
{
$method = 'set' . $option;
if (method_exists($this, $method)) {
$this->$method($value);
} else {
throw new Exception('Unknown option: ' . $option);
}
return $this;
}
This comes in handy when you're trying to configure a component that accepts a plethora of options, which you can then conveniently store in an array.
Furthermore, the constructor of a class could accept $options as first parameter (as is often the case), and also check whether it is an instance of Zend_Config, in which case it first converts it using $options = $options->toArray();
public function __construct($options = null)
{
if ($options instanceof Zend_Config) {
$options = $options->toArray();
}
if (is_array($options)) {
$this->setOptions($options);
}
}
Another convention you may have noticed is that the setter methods tend to "return $this;". This is known as fluent interface and allows for more concise, readable code by being able to chain method calls.
Print This Post