代码之家  ›  专栏  ›  技术社区  ›  Gary Green

mysql+php:实现一个好的模型

  •  4
  • Gary Green  · 技术社区  · 16 年前

    在MVC模型视图控制器设计中,您实现了模型独立工作,并包含business逻辑,从数据库中提取信息。

    我对实现一个好模型的设计非常费劲。我知道需要从数据库中提取什么信息,我只是不知道实现它的最佳方法。我认为这个模型是程序API,我用诸如

    1. 如果我需要对字段进行排序怎么办?
    2. 如果我需要通过某个用户名/id进行选择,该怎么办?
    3. 如果我需要按特定字段分组怎么办?
    4. 如果我选择*只是因为调用函数可能需要下拉的任何信息,那么性能会受到多大的影响?

    我的API/模型变得非常膨胀,每个函数都有单独的函数和查询(稍微调整/更改一下)。

    例如

    $cart->getShoppingCart()
    $cart->getShoppingCartSortByTitle()
    $cart->getShoppingCartGroupByItemType()
    

    我觉得这会使模型非常膨胀,非常捆绑,产生大量重复的代码。有这种型号可能更好;

    更好的主意

    $cart->getItems('title, price')->order_by('title');
    

    在哪里? 'title, price' 是可以选择的mysql字段,显然是由 getItems() 功能。这样不仅限于返回某些字段。

    1. 我怎么能做到这一点?
    2. 这真的是个好模型吗?
    3. 你们还有什么建议吗?
    6 回复  |  直到 15 年前
        1
  •  1
  •   Otar    16 年前

    使用对象关系映射(ORM)…

    试试 Doctrine ORM 项目。

    另一个解决方案是 CodeIgniter 它有最好的活动记录库。非常有帮助。

    如果您仍然决定编写自己的类,那么使用php5 method chaining . 语法会更漂亮…

        2
  •  0
  •   adamnfish    16 年前

    “更好的主意”绝对是个更好的主意。你可以考虑看看 Django implements this 因为这就是这里使用的方法。Django是用python编写的,这确实使一些事情变得简单了一些,但是您也应该能够在php中使用其中的概念(只是稍微不那么整洁)。不过,总的来说,进行查询会创建一个查询对象,该对象具有order\u by等方法。应用这些方法将更改查询的状态,只有当实际执行查询时,它才需要生成SQL并在数据库上执行它。

    如果您坚持使用前者,您可能会考虑像许多现有框架一样,使用动态名称的“魔力方法”。例如,

    getShoppingCart_groupby
    getShoppingCart_orderby
    

    您将拥有一个带有动态参数列表的“catch all”方法,该列表读取被调用函数的名称,并在函数有效时执行所需的行为(如果没有,则引发标准的“method not found”错误)。这基本上与您现在所做的相同,但它将大量整理代码并整理您的模型。你需要PHP5,你在找 the __call magic method .

    Julien在他的回答中提到了代码点火器——编写好的模型非常困难,所以通常您最好使用现有的框架。(但是尝试是很有趣的!)

        3
  •  0
  •   Julien N    16 年前

    这个 Code Igniter framework 数据库管理允许您对数据库执行这种请求,从而限制复制/粘贴代码。
    Active Records module 在他们的文档中

    我觉得他们的模型很好。

        4
  •  0
  •   rr.    16 年前

    首先,确保您问自己的所有这些“问题”都与应用程序当前实际需要的特性有关。我在设计新项目时看到的最大问题之一是投机设计。只添加你需要的。为添加的内容编写单元测试。当您达到需要额外特性的程度时,如果需要,请重构到更好的设计。

    如果您确实需要前面的所有特性,我仍然推荐重构方法。实现一些您可以看到类似的特性,或者促成“膨胀”。完成后,后退一步,看看是否可以重构为更优雅的东西,或者是在不同的对象和/或方法之间更均匀地分配职责的东西。然后继续。各种各样的“模式”和“重构”书籍将在这里帮助您。

        5
  •  0
  •   Jauzsika    16 年前

    首先,你应该考虑的是:

    • 没有好的通用车型。每个项目都需要自己的模型。
    • 易于阅读、可管理的代码
    • 不要重复相同的代码(或查询),因此,如果您有某个任务的函数,并且希望以其他方式对其进行排序,请修改该函数本身,不要克隆它。
    • 使用复杂的数据结构(如数组或对象)向函数发送数据,因此不必总是修改函数所需的参数。
    • 资源使用情况。您越想让它无处不在,通用解决方案将使用越多的资源。

    如果我选择*只是因为调用函数可能需要下拉的任何信息,那么性能会受到多大的影响?

    这取决于你网站的负载。大多数情况下(如果不提取大的blob和文本)*是可以的,但是当资源不足时,必须指定列。这样可以节省一些IO时间。


    我觉得这会使模型非常膨胀,非常捆绑,产生大量重复的代码。有这种型号可能更好;

    也许试试这个:

    首先,对于复杂的查询,我使用很久以前为MySQL编写的这个类。这对他有很大帮助。

    class sqlAssembler
    {
        private $data = array();
        var $S = array();
        var $F = array();
        var $W = array();
        var $G = array();
        var $H = array();
        var $O = array();
        var $L = array();
    
        //Clause abbreviations
        var $clauselist = array
        (
        'S' => 'SELECT',
        'F' => 'FROM',
        'W' => 'WHERE',
        'G' => 'GROUP BY',
        'H' => 'HAVING',
        'O' => 'ORDER BY',
        'L' => 'LIMIT'
        );
    
        //Default clause separators
        var $clausesep = array
        (
        'S' => ',',
        'F' => ',',
        'W' => ' AND ',
        'G' => ',',
        'H' => ' AND ',
        'O' => ',',
        'L' => ''
        );
    
        function gen()
        {
            $tmp = '';
    
            foreach ( $this->clauselist as $area => $clause )
            {
                if ( count($this->{$area}) )
                {
                    $tmp .= ($clause != 'S' ? ' ' : '') . $clause . ' ';
                    for ($i=0; $i < count($this->{$area}); $i++) 
                    {
                        //echo $area = (string)$area;
                        $tmp .= $this->{$area}[$i];
                    } //for
                } //if
            } //foreach
    
            return $tmp;
        } //function
    
    
        function genSection($area, $showsection = 0)
        {
            $tmp = '';
            if ( count($this->{$area}) )
            {
                for ($i=0; $i < count($this->{$area}); $i++) 
                {
                    $tmp .= $this->{$area}[$i];
                } //for
            } //if
    
            return $tmp;
        } //function
    
        function clear()
        {
            foreach ($this as $area => $v)
            {
                //We only care about uppercase variables... do not declare any else variable with ALL UPPERCASE since it will be purged
                if (ctype_upper($area))
                {
                    if ($area == 'L')
                        $this->$area = '';
                    else
                        $this->$area = array();
                } //if
            } //foreach
        } //function
    
        public function add($area, $str, $criteria = 1, $sep = '#')
        {
            if ($criteria)
            {
                if ($sep == '#')
                    $sep = $this->clausesep[$area];
    
                //Postgres' OFFSET should be set like: $str = '25 OFFSET 0'
                //Not very neat I know, but fuck it
                if ($area == 'L')
                {
                    $this->{$area} = array();   
                } //if
    
                //$ref = $this->$area;
                $this->{$area}[] = (count($this->$area) ? $sep : '').$str;
    
                return count($this->$area)-1;
            } //if
        } //function
    
        public function del($area,$index)
        {
            if ( isset($this->{$area}[$index]) )
                unset($this->{$area}[$index]);
            else
                trigger_error("Index nr. {$index} not found in {$area}!",E_USER_ERROR);
        } //function
    
    //-*-* MAGIC CHAIN FUNCTIONS 
    
        public function S($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    
        public function F($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    
        public function W($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    
        public function G($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    
        public function H($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    
        public function O($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    
        public function L($str,$criteria = 1,$sep = '#')
        {
            $this->add(__FUNCTION__,$str,$criteria,$sep);
            return $this;
        } //function
    } //_sql
    

    也许试试这个:

    function getShoppingCart($d)
    {
        $xx = new sqlAssembler();
    
        $xx->S('*')->
        F('items')->
        //Notice, that we specified a criteria... if $d['id_item'] exists it will be joined to the WHERE clause, if not it will be left out
        W("(id_item > '{$d[id_item]}')",$d['id_item'])->
        //Same here
        O("dt DESC",$d['date'])
        $sql = echo $xx->gen();
    
        //id_item = 11, date = 2009-11-12
        //$sql = "SELECT * FROM items WHERE (id_item > '11') ORDER BY dt DESC";
    
        //id_item = null, date = null
        //$sql = "SELECT * FROM items";
    
        $data = sqlArray($sql);
    
        //... handle data
    }
    
        6
  •  0
  •   Adam Ramadhan    16 年前
    推荐文章