web编程之路

php 模型类实例教程

2016-11-11  本文已影响357人  hopevow

在框架中,经常看到对数据库的操作进行链式操作,我们综合前面两个类,来实现一下这种效果。

老规矩,首先构造好我们的类

class model extends sp_obj {
  
    //表名
    public $tab;
    //存放where的字符串
    private $__where;
    //存放第二个参数
    private $_arg;
    //表示where是否已经进行过拼接
    private $_begin;
    //字段
    private $_fields;
    //排序段
    private $_orderby;
    //限制段
    private $_limit;
    //分组段
    private $_groupby;
    //错误信息
    private $error;
    //调试开关
    private $_debug;
    //预处理方法参数容器
    private $_store;
    private $cacheKey;
    private $cacheTime;
    function __construct($tab) {
        $this->tab = $tab;

        $f = DB::M($tab);
        if (file_exists($f)) {
            $this->config = include $f;
        } else {
            throw new Exception($f . "不存在", 1);

        }

        $this->resetStatus();
        // $this->_store['_where'] = [];
    }
}

接下来就是定义一些功能方法了,过滤字段值的model::qf(),fqute():

function qf($v) {

       if ($v == '*' || stripos($v, ',') !== false) {
           return $v;
       }

       if (stripos($v, ':') === 0) {
           return trim($v, ':');
       } else {
           return '`' . trim($this->filterField($v)) . '`';
       }

   }

function fqute($arr) {

       return implode(',', array_map(function ($v) {
           if (stripos($v, '`') === 0) {
               return trim($v, '`');
           }
           return $this->qf($v);

       }, $arr));
   }

在过滤这段,可以借助我们在前面提到的生成的模型文件,其实就是相当于一个配置文件,利用它我们可以提前对字段和值进行检测,手动修改该文件的内容还可以自定义一些实用功能.

function filterField($f) {
        if (isset($this->config['setting'][$f])) {
            return $f;
        } else {

            throw new fieldNotExitsException($f . "不是一个合法的字段");
            return false;
        }

    }

    /**
     * 验证数据
     * @param  [type] $f [description]
     * @param  [type] $v [description]
     * @return [type]    [description]
     */
    function filterVar($f, $v, $flag = 0) {

        if (is_array($v) || is_object($v)) {
            $v = json_encode($v);
        }

        if (stripos($v, '`') === 0) {
            return trim($v, '`');
        }

        $ini = $this->config['setting'][$f];

        if ($ini['min'] && strlen($v) < $ini['min']) {
            throw new valueErrorException($ini['lang'] . "不得少于{$ini['min']}字符");
            return '';

        }

        if (isset($ini['max']) && strlen($v) > $ini['max']) {
            throw new valueErrorException($ini['lang'] . "不得多于{$ini['max']}字符");
            return '';

        }
//在这里我们可以对数据的值进行处理,使得只有符合我们定义条件 的值才能传入。
return $v;
}

定义一个重置方法model::resetStatus(),将类中的属性重置。

function resetStatus() {
        $this->cacheKey = "";
        $this->cacheTime = 0;
        $this->__where = [];
        $this->_arg = [];
        $this->_begin = 1;
        $this->_fields = [];
        $this->_orderby = "";
        $this->_limit = "";
        $this->_groupby = "";

        $this->_debug = false;
        $this->_store['_where'] = [];

    }

这个方法将类中的所有属性全部重置,这样可以为一次全新的查询作准备。注意这里的model::_store变量,它在这个类中有着很重要的作用。先构造两个对它进行操作的方法model::store()和model::dostore()。

function store($medth, $arg) {

        array_push($this->_store[$medth], $arg);
        return $this;

    }

    function doStore($medth) {

        foreach ($this->_store[$medth] as $arg) {

            call_user_func_array([$this, $medth], $arg);

        }

        return $this;
    }

很明显,用了自身的方法名作为键名,而参数数组为值,等下我们将看到哪些方法会放在这里面并且它们的用途是什么。

现在可以来看下一个核心方法_sql(), 这个方法将根据类中收集的各个信息进行sql语句的构造。

