PHPEye开源社区 » Zend Framework 使用讨论 » 对Zend_Db_Table的一个扩展
《Programming PHP》第二版上市
2008-6-13 17:57 sentrychen
对Zend_Db_Table的一个扩展

我是刚学zf的水平有限,Zend_Db_Table给我感觉功能很不错,不过我还是根据自己的需要做了扩展。现在拿出来给大家参考一下。
希望大家给点意见和指出其中的错误。

主要是重写了__call方法l,增加了根据字段名直接查询的方法。另外在查询的时候会根据映射关系将全部父表的记录查出来。
比如有一个user表,dept表和company表。user表中有dept_id做为外键指向dept表。dept表中有外键company_id指向company表
如果我想查询全体男user按age排序的20条记录并从第10条开始查的时候只需要执行

$user = new User();
$rows = $user->findBySex('M','age',20,10);
然后就可以得到一个rowset,里面包含了user表的信息,dept表的信息和company表的信息。
相当于执行以下的一条sql语句。
select user.*,dept.*,company.* from user left join dept on user.dept_id = dept.id left join company on dept.company_id = company.id
where user.sex = 'M' order by age limit 20 offset 10;

同时也对row进行了扩展。比如你想获得其中一条记录中的所有dept信息时
可以这样
$row = $rows->current();
$row_dept = $row->getRowByDept(); //不会再查询数据库,Dept是映射表中的ruleKey而不是Class名,这点要注意。

如果你想直接获得dept的name信息时
可以这样
$name = $row->Dept_name;
也可以
$name = $row->getRowByDept('name');
如果你想获得Company的name时
$name = $row->Dept_Company_name;

$name = $row->getRowByDept()->getRowByCompany('name');
$name = $row->getRowByDept()->Company_name;



hmmm...有点注意的是,设定ruleKey的时候不要设成和数据库字段的前缀相同。

如果要进行比较复杂点的检索就用findAll()
Itc_Db_Table.php
[php]
<?php
/**
*
*
* LICENSE
*
* @category   Itc
* @package    Itc_Db_Table
* @subpackage Table
* @copyright  Copyright (c) 2005-2008 Itc
* @license   
*/

require_once 'Zend/Db/Table/Abstract.php';
require_once 'Itc/Db/Exception.php';
require_once 'Zend/Loader.php';

class Itc_Db_Table extends Zend_Db_Table_Abstract
{
   
    const REF_TABLE = 'refTable';
   
    protected $_rowClass = 'Itc_Db_Row';
   
    protected $_rowsetClass = 'Itc_Db_Rowset';
   
   
    public function findAll($where = null, $order = null, $count = null, $offset = null)
    {
            $select = $this->_db->select()
                            ->from($this->_name);
            try{
                $this->_joinReferenceTable($select);   
            }catch (Itc_Exception $e)
            {
                throw new Itc_Db_Exception($e->getMessage());
            }

            if ($where !== null) {
                $where = (array) $where;
                foreach ($where as $key => $val) {
                    if (is_int($key)) {
                        $val = $this->_name . (($v = strstr($val, '.')) ? $v : '.' .$val);
                        $select->where($val);
                    } else {
                        $key = $this->_name  . (($v = strstr($key, '.')) ? $v : '.' . $key);
                        $select->where($key, $val);
                    }
                }
            }

            if ($order !== null) {
                $order = (array) $order;
                foreach ($order as $val) {
                     $val = $this->_name . (($v = strstr($val, '.')) ? $v : '.' .$val);
                     $select->order($val);
                 }
            }

            if ($count !== null || $offset !== null) {
                $select->limit($count, $offset);
            }
            
            $stmt = $this->_db->query($select);
            $stmt->setFetchMode(Zend_Db::FETCH_ASSOC);
            
            $rows = $stmt->fetchAll();
            $data  = array(
                'table'    => $this,
                'data'     => $rows,
                'readOnly' => false,
                'rowClass' => $this->_rowClass,
                'stored'   => true
            );
            
            try {
                @Zend_Loader::loadClass($this->_rowsetClass);
            } catch (Zend_Exception $e) {
                throw new Itc_Db_Exception($e->getMessage());
            }
            return new $this->_rowsetClass($data);
    }
    /*
     *
     *   魔术方法,自动生成数据表检索方法。
     *
     *  findByColumn1AndColumn2...()
     *  @prama
     *  @return :Itc_Db_Rowset 查询结果集
     *         1.根据表字段生成检索方法,多个字段用And串联。
     */
    protected function __call($method, array $args)
    {
        //参数检查
        $matches = array();
        if (preg_match('/^findBy(\w+?)(?:And(\w+))*?$/', $method, $matches)) {
            $columnNum = count($matches) - 1;
            $argsNum = count($args);
            $where = null;
            $order = null;
            $count = null;
            $offset = null;
            if ($columnNum > $argsNum){
                throw new Itc_Db_Exception("Wrong parameter count for method '$method()'");
            }
            
           // $where = array();
            for($i = 0; $i < $columnNum; ++$i) {
                $column = $this->_getMatchColumnName($matches[$i + 1]);
                $where[] = $this->_db->quoteInto($column .' = ? ', $args[$i]);
            }
            //order
            if (isset($args[$i]))
            {
                $order = $args[$i];
            }
            //count offset
            if (isset($args[$i + 2]))
            {
                $count = (int) $args[$i + 1];
                $offset = (int) $args[$i + 2];
            }
            
            return $this->findAll($where, $order, $count, $offset);
        }
        throw new Itc_Db_Exception("Unrecognized method '$method()'");
    }
   
