자신만의 데이터베이스 패키지를 작성하는 방법 (3)

PHPz
풀어 주다: 2017-04-04 14:25:44
원래의
1816명이 탐색했습니다.


이번 이슈의 핵심

php 함수 의 다양한 보조 함수에 대한 심층적 이해

PHP 핵심 구문: 함수

변수 매개변수

함수, ...$var가 무엇인지 이해하고, PHP5.6의 새로운 기능 소개

compact

함수 사용법 PHP: Compact - Manual

list

PHP 함수 사용법: list - 수동

PHP마법의 방법

시작하기 전에

이번 문제는 SQL에 관한 문제입니다. 여러 클래스의 쿼리문 간의 복잡성과 상관관계가 커넥터 장만큼 간단하지 않기 때문에 이번에는 Builder 클래스, Grammar 클래스, Model

에 대해 설명해야 합니다. 물론 쿼리 부분만

Builder.php
  • 요청 빌더, 모든 클래스 간의 연결

<?php
/**
* 请求构建器
*/
class Builder {
    // 连接数据库, Connector类
    protected $connector;

    // 生成SQL语法,Grammar类
    protected $grammar;

    // 连接的Model, Model类
    protected $model;

    // SQL查询语句中条件的值
    // 虽然列出了全部, 但本教程只实现了where,其余的因为懒(理直气壮),请自行实现, 逻辑和where函数大致
    protected $bindings = [
        &#39;select&#39; => [],
        'join'   => [],
        'where'  => [],
        'having' => [],
        'order'  => [],
        'union'  => [],
    ];

    // select 语法想要查看的字段
    public $columns;

    // 过滤重复值
    public $distinct = false;

    // 需要查询的表
    public $from;

    // 所有join 语法
    public $joins;

    // 所有where 语法
    public $wheres;

    // group 语法
    public $groups;

    // having 语法
    public $havings;

    // order by 语法
    public $orders;

    // 限制数据库返回的数据量, limit语法
    public $limit;

    // 需要略过的数据量, offset语法
    public $offset;

    // 数据写保护, 开启后该条数据无法删除或改写
    public $writeLock = false;
로그인 후 복사
다음은
  • _construct 함수입니다. 인스턴스 생성 후 첫 번째 단계는 무엇인가요?
  function construct() {
      // 新建两个实例
      // 如果已经理解Connector的原理后自然明白这个Connector实例已经联通了数据库
      $this->connector = new Connector;
      $this->grammar = new Grammar;
  }
로그인 후 복사
  • select - 보려는 열을 선택하세요. 기본값은 모두, 즉 '*'
  • public function select($columns = ['*']) {
          // $columns只能存入数组, 所以需要判定, 如果不是, 将所有参数合成一个数组再存入
          // 这是一个更人性化的设定, 用户可以选择以下两种调用方式
          // select(['first_name', 'last_name']), 以数组的方式
          // select('first_name', 'last_name'), 以参数的方式
          // 最后一点, 你可以发现所有函数最后都会存入对应的Builder属性中
          // 这和你在做饭前先处理材料是同一个道理, 也就是预处理
          $this->columns = is_array($columns) ? $columns : func_get_args();
          return $this;
      }
    로그인 후 복사
  • distinct - 중복 값 필터링
  • public function distinct() {
          // 开启过滤
          $this->distinct = true;
          return $this;
      }
    로그인 후 복사
  • from - 테이블 이름 설정
  •   public function from($table) {
          $this->from = $table;
          return $this;
      }
    로그인 후 복사
  • join - 두 테이블을 조인하기 위한 조인 구문, 기본값은 내부 조인
  • /**
       * @param  string $table   需要连接的副表名
       * 为什么主键和外键可以单个或数组呢
       * 原因是join语法可以on多个键
       * @param  string/array $foregin 外键
       * @param  string/array $primary 主键
       * @param  string $type    连接方式, 默认inner
       * @return Builder          返回Builder实例
       */
      public function join($table, $foregin , $primary, $type = 'inner') {
          // 判定外键变量的数据类型
          if(is_array($foregin)) {
              // 如果是数组, 循环加上副表名在前头
              foreach($foregin as &$f)
                  $f = $table.".".$f;
          }else {
              // 反之, 不需循环直接加
              $foregin = $table.".".$foregin;
          }
    
          // 与$foreign的逻辑同理
          if(is_array($primary)) {
              foreach($primary as &$p)
                  $p = $this->from.".".$p;
          }else {
              $primary = $this->from.".".$primary;
          }
    
          // 将所有经过处理的参数收入$joins待用
          $this->joins[] = (object)[
              'from' => $this->from,
              'table' => $table,
              'foregin' => $foregin,
              'primary' => $primary,
              'type' => $type
          ];
    
          // 返回Builder实例
          return $this;
      }
    로그인 후 복사
  • 조인의 다양한 변형 , 세 개의 공통 조인만 구현되며 나머지는 직접 추가하세요.
  • // 所有逻辑同join(), 不过这是left join
      public function leftJoin($table, $foregin , $primary) {
          return $this->join($table, $foregin , $primary, 'left');
      }
    
