如何寫一個屬于自己的數據庫封裝(2)

Connector.php

  • 負責與數據庫通信,增刪改讀(crud)

首先, 建一個Connector類, 并且設置屬性

<?php  class Connector {      // 數據庫地址前綴,常見的有mysql,slqlsrv,odbc等等等      private $driver = 'mysql';      // 數據庫地址      private $host = 'localhost';      // 數據庫默認名稱, 設置為靜態是因為有切換數據庫的需求      private static $db = 'sakila';      // 數據庫用戶名      private $username = 'root';      // 數據庫密碼      private $password = '';      // 當前數據庫連接      protected $connection;      // 數據庫連接箱,切換已存在的數據庫連接不需要重新通信,從這里取即可      protected static $container = [];        // PDO默認屬性配置,具體請自行查看文檔      protected $options = [          PDO::ATTR_CASE => PDO::CASE_NATURAL,          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,          PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,          PDO::ATTR_STRINGIFY_FETCHES => false,      ];  }

以上代碼配合屬性應該可以理解了所以不多解釋了,直接進入屬性

  • buildConnectString – 就是生成DSN連接串, 非常直白

      protected function buildConnectString() {        return "$this->driver:host=$this->host;dbname=".self::$db;    }        // "mysql:host=localhost;dbname=sakila;"
  • connect – 屬性

      public function connect() {        try {            // 連接數據庫,生成pdo實例, 將之賦予$connection,并存入$container之中            self::$container[self::$db] = $this->connection = new PDO($this->buildConnectString(), $this->username, $this->password, $this->options);            // 返回數據庫連接            return $this->connection;        } catch (Exception $e) {            // 若是失敗, 返回原因            // 還記得dd()嗎?這個輔助函數還會一直用上            dd($e->getMessage());        }    }
  • 屬性Database – 切換數據庫

      public function setDatabase($db) {        self::$db = $db;        return $this;    }
  • _construct – 生成實例后第一步要干嘛

      function construct() {        // 如果從未連接過該數據庫, 那就新建連接        if(empty(self::$container[self::$db])) $this->connect();        // 反之, 從$container中提取, 無需再次通信        $this->connection = self::$container[self::$db];    }

接下來兩個函數式配合著用的,單看可能會懵逼, 配合例子單步屬性

$a = new Connector();    $bindValues = [      'PENELOPE',      'GUINESS'  ];    dd($a->read('select * from actor where first_name = ? and last_name = ?', $bindValues));

返回值

array (size=1)    0 =>       object(stdClass)[4]        public 'actor_id' => string '1' (length=1)        public 'first_name' => string 'PENELOPE' (length=8)        public 'last_name' => string 'GUINESS' (length=7)        public 'last_update' => string '2006-02-15 04:34:33' (length=19)
  • read – 讀取數據

      public function read($sql, $bindings) {        // 將sql語句放入預處理函數        // $sql = select * from actor where first_name = ? and last_name = ?        $statement = $this->connection->prepare($sql);        // 將附帶參數帶入pdo實例        // $bindings = ['PENELOPE', 'GUINESS']        $this->bindValues($statement, $bindings);        // 執行        $statement->execute();        // 返回所有合法數據, 以Object對象為數據類型        return $statement->fetchAll(PDO::FETCH_OBJ);    }
  • bindValues – 將附帶參數帶入pdo實例

          // 從例子中可以看出, 我用在預處理的變量為?, 這是因為pdo的局限性, 有興趣可以在評論區討論這個問題    public function bindValues($statement, $bindings) {        // $bindings = ['PENELOPE', 'GUINESS']        // 依次循環每一個參數        foreach ($bindings as $key => $value) {            // $key = 0/1            // $value = 'PENELOPE'/'GUINESS'            $statement->bindValue(                // 如果是字符串類型, 那就直接使用, 反之是數字, 將其+1                // 這里是數值, 因此返回1/2                is_string($key) ? $key : $key + 1,                // 直接放入值                // 'PENELOPE'/'GUINESS'                $value,                // 這里直白不多說                // PDO::PARAM_STR/PDO::PARAM_STR                is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR            );        }    }

    所以懂了嗎_( :3」∠)

  • update – 改寫數據

      // 與read不同的地方在于, read返回數據, update返回boolean(true/false)    public function update($sql, $bindings) {        $statement = $this->connection->prepare($sql);        $this->bindValues($statement, $bindings);        return $statement->execute();    }
  • 屬性屬性數據

    // 與update一樣, 分開是因為方便日后維護制定    public function delete($sql, $bindings) {        $statement = $this->connection->prepare($sql);        $this->bindValues($statement, $bindings);        return $statement->execute();    }
  • create – 增加數據

    // 返回最新的自增ID, 如果有    public function create($sql, $bindings) {        $statement = $this->connection->prepare($sql);        $this->bindValues($statement, $bindings);        $statement->execute();        return $this->lastInsertId();    }
  • lastInsertId – 返回屬性id, 如果有

      // pdo自帶,只是稍微封裝    public function lastInsertId() {        $id = $this->connection->lastInsertId();        return empty($id) ? null : $id;    }

過于高級復雜的SQL語句可能無法封裝, 因此準備了可直接用RAW query通信數據庫的兩個函數

  • exec – 適用于增刪改

      public function exec($sql) {        return $this->connection->exec($sql);    }
  • query – 適用于讀

      public function query($sql) {        $q = $this->connection->query($sql);        return $q->fetchAll(PDO::FETCH_OBJ);    }

將數據庫事務相關的函數封裝起來, 直白所以沒有注釋

        public function beginTransaction() {          $this->connection->beginTransaction();          return $this;      }        public function rollBack() {          $this->connection->rollBack();          return $this;      }        public function commit() {          $this->connection->commit();          return $this;      }        public function inTransaction() {          return $this->connection->inTransaction();      }

完整代碼

<?php  class Connector {      // 數據庫地址前綴,常見的有mysql,slqlsrv,odbc等等等      private $driver = 'mysql';      // 數據庫地址      private $host = 'localhost';      // 數據庫默認名稱, 設置為靜態是因為有切換數據庫的需求      private static $db = 'sakila';      // 數據庫用戶名      private $username = 'root';      // 數據庫密碼      private $password = '';      // 當前數據庫連接      protected $connection;      // 數據庫連接箱,切換已存在的數據庫連接不需要重新通信,從這里取即可      protected static $container = [];        // PDO默認屬性配置,具體請自行查看文檔      protected $options = [          PDO::ATTR_CASE => PDO::CASE_NATURAL,          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,          PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,          PDO::ATTR_STRINGIFY_FETCHES => false,      ];        function construct() {          // 如果從未連接過該數據庫, 那就新建連接          if(empty(self::$container[self::$db])) $this->connect();          // 反之, 從$container中提取, 無需再次通信          $this->connection = self::$container[self::$db];      }        // 生成DSN連接串      protected function buildConnectString() {          return "$this->driver:host=$this->host;dbname=".self::$db;      }        // 連接數據庫      public function connect() {          try {              // 連接數據庫,生成pdo實例, 將之賦予$connection,并存入$container之中              self::$container[self::$db] = $this->connection = new PDO($this->buildConnectString(), $this->username, $this->password, $this->options);                // 返回數據庫連接              return $this->connection;          } catch (Exception $e) {              // 若是失敗, 返回原因              dd($e->getMessage());          }      }        // 切換數據庫      public function setDatabase($db) {          self::$db = $db;          return $this;      }        // 讀取數據      public function read($sql, $bindings) {          // 將sql語句放入預處理函數          $statement = $this->connection->prepare($sql);          // 將附帶參數帶入pdo實例          $this->bindValues($statement, $bindings);          // 執行          $statement->execute();          // 返回所有合法數據, 以Object對象為數據類型          return $statement->fetchAll(PDO::FETCH_OBJ);      }        // 將附帶參數帶入pdo實例      public function bindValues($statement, $bindings) {          // 依次循環每一個參數          foreach ($bindings as $key => $value) {              $statement->bindValue(                  // 如果是字符串類型, 那就直接使用, 反之是數字, 將其+1                  is_string($key) ? $key : $key + 1,                  // 直接放入值                  $value,                  // 這里直白不多說                  is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR              );          }      }        // 改寫數據      public function update($sql, $bindings) {          // 與read不同的地方在于, read返回數據, update返回boolean(true/false)          $statement = $this->connection->prepare($sql);          $this->bindValues($statement, $bindings);          return $statement->execute();      }        // 刪除數據      public function delete($sql, $bindings) {          $statement = $this->connection->prepare($sql);          $this->bindValues($statement, $bindings);          return $statement->execute();      }        // 增加數據      public function create($sql, $bindings) {          $statement = $this->connection->prepare($sql);          $this->bindValues($statement, $bindings);          $statement->execute();          return $this->lastInsertId();      }        // 返回新增id, 如果有      public function lastInsertId() {          $id = $this->connection->lastInsertId();          return empty($id) ? null : $id;      }        // 適用于增刪改      public function exec($sql) {          return $this->connection->exec($sql);      }        // 適用于讀      public function query($sql) {          $q = $this->connection->query($sql);          return $q->fetchAll(PDO::FETCH_OBJ);      }        public function beginTransaction() {          $this->connection->beginTransaction();          return $this;      }        public function rollBack() {          $this->connection->rollBack();          return $this;      }        public function commit() {          $this->connection->commit();          return $this;      }        public function inTransaction() {          return $this->connection->inTransaction();      }  }

本期疑問

1.) 因為php本身的特性, 默認情況下運行完所有代碼類會自行析構,pdo自動斷聯, 所以我沒有disconnect(),讓pdo斷開連接, 不知這樣是不是一種 bad practice?
2.) 增加和改寫數據的兩個函數并不支持多組數據一次性加入,只能單次增該, 原因是我之前寫了嫌太繁瑣所以刪了, 有心的童鞋可以提供方案

? 版權聲明
THE END
喜歡就支持一下吧
點贊7 分享