function _sql($t = "select", $data = "", $replace = 0) {
        if ($t == "select") {
                //构造select语句,_field()方法返回字段部分,getwhere()返回条件部分_groupby属性保存的就是groupby段的sql,_orderby对应orderby, _limit对应limit
            $sql = "SELECT " . $this->_field() . " FROM {$this->tab}  " . $this->getwhere() . $this->_groupby . $this->_orderby . $this->_limit;
        } elseif ($t == "update") {
//修改,修改的数据在$data中,我们需要对其进行拆盒组装成字符串,
            if (is_array($data)) {
                ///传递的是数组
                $strs = trim(array_reduce(array_map(function ($k, $v) {

                    return $this->qf($this->filterField($k)) . "=" . $this->filterVar($k, $v, 1);

                }, array_keys($data), $data), function (&$strs, $v) {
                    return $strs . "," . $v;
                }), ',');

            } elseif (is_string($data)) {
                $strs = $data;

            } elseif (is_callable($data)) {
                return $this->edit($data());
            }

            $this->doStore("_where");

            $sql = "UPDATE  {$this->tab}   SET $strs    " . $this->getwhere() . $this->_limit;
        } elseif ($t == "insert") {

            if (is_array($data) || is_object($data)) {
                $fieds = "";
                $values = "";

                foreach ($data as $key => $value) {
                    $fieds .= $this->qf($this->filterField($key)) . ',';
                    $values .= ",'" . $this->filterVar($key, $value) . "'";

                }
                $fieds = trim($fieds, ',');
                $values = trim($values, ',');

                $cmd = $replace ? 'REPLACE INTO' : ' INSERT INTO ';
                $sql = $cmd . "  {$this->tab} ({$fieds}) values ({$values}) ";

                return $sql;

            } elseif (is_callable($data)) {
                return $this->insert($data());
            }

        } elseif ($t == 'delete') {
            $this->doStore('_where');
            $sql = "DELETE FROM {$this->tab}   " . $this->getwhere() . $this->_orderby . $this->_limit;

        }

        if ($this->_debug) {
            echo $sql;
        }

        return $sql;
    }

首先是select类型构造,主要是_field()方法会返回相应的字段组合,这个在后面一看就懂了,接下来的是update,你只可以直接传入相应的语句,如"f1='abc',f2='bcd'",或者传入数组['f1'=>'abc', 'f2'=>'bcd']会自动构造成sql语句。insert类型则可以传入数组或对象,最后一种则是delete类型,在这个函数里面我们看到了doStore('_where'),我们前面说过这个是个方法,从这里不难看出,它的功能应该是结合getwhere()方法来构造sql语句的where部分。

function _where($f, $v = null, $op = '=', $relation = 'AND') {
   //第一个参数为数组,将依次对键值进行递归
//第一个参数为字段,第二个参数为值,当字段为级数组时,会将其依次分解,当值为数组,如果元素大于2会自动构造成in类型,你也可以传入一个函数 ,再将第二个参数置为false进行另一个条件组合
        if (is_array($f)) {
            foreach ($f as $key => $value) {
                $this->_where($key, $value, $op, $relation);
            }

            return $this;
        }
        if ($this->_begin) {

            $relation = "";
            $this->_begin = 0;
        }

        if (is_null($v)) {
             array_push($this->__where, "{$relation}  {$f}");
            return $this;
        } elseif ($v === true) {
            array_push($this->__where, "{$relation}  {$f}");
            return $this;
        }

        if (is_array($v) and $op == '=') {

            if (count($v) < 2) {
                $v = $v[0];
            } else {
                $op = "in";
            }

        }

        if (is_string($f) && is_callable($f)) {
            if (!$this->_begin) {
                array_push($this->__where, "{$relation} (");

            }

            $this->_begin = 1;
            $f($this);

            if (!$this->_begin) {
                array_push($this->__where, ')');
            }

            return $this;

        }
        if (is_null($v)) {
            return $this;
        }

        switch ($op) {
            case '=':
            case '>':
            case '<':
            case '>=':
            case '<=':
            case '<>':
                array_push($this->__where, sprintf("%s %s %s '%s' ", $relation, $this->qf($f), $op, $v));
                break;
            case 'in':

            case 'notin':

                $op = $op == 'notin' ? "NOT IN" : "IN";

                if (is_array($v)) {
                    array_push($this->__where, sprintf("%s %s %s (%s)", $relation, $this->qf($f), $op, implode(',', array_map(function ($a) {
                        return "'{$a}'";
                    }, $v))));

                } elseif (is_string($v)) {
                    array_push($this->__where, sprintf("%s %s %s (%s)", $relation, $this->qf($f), $op, $v));
                }

                break;
            case 'like':
                array_push($this->__where, sprintf("%s %s %s '%s' ", $relation, $this->qf($f), $op, str_replace("**", '%', $v)));
                break;

            default:

                break;
        }

        return $this;

    }