      // 所有逻辑同join(), 不过这是right join
      public function rightJoin($table, $foregin , $primary) {
          return $this->join($table, $foregin , $primary, 'right');
      }
    로그인 후 복사
  • addBinding - 다양한 SQL 조건에 연결된 값을 저장하세요
  • /**
       * @param string/array $value 字段匹配的值
       * @param string $type  条件类型, 默认为where, 具体查看$bindings
       */
      public function addBinding($value, $type = 'where') {
          // 如果$type并不是$bindings的键, 跳出错误
          if (!array_key_exists($type, $this->bindings)) 
              throw new InvalidArgumentException("Invalid binding type: {$type}.");
    
          // 如果$value是数组,将其与之前存入的值整合为一维数组
          if (is_array($value)) 
              $this->bindings[$type] = array_values(array_merge($this->bindings[$type], $value));
          // 反之, 直接存入最后一位即可
          else
              $this->bindings[$type][] = $value;
    
          // 返回Builder实例
          return $this;
      }
    로그인 후 복사
  • where - SQL의 조건, where 구문

  • where()에는 두 가지 호출 방법이 있습니다
    다음 함수에는 두 가지 호출 방법이 있습니다. 다음 코드 논리를 이해하십시오.

    매개변수 메서드의 경우 기본값은 '='입니다.

    Actor::select('first_name', 'last_name')
      ->where('first_name', 'NICK')
      ->where('last_name','!=', 'WAHLBERG')
      ->first()
    로그인 후 복사
    배열 모드에서 이 메서드는 제한이 있으며 필드가 값과 동일한 경우에만 일치할 수 있습니다

