简体   繁体   English

CodeIgniter-最佳模型方法

[英]CodeIgniter - Best Model Approach

This has been bothering me for quite some time now. 这已经困扰了我好一段时间了。

Controller, on my take, is where I do validation, model calls, and displaying of data. 在我看来,控制器是我进行验证,模型调用和数据显示的地方。 And on the Model, which has only one purpose, is where I make my SQL queries. 在模型上,只有一个目的,就是我进行SQL查询的地方。

But what is the best approach on Model. 但是在Model上最好的方法是什么。 Do I have to make a lot of functions, with different conditions/approaches, or do I have to do it all in a single function, dedicated to a single table in my database. 我是否必须使用不同的条件/方法来创建许多函数,还是必须在单个函数中完成所有这些工作,这些函数专用于数据库中的单个表。 Take the code below for example: 以下面的代码为例:

Multiple Functions: 多种功能:

class Sword_model extends CI_Model {

    public function __construct()
    {
        $this->load->database();
        $this->load->library('session');
        $this->load->helper('url_helper');
    }

    public function getsword($swordid = NULL)
    {
        if($swordid === NULL){
            $query = $this->db->query("SELECT * FROM sword_tb");
            return $query->result_array();
        }

        $query = $this->db->query("SELECT * FROM sword_tb WHERE sword_id = ?", array($swordid));
        return $query->row_array();
    }

    public function getstrongswords($strong = NULL)
    {

        if($strong === NULL){
            return false;
        }

        $query = $this->db->query("SELECT * FROM sword_tb WHERE strong = ?", array($strong));
        return $query->result_array();

    }

    /*** AND MORE SUCCEEDING FUNCTIONS BELOW FOR DIFFERENT COLUMNS/CONDITIONS ***/
}

Pros: This is straight through and easier to understand (and I think is faster) 优点:这是直通且易于理解的(而且我认为速度更快)
Cons: You have to manually create functions for different conditions/columns 缺点:您必须手动创建用于不同条件/列的函数


Single Function: 单一功能:

class Sword_model extends CI_Model {

    public function __construct()
    {
        $this->load->database();
        $this->load->library('session');
        $this->load->helper('url_helper');
    }

    public function getsword($column = NULL, $value = NULL, $condition = 'AND')
    {

        $query = 'SELECT * FROM sword_tb';
        $count = count($column);
        for($x = 0; $x < $count; $x++){
            if($x > 0){
                $query .= ' '.$condition;
            } else {
                $query .= ' WHERE (';
            }
            $query .= ' '.$column[$x].' = ?';
        }
        $query .= ' ORDER BY sword_name';

        $query = $this->db->query($query, $value);
        return $query->result_array();

    }

}

with this single function approach, you can call this function by putting arrays as parameters like this: 使用这种单一函数方法,可以通过将数组作为参数放置来调用此函数,如下所示:

$this->Sword_model->getsword(array('sword', 'strong'), array(3, 1), 'OR');

And the query will look like this: 查询将如下所示:

SELECT * FROM sword WHERE (sword = ? OR strong = ?) ORDER BY sword_name

And if you left it behind blank, the query will look like this: 如果将其留为空白,查询将如下所示:

SELECT * FROM sword ORDER BY sword_name

Pros: Flexible 优点:灵活
Cons: Slower (?) than the first approach 缺点:比第一种方法慢(?)

What is more ideal between the two? 两者之间更理想的是什么? Or is there any other more ideal way? 还是还有其他更理想的方法?

I prefer to have each table to have it's own model with different preferable methods as you do in "Multiple Model". 我更喜欢让每个表都有自己的模型,就像在“多个模型”中所做的那样,使用不同的首选方法。 This way it is more maintainable and easier to approach & understand the codes. 这样,它更易于维护,更易于理解和理解代码。 The second one is though it's harder to approach and it may not be useful on all scenario. 第二个问题是,虽然很难解决,但可能并非在所有情况下都有用。

I have been using Codeigniter MVC & HMVC for more than 2 years. 我已经使用Codeigniter MVC和HMVC超过2年了。 First one is my choice for so far and it helps me to check and maintain my codes and also helps in my future updates/codes. 到目前为止,第一个选择是我的选择,它可以帮助我检查和维护代码,还可以帮助我将来进行更新/代码。

You can try ORM ( Object-relational mapping ) 您可以尝试ORM(对象关系映射)

Datamapper is an ORM library .It is designed to map your Database tables into easy to work with objects . Datamapper是一个ORM库,旨在将您的数据库表映射为易于使用的对象。

After successful installation of Datamapper 成功安装Datamapper之后

If you have a table called users 如果您有一个称为users的表

Create a model with name user 使用名称user创建模型

<?php

class User extends DataMapper {

    function __construct($id = NULL)
    {
        parent::__construct($id);
    }
}

In your controller, you can simply access data by 在您的控制器中,您可以通过以下方式简单地访问数据:

    $u = new user(); // Singular of model name is required
    $u->get();

    foreach ($u as $user => $value) 
    {
        echo $user->name;
    }