构造需要查询的字段语句

public function select() {
        $this->_fields = array_merge($this->_fields, array_map(function($k){
            if (is_array($k)){
                return implode(',', $k);
            }
                return $k;
        }, func_get_args()));
        return $this;
    }

构造where语句

public function where($f, $v = null, $op = '=') {
        return $this->store("_where", [$f, $v, $op, 'AND']);
    }

    public function whereAnd($f, $v = null, $op = '=') {
        return $this->store("_where", [$f, $v, $op, 'AND']);
    }

    public function whereOr($f, $v = null, $op = '=') {
        return $this->store("_where", [$f, $v, $op, 'OR']);

构造limit ,groupby orderby语句

    //将内容存在model::_limit中待拼接
    public function limit($b, $size = 0) {
        $b = intval($b);
        if ($size == 0) {
            $this->_limit = " LIMIT {$b} ";
        } else {
            $size = intval($size);
            $this->_limit = " LIMIT {$b}, {$size} ";
        }
        return $this;
    }
    //除了分开传值,也可以 orderBy('field asc')的方式,这样第二个参数 会自动失效。
    function orderBy($field, $type = "ASC") {
        if (empty($field)) {
            return $this;
        }

        if (stripos($field, " ") !== false) {
            list($field, $type) = explode(' ', $field);
        }

        if (empty($this->_orderby)) {
            $this->_orderby = " ORDER BY {$field} {$type} ";
        } else {
            $this->_orderby .= ", {$field} {$type} ";
        }
        return $this;
    }
    //传入字段并将其传入model::_groupby中待拼接
    function groupBy($field) {
        $this->_groupby = " GROUP BY {$field} ";
        return $this;
    }

获取where语句

function getwhere($type = 1) {
        $this->wheretype = $type;
        if (empty($this->__where)) {
            $this->dostore('_where');
        }

        if ($this->wheretype !== 1) {
            $this->wheretype = 1;
        }

        return empty($this->__where) ? '' : "WHERE " . implode('  ', $this->__where);
    }

接下来就是把简单的curd操作暴露出去使用了

//这是最终操作端了,参数会给_sql()方法,用来构造出相应的sql语句,再交给DBmysqli处理DB只是一个中介 
    function insert($data, $return_insert_id = false, $replace = false, $silent = false) {
        $sql = $this->sql('insert', $data, $replace);

        if (!$sql) {
            $this->resetStatus();
            return false;
        }

        $res = DB::queryInsert($sql, false, $return_insert_id, $silent);
        $this->resetStatus();
        return $res;
    }

function edit($data, $effect = 0, $slient = 0) {
        $sql = $this->sql('update', $data);

        if (!$sql) {
            $this->resetStatus();
            return false;
        }
        $res = DB::queryUpdate($sql, $this->_arg, $effect, $slient);
        $this->resetStatus();
        return $res;
    }

    public function del() {
        $sql = $this->_sql("delete");
        if (!$sql) {
            $this->resetStatus();
            return false;
        }
        $res = DB::queryUpdate($sql, false, 1);
        return $res;
    }
    //获取一条数据,结果信中的第一条
    public function one() {
        $this->limit(0,1);
        $sql = $this->sql();
        if (!$sql) {
            $this->resetStatus();
            return false;
        }

        $res = DB::fetchOne($sql, false);
        $this->resetStatus();
        return $res;
    }

    public function all() {
        $sql = $this->sql();
        if (!$sql) {
            $this->resetStatus();
            return false;
        }

        $res = DB::fetchAll($sql, false);
        $this->resetStatus();
        return $res;
    }

    public function iter() {
        $sql = $this->sql();
        if (!$sql) {
            $this->resetStatus();
            return false;
        }

        $res = DB::iter($sql, false);
        $this->resetStatus();
        return $res;
    }

    public function getsvar(){
        $sql = $this->sql();
        echo $sql;
        if (!$sql) {
            $this->resetStatus();
            return false;
        }
        $res = DB::fetchVar($sql, false);
        $this->resetStatus();
        return $res;
    }

    public function getscol($field) {
        $res = $this->iter();
        $col = array();
        foreach($res as $key => $value) {
            $col[$key] = isset($value->$field) ? $value->$field : null;
        }
        return $col;
    }
public function getsall() {
        return $this->all();
    }

    public function getsone() {
        return $this->one();
    }

    public function getscount($f = "*") {
        return $this->select("`count($f) as num`")->getsvar();
    }

    public function fields() {
        return call_user_func_array([$this, "select"], func_get_args());
    }

    public function F() {
        return call_user_func_array([$this, "select"], func_get_args());
    }

    public function rand($num) {
        
        $sql = "SELECT MAX({$this->pri}) as m, MIN($this->pri) as n FROM {$this->tab} ";
        $res = DB::fetchOne($sql);
        $id = mt_rand($res->n, $res->m - $num > $res->n ?  $res->m - $num :$res->m);
        $this->where($this->pri, $id, '>');
        $this->where($this->pri, $id + $num *2, '<');

        $this->limit(0, $num);
        $data = $this->all();

        return $data;
    }

    function get_page($page_now = 1, $page_size = 10, $url, $lastid = 0) {
        $addwhere = '';
        $t = is_array($this->table) ? implode(',', $this->table) : $this->table;

        $where = $this->getwhere(3);
        $sql = sprintf("SELECT COUNT(%s) FROM %s %s ", !empty($this->_groupby) && $this->groupby ? "DISTICT $this->_groupby" : "*", $this->tab, $where);
        
        $num = DB::fetchVar($sql);
        //echo $num, $sql;die;

        $page_num = max(1, ceil($num / $page_size));
        $page_now = max(1, $page_now);
        $b = max(0, ($page_now - 1) * $page_size);
        if (!$lastid) {
            $sql = "SELECT {$this->pri} AS ID FROM {$this->tab} " . $where . " {$this->_groupby} {$this->_orderby} limit $b,$page_size";
        } else {
            if (stripos($this->_orderby, "desc") !== false) {
                $addwhere = empty($where) ? "where `{$this->pri}`>'{$lastid}' " : " and `{$this->pri}'<'{$lastid}'";
            } else {
                $addwhere = empty($where) ? "where `{$this->pri}`>'{$lastid}' " : "and `{$this->pri}`>'{$lastid}'";
            }
            $sql = "SELECT {$this->pri} AS ID FROM {$this->tab} " . $where . $addwhere . " {$this->_groupby} {$this->orderby} limit 0,$page_size";
        }
//echo $sql;die;
        $res = DB::fetchAll($sql);
        $col = array();
        foreach($res as $key => $value) {
            $col[$key] = $value->ID;
        }

        if (empty($this->__where)) {
            $this->_begin = 1;
        } 
        

        $this->_where($this->pri, $col);
        $sql = $this->sql();
        $list = DB::fetchAll($sql, false);


        $pagestr = '';

        $data = array("list" => $list, "pagelist" => $pagestr, "num" => $page_num, 'count' => $num, "pagenow" => $page_now);
        $this->resetStatus();
        return $data;
        
    }

在这里,有用到一个分页的函数,可以自行设计符合需求的

上一篇下一篇

猜你喜欢

热点阅读