盾怪网教程:是一个免费提供流行杀毒软件教程、在线学习分享的学习平台!

怎么写一个属于自己的数据库封装(3)

时间:2025/3/12作者:未知来源:盾怪网教程人气:

[摘要]is_null($value)) list($operator, $value) = [=, $operator];// 最简单原始的条件查询, 所以$type值为Basic$type = &quo...
is_null($value)) list($operator, $value) = ['=', $operator]; // 最简单原始的条件查询, 所以$type值为Basic $type = "Basic"; // 将处理过的条件存入$wheres $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean'); // 将字段需要匹配的值存入$bindings中的where $this->addBinding($value, 'where'); } // 返回Builder实例 return $this; }
  • where的各种变形

    // 所有逻辑同where(), 不过这是or where
      public function orWhere($column, $operator = null, $value = null) {
          return $this->where($column, $operator, $value, 'or');
      }
    
      /**
       * where in 语法, 字段匹配多个值
       * @param  string  $column  字段
       * @param  array  $values  一组字段需匹配的值
       * @param  string  $boolean 默认为and
       * @param  boolean $not     默认为假, 真为排除所有$value里的数据
       * @return Builder           返回Builder实例
       */
      public function whereIn($column, $values, $boolean = 'and', $not = false) {
          // 判定条件查询的类型, false = where in ($value),true = where not in ($value)
          $type = $not ? 'NotIn' : 'In';
          // 将条件存入$wheres
          $this->wheres[] = compact('type', 'column', 'values', 'boolean');
          // 循环将字段需要匹配的值存入$bindings中的where
          foreach ($values as $value) 
              $this->addBinding($value, 'where');
    
          // 返回Builder实例
          return $this;
      }
    
      // 所有逻辑同whereIn(), 不过这是or where in
      public function orWhereIn($column, $values) {
          return $this->whereIn($column, $values, 'or');
      }
    
      // 所有逻辑同whereIn(), 不过这是and where not in
      public function whereNotIn($column, $values, $boolean = 'and') {
          return $this->whereIn($column, $values, $boolean, true);
      }
    
      // 所有逻辑同whereNotIn(), 不过这是or where not in
      public function orWhereNotIn($column, $values) {
          return $this->whereNotIn($column, $values, 'or');
      }
    
      /**
       * where $coulmn is null 语法, 搜索字段为空的数据
       * @param  string  $column  字段
       * @param  string  $boolean 默认为and
       * @param  boolean $not     默认为假, 真为排除所有字段为空的数据
       * @return Builder          返回Builder实例
       */
      public function whereNull($column, $boolean = 'and', $not = false) {
          // 判定条件查询的类型, false = where $column is null,true = where $column is not null
          $type = $not ? 'NotNull' : 'Null';
          // 将条件存入$wheres
          $this->wheres[] = compact('type', 'column', 'boolean');
          // 返回Builder实例
          return $this;
      }
    
      // 所有逻辑同whereNull(), 不过这是or where $column is null
      public function orWhereNull($column) {
          return $this->whereNull($column, 'or');
      }
    
      // 所有逻辑同whereNull(), 不过这是and where $column is not null
      public function whereNotNull($column, $boolean = 'and') {
          return $this->whereNull($column, $boolean, true);
      }
    
      // 所有逻辑同whereNotNull(), 不过这是or where $column is not null
      public function orWhereNotNull($column) {
          return $this->whereNotNull($column, 'or');
      }
  • orderBy - order by 语句, 决定返回的数据排列

    /**
       * @param  string/array $column    字段
       * @param  string $direction 排序,默认为asc, 顺序
       * @return Builder            返回Builder实例
       */
      public function orderBy($column, $direction = 'asc') {
          // 局限性在于必须声明顺序或逆序
          if(is_array($column)) {
              foreach ($column as $key => $value) 
                  $this->orderBy($key, $value);
          }else {
              // 简单判定后直接存入$orders, $direction输入错误不跳错误直接选择顺序
              $this->orders[] = [
                  'column' => $column,
                  'direction' => strtolower($direction) == 'desc' ? 'desc' : 'asc',
              ];
          }
    
          // 返回Builder实例
          return $this;
      }

    辅助函数 - array_flatten

    这是我自己写的一个函数, 用于抚平多维数组
    什么意思呢, 就是将多维数组整成一维数组

    function array_flatten(array $array) {
      $return = array();
      array_walk_recursive($array, function($a) use (&$return) { 
          $return[] = $a; 
      });
      return $return;
    }

    例子

    $a = array('this', 'is', array('a', 'multidimentional', array('array') ), 'to', 'make', 'the', 'tutotal', array('more', 'easier', 'to', 'understand') );
    dd(array_flatten($a));

    返回

    array (size=13)
    0 => string 'this' (length=4)
    1 => string 'is' (length=2)
    2 => string 'a' (length=1)
    3 => string 'multidimentional' (length=16)
    4 => string 'array' (length=5)
    5 => string 'to' (length=2)
    6 => string 'make' (length=4)
    7 => string 'the' (length=3)
    8 => string 'tutotal' (length=7)
    9 => string 'more' (length=4)
    10 => string 'easier' (length=6)
    11 => string 'to' (length=2)
    12 => string 'understand' (length=10)
  • groupBy - group by 语句, 整合数据

    /**
       * @param  string/array $groups 字段
       * @return Builder        返回Builder实例
       */
      public function groupBy(...$groups) {
          if(empty($this->groups)) $this->groups = [];
          $this->groups = array_merge($this->groups, array_flatten($groups));
          // 返回Builder实例
          return $this;
      }
  • limit - 限制返回的数据量, sqlsrv的写法不同, 有兴趣知道的可以留言

      public function limit($value) {
          // 如果$value大于零这条函数才生效
          if ($value >= 0) $this->limit = $value;
          return $this;
      }
    
      // limit函数的别名, 增加函数链的可读性
      public function take($value) {
          return $this->limit($value);
      }
  • offset - 跳过指定的数据量, sqlsrv的写法不同, 有兴趣知道的可以留言

      public function offset($value) {
          // 如果$value大于零这条函数才生效
          if ($value >= 0) $this->offset = $value;
          return $this;
      }
    
      // offset函数的别名, 增加函数链的可读性
      public function skip($value) {
          return $this->offset($value);
      }
  • get - 读取数据库数据
    重头戏来了, 之前所有的函数都是返回Builder实例的,这意味着可再编辑
    这是一个总结, 将前端的函数链请求一并处理了

    // 返回一组数据库数据, 可以在这里设定想返回的字段, 但是select()的优先度最高
      public function get($columns = ['*']) {
          // 如果Builder的$columns依然为空, 那么就用该函数的$columns, 反之则使用select()所声明的字段
          if (is_null($this->columns)) $this->columns = $columns;
          // 如果Builder的$orders依然为空, 那么就默认第一个字段顺序
          // 发现一个莫名的bug, 可能是我理解错了, 不加 order by 1数据返回竟然不是按照主键(第一个字段)排序
          // 所以以防万一加一个默认
          if (is_null($this->orders)) $this->orderBy(1);
          // 将Grammar类生成的语句,和处理过的字段所对应的值,都交给Connector类, 让它与数据库进行通信,返回数据
          // 注意这里的三个函数
          // read() 不用说Connector篇介绍过了
          // compileSelect()是用来编译生成查询语句
          // getBindings()用来获取收在$bindings中条件的值, 下方会有说明
          $results = $this->connector->read($this->grammar->compileSelect($this), $this->getBindings());
          // 返回一组数据库数据,如果查询为空,返回空数组
          // cast()下方会有说明
          return $this->cast($results);
      }
    
      // get函数的别名, 增加函数链的可读性
      public function all($columns = ['*']) {
          return $this->get($columns);
      }
  • getBindings - 返回所有$bindings中的值

      public function getBindings() {
          // 抚平多维数组成一维数组后再返回
          return array_flatten($this->bindings);
      }
  • cast - 转化返回的数据类型为自身的Model子类
    在基本思路篇的最终效果小节有提到过数据的可操作性, 核心代码就是这里
    如果看不明白这里没关系, 暂时跳过, 等看完Model.php就能理解了(吧?)

      public function cast($results){
          // 获取Model子类的名称
          $class = get_class($this->model);
          // 新建一个Model子类
          $model = new $class();
          // 如果获得的数据库数据是数组
          if (gettype($results)=="array") {
              $arr = [];
              // 循环数据
              foreach ($results as $result) 
                  // 再调用本函数
                  $arr[] = $this->cast($result);
              // 返回经过转化的数据数组
              return $arr;
          // 如果获得的数据库数据是对象
          }elseif(gettype($results)=="object"){
              // 存入数据对象
              $model->setData($results);
              // 加入主键或unique key以实现数据的可操作性
              // 如果表存在主键和返回的数据中有主键的字段
              if($model->getIdentity() && isset($results->{$model->getIdentity()})) {
                  $model->where($model->getIdentity(), $results->{$model->getIdentity()});
              // 如果表存在unique key和返回的数据中有unique key的字段
              }elseif($model->getUnique() && array_check($model->getUnique(),$results)) {
                  foreach ($model->getUnique() as $key) 
                      $model->where($key, $results->$key);
              // 改写和删除操作仅仅在符合以上两种条件其中之一的时候
              // 反之, 开启写保护不允许改写
              }else {
                  // 其实还可以考虑直接复制query
                  // 但变数太多干脆直接一棍子打死
                  $model->getBuilder()->writeLock = true;
              }
              // 返回该实例
              return $model;
          }
          // 如果转化失败返回false
          return false;
      }
  • first - 仅取头一条数据, 所以返回的是对象, 而get返回的是数组,里头多条对象

    /**
       * @param  array  $columns 如果Builder的$columns依然为空, 那么就用该函数的$columns, 反之则使用select()所声明的字段
       * @return boolean/Model          查询为空返回false, 反之则返回附带数据的表类
       */
      public function first($columns = ['*']) {
          $results = $this->take(1)->get($columns);
          return empty($results) ? false : $results[0];
      }
  • setModel - 设置Model实例

      public function setModel(Model $model) {
          $this->model = $model;
          return $this;
      }

    告一段落, 接下来编译SQL语句, Grammar类