By this method you have to create each models for your tables and access data through your controller 通过这种方法,您必须为表创建每个模型并通过控制器访问数据

I prefer one generic model using which I can execute all mysql queries (which are not so complex, otherwise you need to write a query string and get it executed in few cases if there is a need of some too much complex query.) Otherwise you can call the models' generic functions from your controller by passing data array and table name and rest will be managed by the model. 我更喜欢一个通用模型,通过该模型我可以执行所有mysql查询(不是那么复杂,否则,如果需要太多复杂的查询,则需要编写查询字符串并在少数情况下使它执行。)可以通过传递数据数组和表名来从控制器调用模型的通用函数,其余部分将由模型管理。 Here is the generic model I use: 这是我使用的通用模型:

<?php

/*
  All user module related databse functions
  Author : Himanshu Upadhyay (himanshuvarun@gmail.com)
 */

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

class User_Model extends MY_Model {

    public function __construct() {
        parent::__construct();
    }

    public function get_rows($filters = array(), $table = TBL_USER) {
        return parent::get_rows($filters, $table);
    }

    public function get_columns($table = TBL_USER) {
        return parent::get_columns($table);
    }

    public function update_table($data, $where, $table = TBL_USER, $set = '') {
        return parent::update_table($data, $where, $table, $set = '');
    }

    public function get_count($filters = array(), $table = TBL_USER) {
        return parent::get_count($filters, $table);
    }

    public function insert($data, $table = TBL_USER) {
        return parent::insert($data, $table);
    }

    public function delete($where, $table = TBL_USER) {
        return parent::delete($where, $table);
    }
/* End of file user_model.php */
/* Location: ./application/models/user_model.php */
?>

And in your controller, you can call the model function like : 在您的控制器中,您可以像这样调用模型函数:

$user_data['first_name'] = 'ABC';
$user_data['last_name'] = 'XYZ';
$this->user_model->insert($user_data, 'tbl_users');  // This is calling `insert` function of user model with first array argument with the data with column names as keys of the array and 2nd argument is the table name.

Conclusion: So by this approach, you can load user_model in all the controllers and you can use all its generic functions in all of the controllers. 结论:因此,通过这种方法,您可以在所有控制器中加载user_model ,并且可以在所有控制器中使用其所有通用功能。 So this approach avoids redundant model functions to fetch, insert and update the data as well as it saves us by defining different models for each tables. 因此,这种方法避免了多余的模型函数来获取,插入和更新数据,并且通过为每个表定义不同的模型来节省了我们。

Most of the time multiple functions are easier to debug, comprehend and maintain. 大多数时候,多个功能更易于调试,理解和维护。

That said, you could make your multi-method model code a lot less repetitive. 也就是说,您可以使多方法模型代码的重复性降低很多。 Consider the following. 考虑以下。 ( __construct() not shown cause yours is fine.) __construct()未显示,因为您没问题。)

class Sword_model extends CI_Model
{
    protected $get_all_sql = 'SELECT * FROM sword_tb';
    protected $get_where_sql = "SELECT * FROM sword_tb WHERE sword_id = ?";

    public function getsword($swordid = NULL)
    {
        $sql = isset($swordid) ? $this->get_where_sql : $this->get_all_sql;
        $bind = isset($swordid) ? $swordid : FALSE;
        return $this->do_Query($sql, $bind);
    }

    public function getstrongswords($strong = NULL)
    {
        if(isset($strong))
        {
            return $this->do_Query($this->get_where_sql, $strong);
        }
        //Returning an empty array means the controller can skip doing a conditional 
        //check for a model return === FALSE.
        //foreach() won't choke on an empty array, but it will choke if passed FALSE
        return array();
    }

    protected function do_Query($sql, $binds = FALSE)
    {
        $query = $this->db->query($sql, $binds);
        return $query->result_array();
    }
}

However, the "flexible" approach can be useful is certain circumstances. 但是,在某些情况下,“灵活”的方法可能会有用。

The speed difference between "singles" vs "flexible" is negligible and not a consideration. “单人”与“灵活”之间的速度差异可以忽略不计,而不是一个考虑因素。 What does need to be considered is that "flexible" quickly becomes unwieldy as you try to respond to more "conditions". 需要考虑的是,当您尝试应对更多“条件”时,“灵活”很快变得笨拙。

There is something that will make writing "flexible" model methods easier - easier to write, comprehend, debug and maintain. 有一些东西会使编写“灵活的”模型方法更容易-易于编写,理解,调试和维护。 Instead of manually constructing query strings and passing them to $this->db->query() use Query Builder . 使用查询生成器,而不是手动构造查询字符串并将其传递给$this->db->query() It is designed exactly for situations where you need to conditionally build a query statement. 它专为需要有条件地构建查询语句的情况而设计。

Your "flexible" version of getsword() has at least one limitation and that is that you cannot SELECT columns that are not part of the WHERE clause. getsword() “灵活”版本至少有一个限制,那就是您不能选择不属于WHERE子句的列。

