When porting a module of my site from XOOPS to Xoops², I crawled through the classes and it occured to me that some db/object accessing code is replicated throughout many classes. I wrote my own WhoswhoItem class (my module is a Who's who module for my student association), which extends XoopsObject and is the base class for all my other objects.
I think the following code is something that could (should?

) be included in the next RC/release inside the core XoopsObject class itself.
The base principle is that it uses the java-reflection-like capability of php and assumes proper naming conventions to create the concrete class instances. If this becomes a performance bottleneck, the createInstance method can be made abstract or overriden.
(forgive my typos, my own coding conventions, and so on)
le="color: #000000"><?php /** * @abstract */ class WhoswhoItem extends XoopsObject { function WhoswhoItem() { $this->XoopsObject(); } } /** * @abstract */ class WhoswhoItemHandler extends XoopsObjectHandler { var $concreteClassName; function WhoswhoItemHandler(&$db) { $this->XoopsObjectHandler($db); // Of course, it assumes a correct implementation // enforcing the naming conventions $handlerClassName = get_class($this); $this->concreteClassName = substr($handlerClassName, 0, -7); // 7 is the length of 'Handler' } /** * Returns the table used for persistance. * @abstract */ function getTableName() { } /** * Returns the complete Sql request for an Insert operation op the concrete handled subclass. * @abstract * @param int $id the id generated for this instance. * @param object &$toStore the instance to store. */ function getInsertRequest($id, $toStore) { } /** * Returns the complete Sql request for an Update operation op the concrete handled subclass. * @param object &$toStore the instance to store. * @abstract */ function getUpdateRequest($toStore) { } /* * Returns the column name of the primary key used to * retrieve instances from the table. Usuall 'id', or 'uid', .... * @abstract */ function getPKName() { } /** * Creates an instance of the concrete handled class. * @access protected */ function &createInstance() { $className = $this->concreteClassName; $value = new $className(); return $value; } /** * Creates a new instance of the concrete handled subclass. * @return bool $isNew Flag the object as "new"? * @access public */ function &create($isNew = true) { $value =& $this->createInstance(); if ($isNew) { $value->setNew(); } return $value; } /* * Retrieves data from DB and fills a new instance the concrete handled subclass with it. * Assumes the PK id is an int. * @access private * @param int $id ID * @return object instance, FALSE on fail */ function &get($id) { $sql = sprintf( "SELECT * FROM %s WHERE %s = %d ", $this->db->prefix($this->getTableName()), $this->getPKName(), intval($id)); if ( !$result = $this->db->query($sql) ) { return false; } $numrows = $this->db->getRowsNum($result); if ( $numrows == 1 ) { $value =& $this->createInstance(); $value->assignVars($this->db->fetchArray($result)); return $value; } return false; } /** * Stores a the concrete handled subclass. * @param object &$toStore an instance the concrete handled subclass. * @return bool TRUE on success */ function insert(&$toStore) { if ( get_class($toStore) != $this->concreteClassName ) { return false; } if ( !$toStore->isDirty() ) { return true; } if (!$toStore->cleanVars()) { return false; } if ($toStore->isNew()) { $generated_id = $this->db->genId( $this->getTableName()); $sql = $this->getInsertRequest($generated_id, $toStore); } else { $sql = $this->getUpdateRequest($toStore); } if (!$result = $this->db->query($sql)) { return false; } if (empty($generated_id)) { $generated_id = $this->db->getInsertId(); } $toStore->assignVar( $this->getPKName(), $generated_id); return true; } /** * Deletes an instance the concrete handled subclass. * @param object &$toDelete * @return bool TRUE on success */ function delete(&$toDelete) { if (get_class($toDelete) != $this->concreteClassName) { return false; } $sql = sprintf("DELETE FROM %s WHERE %s = '%s'", $this->db->prefix($this->getTableName()), $this->getPKName(), $toDelete->getVar($this->getPKName())); if (!$result = $this->db->query($sql)) { return false; } return true; } /** * Deletes all instances matching a set of conditions * @param object $criteria {@link CriteriaElement} * @return bool FALSE if deletion failed */ function deleteAll($criteria = null) { $sql = 'DELETE FROM '.$this->db->prefix($this->getTableName()); if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) { $sql .= ' '.$criteria->renderWhere(); } if (!$result = $this->db->query($sql)) { $this->notifyUpdate($sql, 'failed_update'); return false; } return true; } /** * Retrieves multiple instance of the concrete handled subclass. * @param object $criteria {@link CriteriaElement} * @param bool $id_as_key Use IDs as array keys? * * @return array Array of {@link XoopsGroupPerm}s */ function &getObjects($criteria = null, $id_as_key = false) { $ret = array(); $limit = $start = 0; $sql = 'SELECT * FROM '.$this->db->prefix($this->getTableName()); if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) { $sql .= ' '.$criteria->renderWhere(); if ( '' != $criteria->getSort() ) { $sql .= ' ORDER BY '.$criteria->getSort().' '.$criteria->getOrder(); } $limit = $criteria->getLimit(); $start = $criteria->getStart(); } $result = $this->db->query($sql, $limit, $start); if (!$result) { return $ret; } while ($myrow = $this->db->fetchArray($result)) { $value =& $this->createInstance(); $value->assignVars($myrow); if (!$id_as_key) { $ret[] =& $value; } else { $ret[$myrow[$this->getPKName()]] =& $value; } unset($value); } return $ret; } /** * Obtains the "db-quoted" version of the variables contents, to be able to store them directly. Just a helper method, not mandatory. * @access protected */ function "edCleanVars(&$object) { $toReturn = array(); foreach ($object->cleanVars as $k => $v) { $toReturn[$k] = $this->db->quoteString($v); } return $toReturn; } }
With this, in subclasses, you don't need the write the typical "get", "getObjects", "delete", ... methods. You only need to write:
o a Constructor
o the concrete implementation of getTableName()
o the concrete implementation of getInsertRequest()
o the concrete implementation of getUpdateRequest()
o the concrete implementation of getPKName()
All of which are quite simple and quite short methods. This makes your classes shorter to write and easier to maintain.