    /**
     *
     */
    protected function _joinReferenceTable(Zend_Db_Select $select, $alise = '')
    {
        foreach ($this->_getReferenceMapNormalized() as $rule => $map) {
            $t = $this->getParentTable($rule);
            $cond = '';
            if ($alise ==''){
                $curAlise = $this->_name;
                $refAlise = $rule;
            }
            else
            {
                $curAlise = $alise;
                $refAlise = $alise . '_' . $rule;
            }
            
            for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
                $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
                $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
                $cond .= $curAlise . '.' . $col . ' = ' . $refAlise . '.' . $refCol;
            }
            $cols = $t->getAliseCols($refAlise);
            $select->joinLeft($t->info('name') . ' AS ' . $refAlise, $cond, $cols);
            $t->_joinReferenceTable($select, $refAlise);
            
        }
        return $select;
    }
   
    /**
     *
     */
    public function getAliseCols($asPrefix)
    {
        $asCols = array();
        foreach ($this->_cols as $col)
        {
            $asCols[] = $col . ' AS ' . $asPrefix . '_' . $col;
        }
        return $asCols;
    }
   
   
    public function getPrefixCols($prefix)
    {
        $pfCols = array();
        foreach ($this->_cols as $col)
        {
            $pfCols[] = $prefix . '_' . $col;
        }
        return $pfCols;
    }
   
    /**
     *
     */
    protected function _getMatchColumnName($name)
    {
       foreach ($this->_cols as $columnName)
       {
           if (strtolower($name) == strtolower($columnName))
               return $columnName;
       }
       throw new Itc_Db_Exception('Specified column ' . $name . ' is not in the table ' . $this->_name);
    }
   
    /**
     *
     */
    public function getReferenceMap($ruleKey = null)
    {
        $thisClass = get_class($this);
        $refMap = $this->_getReferenceMapNormalized();
        if ($ruleKey !== null) {
            if (!isset($refMap[$ruleKey])) {
                throw new Itc_Db_Exception("No reference rule \"$ruleKey\" at table $thisClass ");
            }
            return $refMap[$ruleKey];
        }
        return $refMap;
    }

    /**
     *
     */
    public function getParentTable($ruleKey = null)
    {
        $parentTables = array();
        $maps = $this->getReferenceMap($ruleKey);
        
        if ($ruleKey !== null){
            $maps = array($ruleKey => $maps);
        }
        
        $db = $this->getAdapter();            
        foreach ($maps as $key => $map)
        {
            if (isset($map[self::REF_TABLE]) && $map[self::REF_TABLE] instanceof Itc_Db_Table){
               $parentTables[$key] = $map[self::REF_TABLE];
            }
            else{
                $tableClass = $map[self::REF_TABLE_CLASS];
                try {
                    @Zend_Loader::loadClass($tableClass);
                } catch (Itc_Exception $e) {
                    throw new Itc_Db_Exception($e->getMessage());
                }              
                $parentTables[$key] = new $tableClass(array(self::ADAPTER => $db));
                $this->_referenceMap[$key][self::REF_TABLE] = $parentTables[$key];
            }
        }
        if ($ruleKey !== null){
            return $parentTables[$ruleKey];
        }
        else{
            return $parentTables;   
        }
    }
}
[/php]