Using Query Builder here is one way you could implement a flexible method that builds queries for both AND WHERE and OR WHERE clauses. 在这里使用查询生成器是一种可以实现为AND WHERE和OR WHERE子句构建查询的灵活方法的方法。 Hopefully the DocBlock before the method will provide some insight. 希望该方法之前的DocBlock能够提供一些见解。

class Sword_model extends CI_Model
{
    protected $get_all_sql = 'SELECT * FROM sword_tb';
    protected $get_where_sql = "SELECT * FROM sword_tb WHERE sword_id = ?";

    /**
     * 
     * @param mixed $columns The columns to retrieve. Can be either a string 
     * e.g. 'title, content, date',
     * or it can be an array e.g. array('title', 'content', 'date') 
     * 
     * @param array $where If provided, must be an associative array where 
     * the key => value is 'field_name' => value_to_match, e.g. 
     * array('title' => "Kill Bill") 
     * $where requires a different structure when the $condition argument is 
     * "OR". In this case the value part should provide multiple values. 
     * These values can be provided either in an array 
     * or a comma separated string list. For instance:
     *    As an array, $where = array('field_name' => array('val1', 'val2'));

     *    As a string, $where = array('field_name' => 'val1, val2'));
     * 
     * @param string $condition For this example can be either 'AND' (default) or 'OR'
     */
    public function getsword($columns = NULL, $where = NULL, $condition = 'AND')
    {
        if(!empty($columns)) //No $columns means SELECT *
        {
            $this->db->select($columns);
        }

        $condition = strtoupper($condition); //Don't assume
        if(!empty($where))
        {
            if($condition === 'OR')
            {
                foreach($where as $key => $values)
                {
                    if(is_string($values))
                    {
                        $values = explode(', ', $values);
                    }
                    if(is_array($values))
                    {
                        foreach($values as $matching)
                        {
                            $match = [$key => $matching];
                            $this->db->or_where($match);
                        }
                    }
                    else
                    {
                        $this->db->or_where($key, $values);
                    }
                }
            }
            else
            {
                $this->db->where($where);
            }
        }

        return $this->db
          ->order_by('sword_name')
          ->get("sword_tb")
          ->result_array();
    }

}

You might look at the block 您可能会看一下街区

                        foreach($values as $matching)
                        {
                            $match = [$key => $matching];
                            $this->db->or_where($match);
                        }

and question the use of or_where() before calling where() . 并在调用where()之前or_where()的使用。 Query Builder is smart enough to know if any other WHEREs have been added and won't put "OR" in front of "WHERE" if it's not needed. 查询生成器足够聪明,可以知道是否添加了其他任何WHERE,并且如果不需要,也不会在“ WHERE”前放置“ OR”。

Let's look at some usage examples. 让我们看一些用法示例。

$this->sword_model->getsword();

Produces the query statement 产生查询语句

SELECT * FROM sword_tb ORDER BY sword_name SELECT * FROM sword_tb排序方式

$this->sword_model->getsword('model, sword_name, cost', array('model' => 'Broad'));

produces 产生

SELECT model, sword_name, cost FROM sword_tb WHERE model = Broad ORDER BY sword_name SELECT模型,sword_name,成本FROMwords_tb WHERE模型=广泛ORDER BY sword_name

$this->sword_model->getsword(NULL, array('model' => 'Broad', 'cost <' => 100));

SELECT * FROM sword_tb WHERE model = Broad AND cost < 100 ORDER BY sword_name SELECT * FROM sword_tb哪里模型=广泛AND成本<100 ORDER BY剑名

$this->sword_model->getsword('model, sword_name, cost',
      array('model' => 'Broad, Samurai', 'sword_name' => 'Excalibur'), 'or');

SELECT model, sword_name, cost FROM sword_tb WHERE model = Broad OR model = Samurai OR sword_name = Excalibur ORDER BY sword_name SELECT模型,sword_name,成本FROMwords_tb WHERE模型=广义OR模型=武士OR sword_name = Excalibur ORDER BY sword_name

$this->sword_model->getsword(NULL,
      array('model' => 'Broad, Samurai', 'sword_name' => ['Excalibur', 'Fred']), 'or');

SELECT * FROM sword_tb WHERE model = Broad OR model = Samurai OR sword_name = Excalibur OR sword_name = Fred ORDER BY sword_name 选择* FROM sword_tb,其中模型=宽泛OR模型=武士OR剑名=神剑OR剑名=弗雷德·ORDER BY剑名

NOTE: I removed the back-tics from the generated queries string because here on SO it looked weird. 注意:我从生成的查询字符串中删除了back-tic,因为在这里看起来很奇怪。 Rest assured, everything is properly escaped. 放心,一切都已妥当。

If you're worried about speed, I benchmarked the above. 如果您担心速度,我会以上述为基准。 The longest time required to build a query statement was 0.0007 second. 建立查询语句所需的最长时间为0.0007秒。 To build all five queries required a total of 0.0022 seconds - an average of 0.00044 each. 要构建所有五个查询,总共需要0.0022秒-每个平均0.00044秒。

Note, this is time to build the statement. 注意,现在是构建语句的时候了。 The time to retrieve data is not included because, well... I don't have the data. 检索数据的时间不包括在内,因为……我没有数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM