ZF-3399: Subclassing Zend_Db_Select/Zend_Db_Table_Select
Description
Subclasses of Zend_Db_Select cannot be used by Zend_Db_Table_Abstract::select() because it instantiates Zend_Db_Table_Select (which in turn extends Zend_Db_Select).
The only way around this is to first copy/paste the contents of Zend/Db/Table/Select.php and change the class it extends to your custom class. Then, you need to subclass Zend_Db_Table_Abstract and overload Zend_Db_Table_Abstract::select() to instantiate My_Db_Table_Select instead of Zend_Db_Table_Select.
Comments
Posted by Roger Hunwicks (rhunwicks) on 2008-10-22T03:50:38.000+0000
Rather than copy/paste, we are using a subclass of Zend_Db_Table, which applies the same approach as is used for the row and rowset subclasses:
New:
Updated:
I think this code could be copied straight into Zend_Db_Table_Abstract.
This allows users to override the Table_Select class in the same way they override the row or rowset classes, and would make the class more consistent.
Posted by Roger Hunwicks (rhunwicks) on 2008-10-22T07:43:09.000+0000
_where() might also need to be updated. Currently the class is fixed:
If we are allowed to specify our own class, it might not be a sub-class of Zend_Db_Table_Select.
I know that the normal practice would be to subclass Zend_Db_Table_Select and then make any changes necessary, but Zend_Db_Table_Select is a sub-class of Zend_Db_Select and this is also frequently sub-classed (e.g. to support bind variables). I have found it easier to incorporate the functionality I need by sub-classing Zend_Db_Select first (e.g. My_Db_Select extends Zend_Db_Select), and then sub-classing that to inherit the additional functionality at the table level (My_Db_Table_Select extends My_Db_Select).
This means that I have to keep My_Db_Table_Select as a verbatim copy of Zend_Db_Table_Select, except for the extends clause! It would be better if these classes used composition rather than inheritance, but that is another battle! I'm hoping that Zend_Db_Select will be enhanced to bind variables and I can forget about my sub classes.
In any case, for people in my situation who are deliberately using a Table Select that is not sub-classed from Zend_Db_Table_Select, the following might be better:
Posted by Roger Hunwicks (rhunwicks) on 2008-10-23T07:07:46.000+0000
There are other places where a function requires a Zend_Db_Table_Select, e.g. Zend_Db_Table_Row_Abstract->findDependentRowset, so removing the class requirement and replacing it with the check I suggest above is probably not a good idea as it will be required in many places.
A better approach might be to create Zend_Db_Table_Select_Interface and require that instread. Then our custom classes could just implement the interface.
Posted by Roger Hunwicks (rhunwicks) on 2009-04-22T04:58:38.000+0000
Zend_Db_Adapter is similarly affected. It has variables for $_defaultStmtClass and $_defaultProfilerClass but not one for $_defaultSelectClass. It would be better if the select class could also be changed easily.
I think this requires the addition of:
/** * Default class name for a Select statement. * * @var string */ protected $_defaultSelectClass = 'Zend_Db_Select';and:
/** * Get the default select class. * * @return string */ public function getSelectClass() { return $this->_defaultSelectClass; } /** * Set the default select class. * * @return Zend_Db_Adapter_Abstract Fluent interface */ public function setSelectClass($class) { $this->_defaultSelectClass = $class; return $this; }And updating:
/** * Creates and returns a new Zend_Db_Select object for this adapter. * * @return Zend_Db_Select */ public function select() { return new Zend_Db_Select($this); }To:
/** * Creates and returns a new Zend_Db_Select object for this adapter. * * @return Zend_Db_Select */ public function select() { Zend_Loader::loadClass($this->_defaultSelectClass); return new $this->_defaultSelectClass($this); }Posted by Roger Hunwicks (rhunwicks) on 2009-04-22T05:51:21.000+0000
For consistency and completeness we also need getter and setter methods for the $_select in Zend_Db_Table_Abstract:
/** * @param string $classname * @return Zend_Db_Table_Abstract Provides a fluent interface */ public function setSelectClass($classname) { $this->_selectClass = (string) $classname; return $this; } /** * @return string */ public function getSelectClass() { return $this->_selectClass; }Posted by julien PAULI (doctorrock83) on 2009-05-13T12:31:50.000+0000
I agree that for such uses cases (that I understand), some interfaces are highly needed.
Zend_Db_Select_Interface should then define all the Zend_Db_Select methods that are actually used by Zend classes which use Zend_Db_Select typed params (all ?) The Interface should also define a public function __construct(Zend_Db_Adapter_Abstract $adapter) signature
We should also create a Zend_Db_Table_Select_Interface extending Zend_Db_Select_Interface and adding Zend_Db_Table_Select methods to it for Zend object using Zend_Db_Table_Select objects as input method params
Posted by Claude Duvergier (cduv) on 2009-08-27T06:50:57.000+0000
When extending {{Zend_Db_Select::assemble()}} (to make it able to return only one part of generated SQL SELECT query) I ran into the same problem than Roger Hunwicks's {quote} I know that the normal practice would be to subclass Zend_Db_Table_Select and then make any changes necessary, but Zend_Db_Table_Select is a sub-class of Zend_Db_Select and this is also frequently sub-classed (e.g. to support bind variables). I have found it easier to incorporate the functionality I need by sub-classing Zend_Db_Select first (e.g. My_Db_Select extends Zend_Db_Select), and then sub-classing that to inherit the additional functionality at the table level (My_Db_Table_Select extends My_Db_Select).
This means that I have to keep My_Db_Table_Select as a verbatim copy of Zend_Db_Table_Select, except for the extends clause! It would be better if these classes used composition rather than inheritance, but that is another battle! I'm hoping that Zend_Db_Select will be enhanced to bind variables and I can forget about my sub classes. {quote} And copy/pasting {{Zend_Db_Table_Select}} proper code (the one that differs from {{Zend_Db_Select}}) into {{My_Db_Table_Select}} is quite "ugly" but seems to be the only way.
I think using interfaces in {{Zend_Db_Table_Abstract}} instead of concrete classes would be a good step forward.
Posted by Ralph Schindler (ralph) on 2009-09-17T11:16:41.000+0000
I agree, but this type of change would have to wait till 2.0 time as it would be a mostly-complete rewrite that is needed to facilitate this.