Grammar.php

  • 根据经过处理后存在Builder实例属性中的值进行编译

  • 编译是一部分一部分语法慢慢编译的, 最后在总结起来

<?php
/**
* 数据库语法生成    
*/
class Grammar {
    // 构建查询语句所可能出现的各种SQL语法
    // 注意, 它们的顺序是对应着各自在SQL语句中合法的位置
    // sqlsrv略微不同
    protected $selectComponents = [
        'distinct',
        'columns',
        'from',
        'joins',
        'wheres',
        'groups',
        'orders',
        'limit',
        'offset',
    ];
  • concatenate - 排除编译后可能存在空的值,然后连接整句SQL语句

      protected function concatenate($segments) {
          return implode(' ', array_filter($segments, function ($value) {
              return (string) $value !== '';
          }));
      }
  • compileSelect - 编译SQL查询语句

      // 还记得Builder->get()中的compileSelect()吗?
      public function compileSelect(Builder $query) {
          // concatenate()排除编译后可能存在空的值,然后连接整句SQL语句
          // 去掉可能存在的前后端空格再返回
          return trim($this->concatenate($this->compileComponents($query)));
      }
  • compileComponents - 循环$selectComponents, 根据不同的语法局部编译对应的语句

      protected function compileComponents(Builder $query) {
          $sql = [];
          // 循环$selectComponents
          foreach ($this->selectComponents as $component) {
              // 如果Builder实例中对应的函数曾经被调用,那意味着对应的语法非空
              if (!is_null($query->$component)) {
                  $method = 'compile'.ucfirst($component);
                  // 编译该语法并将之收入$sql
                  $sql[$component] = $this->$method($query, $query->$component);
              }
          }
          // 返回$sql数组
          return $sql;
      }
  • compileDistinct - 编译distinct语句