    Actor::select('first_name', 'last_name')
      ->where(['first_name'=> 'NICK', 'last_name'=> 'WAHLBERG'])
      ->first()
    로그인 후 복사
    다음 코드를 보세요
      public function where($column, $operator = null, $value = null, $boolean = 'and') {
              // 判定$column是否为数组
              if (is_array($column)) {
                  // 如果是数组, 循环再调用本函数,where()
                  foreach ($column as $key => $value) 
                      $this->where($key, "=", $value, $boolean);
              }else {
                  // 反之, 判定参数数量和$value是否为空, 如果为真,这意味着用户省略了'=',自动添加
                  if(func_num_args() == 2 || 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 - 명령문별로 정렬하여 반환된 데이터의 배열을 결정합니다

      /**
         * @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

      이것은 제가 직접 작성한 함수로, 다차원 배열

      을 부드럽게 변환하는 데 사용됩니다. 차원 배열을 1차원 배열로

      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));
      로그인 후 복사
      return
    • 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 - 명령문별로 그룹화, 데이터 통합
    • /**
         * @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 - 지정된 데이터 양을 건너뛰세요. 관심이 있으시면 메시지를 남겨주세요.
    •   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 - 모델 인스턴스 설정

        public function setModel(Model $model) {
            $this->model = $model;
            return $this;
        }
      로그인 후 복사
      끝나고 다음 SQL 문 컴파일, Grammar 클래스

    • Grammar.php
      • Builder 인스턴스 속성에 저장된 처리된 값을 기반으로 컴파일
      • 컴파일은 구문의 일부에서 천천히 이루어지며 최종적으로 요약됩니다
        <?php
        /**
        * 数据库语法生成    
        */
        class Grammar {
            // 构建查询语句所可能出现的各种SQL语法
            // 注意, 它们的顺序是对应着各自在SQL语句中合法的位置
            // sqlsrv略微不同
            protected $selectComponents = [
                &#39;distinct&#39;,
                &#39;columns&#39;,
                &#39;from&#39;,
                &#39;joins&#39;,
                &#39;wheres&#39;,
                &#39;groups&#39;,
                &#39;orders&#39;,
                &#39;limit&#39;,
                &#39;offset&#39;,
            ];
        로그인 후 복사
      • concatenate - 컴파일 후 존재할 수 있는 빈 값을 제외하고 전체 SQL 문을 연결합니다.
      •   protected function concatenate($segments) {
              return implode(&#39; &#39;, array_filter($segments, function ($value) {
                  return (string) $value !== &#39;&#39;;
              }));
          }
        로그인 후 복사
      • 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 - 고유한 문 컴파일
      •   protected function compileDistinct(Builder $query, $distinct) {
              return $distinct ? 'select distinct' : 'select';
          }
        로그인 후 복사
      • compileLimit - 컴파일 제한 문
      •   protected function compileLimit(Builder $query, $limit) {
              return "limit $limit";
          }
        로그인 후 복사
      • compileOffset - 컴파일 오프셋 문
      •   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 - 조인 문 컴파일
      •   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 |or /i', '', implode(" ", $sql), 1);
          }
        로그인 후 복사
      • whereBasic - 가장 기본적인 where 사용법 컴파일
      •   protected function whereBasic(Builder $query, $where) {
              // 检测$where[column]是否存在小数点
              // 是, 就意味着前缀已经附带了表名
              // 否, 为之后的字段添上表名
              // 因为join存在副表, 所以部分$where可能有附带表名, 这时候就不用添加了
              $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
              // 返回添上表名的字段,和表达式, 再一个问号
              // 为何使用问号而不是:变量名? 因为:变量名存在太多局限性,不能标点符号,不能数字开头
              return $table.$where['column'].' '.$where['operator'].' ?';
          }
        로그인 후 복사
      • whereIn - where in 사용법 컴파일
      •   protected function whereIn(Builder $query, $where) {
              // 检测$where[column]是否存在小数点, 同理whereBasic()
              $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
              // 有多少个匹配值那就连接多少个问号
              $values = implode(', ', array_fill(0, sizeof($where['values']), '?'));
              // 返回where in 语句
              return $table.$where['column'].' in ('.$values.')';
          }
        로그인 후 복사
      • whereNotIn - 사용되지 않는 곳에서 컴파일
      •   protected function whereNotIn(Builder $query, $where) {
              // 检测$where[column]是否存在小数点, 同理whereBasic()
              $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
              // 有多少个匹配值那就连接多少个问号
              $values = implode(', ', array_fill(0, sizeof($where['values']), '?'));
              // 返回where not in 语句
              return $table.$where['column'].' not in ('.$values.')';
          }
        로그인 후 복사
      • whereNull - null이 사용되는 곳에서 컴파일
      •   protected function whereNull(Builder $query, $where) {
              // 检测$where[column]是否存在小数点, 同理whereBasic()
              $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
              // 返回where is null 语句
              return $table.$where['column'].' is null';
          }
        로그인 후 복사
      • whereNotNull - null이 아닌 곳에서 컴파일
      •   protected function whereNotNull(Builder $query, $where) {
              // 检测$where[column]是否存在小数点, 同理whereBasic()
              $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
              // 返回where is not null 语句
              return $table.$where['column'].' is not null';
          }
        로그인 후 복사
      • compileGroups - 문별로 그룹 컴파일
      •   protected function compileGroups(Builder $query, $groups) {
              // 连接$groups, 返回group by语句
              return 'group by '.implode(', ', $groups);
          }
        로그인 후 복사
      • compileOrders - 문별로 컴파일 순서

          protected function compileOrders(Builder $query, $orders) {
              // 连接每一个$order与其$direction, 然后返回order by语句
              return 'order by '.implode(', ', array_map(function ($order) {
                  return $order['column'].' '.$order['direction'];
              }, $orders));
          }
        로그인 후 복사
        문법이 끝나면 다음은 엔트리 파일, 모델 클래스, 마법의 세계

      • Model.php

        • 数据库表的依赖对象

        • 作为数据的出口, 数据就在这里进行修饰

        • 各种魔术方法用得飞起, 使用之前请先理解魔术方法是什么

        <?php
        /**
        * 入口文件, 数据库表的父类
        */
        class Model {
            // SQL命令构建器, Builder类
            protected $builder;
        
            // 数据库返回的数据存在这里
            protected $data;
        
            // 数据库表名, 选填, 默认为类名
            protected $table;
        
            // 主键, 二选一($unique)
            protected $identity;
        
            // unique key, 二选一($identity)
            protected $unique;
        로그인 후 복사
        • getTable - 获取数据库表名, 没有设置返回false

            public function getTable() {
                return isset($this->table) ? $this->table : false;
            }
          로그인 후 복사
        • getIdentity - 获取主键名, 没有返回假

            public function getIdentity() {
                return isset($this->identity) ? $this->identity : false;
            }
          로그인 후 복사
        • getUnique - 获取unique key名, 没有返回假

            public function getUnique() {
                // 检测是否存在unique key, 不存在返回假, 存在就在检查是否数组, 不是就装入数组再返回
                return isset($this->unique) ? is_array($this->unique) ? $this->unique : [$this->unique] : false;
            }
          로그인 후 복사
        • check - 检查必须预设的实例属性

            public function check() {
                // 如果数据库表的名称和Model的子类相同,可以选择不填,默认直接取类的名称
                if(!$this->getTable()) 
                    $this->table = get_class($this);
          
                // 跳出提醒必须设置$identity或$unique其中一项
                if(!$this->getIdentity() && !$this->getUnique())
                    throw new Exception('One of $identity or $unique should be assigned in Model "'.get_called_class().'"');
          
            }
          로그인 후 복사
        • set/getBuilder - 设置或读取Builder实例

          // 设置Builder实例
            public function setBuilder(Builder $builder) {
                $this->builder = $builder;
                return $this;
            }
          
            // 获取Builder实例
            public function getBuilder() {
                return $this->builder;
            }
          로그인 후 복사
        • setData - 设置数据库数据

            public function setData($data) {
                $this->data = $data;
                return $this;
            }
          로그인 후 복사
        • construct - 创建实例后的第一步

            function construct() {
                // 检查设定是否正确
                $this->check();
                // 新建一个Builder实例
                $this->setBuilder(new Builder);
                // 设置构建器的主表名称
                $this->getBuilder()->from($this->table);
                // 将Model实例带入Builder
                $this->getBuilder()->setModel($this);
            }
          로그인 후 복사

          魔术方法

        • callStatic - 如果找不到静态函数的时候自动运行下面的逻辑

            static public function callStatic($method, $args = null) {
                // 这是一个伪静态, 创建一个实例
                $instance = new static;
                // 在$instance->builder之中, 寻找函数$method, 并附上参数$args
                return call_user_func_array([$instance->builder, $method], $args);
            }
          로그인 후 복사
        • call - 如果找不到函数的时候自动运行下面的逻辑

            public function call($method, $args) {
                // 在$this->builder之中, 寻找函数$method, 并附上参数$args
                return call_user_func_array([$this->builder, $method], $args);
            }
          로그인 후 복사
        • debugInfo - 在调试的时候隐藏多余的信息, 只留下数据库返回的数据

            public function debugInfo() {
                // 也不懂算不算bug, 该方法强制要求返回的数据类型必须是array数组
                // 但是就算我强行转换(casting)后返回的数据依然是对象(object)
                return (array)$this->data;
            }
          로그인 후 복사
        • get - 当调用对象的属性时, 强制调用这个魔术方法

            // 为了避免局外人可以访问Model类的属性
            // 为了避免对象属性和表的字段名字相同
            public function get($field) {
                // 如果调用的属性是Model类内的逻辑
                // 直接返回该属性的值
                if(get_called_class()==="Model") 
                    return $this->$field;
                // 反之, 则检查$data内是否存在该属性, 没有的话跳出错误
                if(!isset($this->data->$field)) 
                    throw new Exception("column '$field' is not exists in table '$this->table'");
                // 如果存在,由于返回的数据都是存在$data里, 所以要这样调用
                return $this->data->$field;
            }
          로그인 후 복사
        • set - 当想修改的对象属性时, 强制调用这个魔术方法

            public function set($field, $value) {
                // 如果调用的属性是Model类内的逻辑
                // 直接赋值该属性
                if(get_called_class()==="Model")
                    return $this->$field = $value;
                // 反之, 则检查$data内是否存在该属性, 没有的话跳出错误
                if(!isset($this->data->$field)) 
                    throw new Exception("column '$field' is not exists in table '$this->table'");
          
                // 如果存在,由于返回的数据都是存在$data里, 所以要这样赋值
                return $this->data->$field = $value;
            }
          로그인 후 복사

          进阶的Buider类封装没错, 我又把Builder(微)封装了

        • find - 使用主键查寻数据

          /**
             * @param  int $id 主键
             * @return Model subclass     返回一个Model的子类数据
             */
            public static function find($id) {
                // 这是一个伪静态, 创建一个实例
                $self = new static;
          
                // 该函数只适用于设置了主键的表, 如果没有设置, 跳出错误
                if(!$self->getIdentity()) 
                    throw new Exception("Table's identity key should be assgined");
          
                return $self->where($self->identity, $id)->first();
            }
          로그인 후 복사

          还有更多, 看心情更...


        • 本期疑问

          1.) 缺少的Builder函数如果有人愿意提供例子就好了, 进阶复杂的语句那就更好了, 直接写然后再分享给我那就最好了
          2.) 有些函数或结构可能没有效率或者白白添加服务器压力, 但我写的顺了可能没看见, 请指出
          3.) 有人能告诉我laravel是怎么解决pdo 2100个最大参数(bind)的问题吗? 源代码把我看蒙了


          위 내용은 자신만의 데이터베이스 패키지를 작성하는 방법 (3)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

        관련 라벨:
        원천:php.cn
        본 웹사이트의 성명
        본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
        인기 튜토리얼
        더>
        최신 다운로드
        더>
        웹 효과
        웹사이트 소스 코드
        웹사이트 자료
        프론트엔드 템플릿
        회사 소개 부인 성명 Sitemap
        PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!