2008-6-13 18:00 sentrychen
回复 #1 sentrychen 的帖子

Itc_Db_Row.php
[php]
<?php

/**
* Itc_Db_Row
*
* @author SentryChen
* @version 1.0
* @create at 2008-6-2 ����04:52:39
*/

require_once 'Zend/Db/Table/Row/Abstract.php';       
require_once 'Itc/Db/Table.php';
require_once 'Zend/Loader.php';

class Itc_Db_Row extends Zend_Db_Table_Row_Abstract
{
        /*
         * 获得字段的值
         */
    public function get($columnName)
    {
        return $this->__get($columnName);
    }
    /*
     * 设置字段的值
     */
    public function set($columnName, $value)
    {
        $this->__set($columnName, $value);
    }
   
    /**
     *
     */
    public function getByRule($ruleKey, $columnName = null)
    {
        if ($ruleKey === null){
           return $this;
        }
        if ($columnName !== null){
            return $this->__get($ruleKey . '_' . $columnName);
        }
            
        $parentTable = $this->_getTable()->getParentTable($ruleKey);
        $prefixCols = $parentTable->getPrefixCols($ruleKey);
        if (array_intersect($prefixCols, array_keys($this->_data)) == $prefixCols){
            $rows = array();
            foreach ($this->_data as $col => $val) {
                    if (strpos($col, $ruleKey . '_') === 0){
                        $rows[ltrim($col, $ruleKey . '_')] = $val;
                    }
            }
            $rowClass = $parentTable->getRowClass();
            $data  = array(
                'table'    => $parentTable,
                'data'     => $rows,
                'readOnly' => $this->isReadOnly(),
                'rowClass' => $rowClass,
                'stored'   => true
            );
            try{
                @Zend_Loader::loadClass($rowClass);   
            }
                    catch (Exception $e){
                        require_once 'Itc/Db/Exception.php';
                        throw new Itc_Db_Exception($e->getMessage());
            }
            return new $rowClass($data);
        }
        else{
            return $this->findParentRow($parentTable, $ruleKey);
        }
        
    }
   
   
    protected function __call($method, array $args)
    {
        $matches = array();
        if (preg_match('/^getRowBy(\w+?)$/', $method, $matches)) {
            $ruleKey = $matches[1];
            $columnName = isset($args[0])? $args[0] : null;
            return $this->getByRule($ruleKey, $columnName);
        }
        else{
            return parent::__call($method, $args);
        }
    }
  
}


[/php]

2008-6-13 18:03 sentrychen
[php]
<?php
/**
* User
*
* @desc
*
*
* @author [email]sentrychen@hotmail.com[/email]
* @version 1.0
* @copyright ITC(SHENZHEN) CO.,LTD
* created at 2008-6-6 10:00
*/
//require_once 'Itc/Db/Table.php';
class Tbl_User extends Itc_Db_Table
{
    /**
     * The default table name
     */
    protected $_name = 'tbl_user';
    protected $_dependentTables = array('Tbl_CoeTimeLimit', 'Tbl_Workflow');
    protected $_referenceMap = array(
            'Dept' => array(
            self::COLUMNS=> 'dept_id', // 外键
            self::REF_TABLE_CLASS => 'Tbl_Dept', // 外键引用的表
            self::REF_COLUMNS => 'id', // 外键引用的表的主键
            self::ON_UPDATE => self::CASCADE
        ),
        'Part_time_dept' => array(
            self::COLUMNS=> 'part_time_dept_id', // 外键
            self::REF_TABLE_CLASS => 'Tbl_Dept', // 外键引用的表
            self::REF_COLUMNS => 'id', // 外键引用的表的主键
            self::ON_UPDATE => self::CASCADE
        )
        );
}

[/php]

2008-6-16 09:32 c61811
这才是真正的牛人啊

页: [1]


Powered by Discuz! Archiver 5.5.0  © 2001-2006 Comsenz Inc.