      protected function compileDistinct(Builder $query, $distinct) {
          return $distinct ? 'select distinct' : 'select';
      }
  • compileLimit - 编译limit语句

      protected function compileLimit(Builder $query, $limit) {
          return "limit $limit";
      }
  • compileOffset - 编译offset语句

      protected function compileOffset(Builder $query, $offset) {
          return "offset $offset";
      }
  • compileColumns - 编译需查询的字段

      protected function compileColumns(Builder $query, $columns) {
          return implode(', ', $columns);
      }
  • compileFrom - 编译生成表名

      protected function compileFrom(Builder $query, $table) {
          return 'from '.$table;
      }
  • compileJoins - 编译join语句

      protected function compileJoins(Builder $query, $joins) {
          $sql = [];
          foreach ($joins as $join) {
              // 如果存在多个副键和主键
              if(is_array($join->foregin) && is_array($join->primary)) {
                  $on = [];
                  // 循环键的数量, 将之与对应的主键组合
                  for($i=0; $i<sizeof($join->foregin); $i++)
                      $on[] = $join->foregin[$i]." = ".$join->primary[$i];
                  // 最后再将所有句子用and连接
                  $on = implode(' and ', $on);
              } else {
              //反之, 直接连接即可
                  $on = "$join->foregin = $join->primary";
              }
              // 附上join的类型和副表
              $sql[] = trim("{$join->type} join {$join->table} on $on");
          }
    
          // 连接再返回
          return implode(' ', $sql);
      }
  • compileWheres - 编译where语句

      protected function compileWheres(Builder $query) {
          $sql = [];
          // 类似与compileComponents(), 循环Builder实例中的$wheres
          foreach ($query->wheres as $where) {
              // 根据不同的$type来进行编译
              $method = "where{$where['type']}";
              // 返回的部分语句先收入数组
              $sql[] = $where['boolean'].' '.$this->$method($query, $where);
          }
          // 最后将$sql数组连接起来, 删掉最前面的and或or在返回
          return 'where '.preg_replace('/and 

关键词:如何写一个属于自己的数据库封装(3)




Copyright © 2012-2018 盾怪网教程(http://www.dunguai.com) .All Rights Reserved 网站地图 友情链接

免责声明:本站资源均来自互联网收集 如有侵犯到您利益的地方请及时联系管理删除,敬请见谅!

QQ:1006262270   邮箱:kfyvi376850063@126.com   手机版