MySQL + PHP:实现一个好的模型

时间:2010-07-29 10:48:54

标签: php mysql model-view-controller design-patterns

在MVC模型视图控制器设计中,您可以将模型单独工作并包含商务逻辑,从数据库中提取信息。

我在实施一个好模型的设计上非常挣扎。我知道需要从数据库中下载哪些信息,我只是不知道实现它的最佳方法。我认为该模型是程序API,我用像

这样的问题超载自己
  1. 如果我需要对字段进行排序怎么办?
  2. 如果我需要按某个用户名/ ID选择怎么办?
  3. 如果我需要按特定字段分组怎么办?
  4. 如果我选择*只是因为调用函数可能需要删除任何信息,那么性能会受到多大影响?
  5. 我的API /模型变得非常臃肿,为每个功能提供单独的功能和查询(只是轻微调整/更改)

    例如

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

    我觉得这使得模型非常臃肿且非常紧密,创造了大量重复的代码。拥有这种模型可能会更好;

    更好的主意

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

    'title, price'是您可以选择的mySQL字段,显然是由getItems()函数验证的。这样,它不仅限于返回某些字段。

    1. 我怎么能实现这个目标?
    2. 这真的是一个好模特吗?
    3. 你们可以提出其他建议吗?

6 个答案:

答案 0 :(得分:1)

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

尝试Doctrine ORM项目。

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

如果您仍然决定编写自己的类,请使用PHP5的method chaining。语法会更漂亮......

答案 1 :(得分:0)

Code Igniter framework数据库管理允许您在数据库上执行此类请求,限制复制/粘贴代码。
请参阅其文档中的Active Records module

我认为他们的模型非常好。

答案 2 :(得分:0)

“更好的主意”绝对是一个更好的主意。您可以考虑查看Django implements this的方式,因为这是在那里使用的方法。 Django是用Python编写的,它确实使一些事情变得更容易,但你也应该能够在PHP中使用那里的概念(只是有点不那么整洁)。总而言之,创建查询会创建一个查询对象,该对象具有order_by等方法。应用这些方法将改变查询的状态,只有在实际执行查询时才需要生成SQL并在数据库上执行它。

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

getShoppingCart_groupby
getShoppingCart_orderby

你将拥有一个带有动态参数列表的'catch-all'方法,该列表读取被调用函数的名称,并在有效时执行所需的行为(如果有效则抛出标准的'找不到方法'错误它不是)。这与你现在正在做的基本相同,但它会整理代码并整理你的模型。你需要PHP5,而且你正在寻找the __call magic method

Julien在他的回答中提到了Code igniter - 编写好的模型非常困难,所以通常你最好使用现有的框架。 (但尝试很有趣!)

答案 3 :(得分:0)

首先,请确保您自问的所有“问题”都涉及您的应用程序现在实际需要的功能。我在设计新项目时遇到的最大问题之一是投机设计。只添加你需要的东西。为您添加的内容编写单元测试。当您到达需要额外功能的位置时,如有必要,请重构为更好的设计。

如果您确实需要预先提供所有功能,我仍然建议使用重构方法。实现一些您可以看到相似或有助于“膨胀”的功能。当你完成后,退后一步,看看你是否可以重构更优雅的东西或者在不同的对象和/或方法之间更均匀地分配责任的东西。然后继续。各种“模式”和“重构”书籍将在这里为您提供很多帮助。

答案 4 :(得分:0)

首先,你应该考虑的是:

  • 没有好的通用模型。每个项目都需要自己的模型。
  • 易于阅读,可管理的代码
  • 不要重复相同的代码(或查询),所以如果你有一个特定任务的功能,并希望以另一种方式进行排序,修改函数本身而不是克隆它
  • 使用复杂的数据结构(如数组或对象)将数据发送到函数,因此您不必总是修改函数所需的参数
  • 资源使用情况。你想要的就越多,一般的解决方案就是它将使用的资源越多。

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

这取决于您网站的负载。大多数时候(如果你没有拉大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
}

答案 5 :(得分:0)