前回の続きがこんなに空いてしまいました。DB周りの実装にかなり悩んでいて、悩んでいるうちにめんどくさく…
DB周りの実装については、PDOを利用する方法一択かと思います。
この辺りの機能は一度作ってしまえば、再利用しやすいし、使い慣れると、開発速度も速くなります。また、SQLインジェクションなどの脆弱性を埋め込みにくくなりますので一石二鳥。
基本的にDB周りで必要な機能は以下のとおり。
- SELECT
- INSERT
- UPDATE
- DELETE
これらのSQLが手っ取り早く実行できればいいわけです。
クラス作成にあたっては、設計思想によって、実装方法は様々なバリエーションがあることになりますが、以下の目的で作成することとします。
- メソッドの呼び出し方法が簡単であること
- 接続(コネクションを使い回すこと)
- SQLのエラーが適切に通知されること
これらを目的として実装します。
以下のようなスケルトンを作成します。
<?php
namespace MyApp\common;
class Db
{
/**
* SELECT実行
* @param string $sql
* @param array $arr
* @return array
*/
public static function select($sql, array $arr = array())
{
}
/**
* INSERT実行
* @param string $sql
* @param array $arr
* @return int
*/
public static function insert($sql, array $arr)
{
}
/**
* UPDATE実行
* @param string $sql
* @param array $arr
* @return bool
*/
public static function update($sql, array $arr)
{
}
/**
* DELETE実行
* @param string $sql
* @param array $arr
* @return bool
*/
public static function delete($sql, array $arr)
{
}
}
static
でメソッドを設定しておくと、呼び出し元からは、以下のように呼び出しできます。
<?php
use MyApp\common\Db;
$sql = 'SELECT id FROM Users';
$res = Db::select($sql);
$sql = 'INSERT INTO Users (id, name) VALUES (null, :name)';
$arr = array(
':name' => 'Sato'
);
Db::insert($sql, $arr);
楽チン!
今回のメインは、「コネクションをいかにして使いまわすか」ということですが、以下のように実装します。
大事なのは、getInstance()
の実装です。$instance
が null
のとき、PDO
オブジェクトを生成し、それを返す。null
でないときは、生成済みの PDO
オブジェクトを返す仕組みになっています。
トランザクション処理に関連する、transaction()
, commit()
, rollback()
を実装しています。
<?php
class Db
{
/**
* 接続文字列
*/
const DSN = 'mysql:dbname=%s;host=localhost;charset=utf8;';
/**
* データベース名
*/
const DBNAME = 'sample';
/**
* ユーザー名
*/
const USER_NAME = 'root';
/**
* パスワード
*/
const PASSWORD = 'password';
/**
* PDOインスタンス
* @var \PDO
*/
static private $instance = null;
/**
* コンストラクタ
* @access private
*/
private function __construct()
{
// 外部からインスタンス化できないように、private で宣言
}
/**
* インスタンスを取得
* @return \PDO
*/
private static function getInstance()
{
if (is_null(self::$instance)) {
$options = array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
, \PDO::ATTR_AUTOCOMMIT => true
);
self::$instance = new \PDO(
sprintf(self::DSN, self::DBNAME)
, self::USER_NAME
, self::PASSWORD
, $options
);
}
return self::$instance;
}
/**
* クローン
* @throws \Exception
*/
final public function __clone()
{
$msg = sprintf('Clone is not allowed against %s', get_class($this));
throw new \Exception($msg);
}
/**
* トランザクション実行
*/
public static function transaction()
{
self::getInstance()->beginTransaction();
}
/**
* コミット
*/
public static function commit()
{
self::getInstance()->commit();
}
/**
* ロールバック
*/
public static function rollback()
{
self::getInstance()->rollBack();
}
/**
* SELECT実行
* @param string $sql
* @param array $arr
* @return array
*/
public static function select($sql, array $arr = array())
{
$stmt = self::getInstance()->prepare($sql);
$stmt->execute($arr);
return $stmt->fetchAll();
}
/**
* INSERT実行
* @param string $sql
* @param array $arr
* @return int
*/
public static function insert($sql, array $arr)
{
if (!self::getInstance()->inTransaction()) {
throw new \Exception('Not in transaction!');
}
$stmt = self::getInstance()->prepare($sql);
$stmt->execute($arr);
return self::getInstance()->lastInsertId();
}
/**
* UPDATE実行
* @param string $sql
* @param array $arr
* @return bool
*/
public static function update($sql, array $arr)
{
if (!self::getInstance()->inTransaction()) {
throw new \Exception('Not in transaction!');
}
$stmt = self::getInstance()->prepare($sql);
return $stmt->execute($arr);
}
/**
* DELETE実行
* @param string $sql
* @param array $arr
*/
public static function delete($sql, array $arr)
{
if (!self::getInstance()->inTransaction()) {
throw new \Exception('Not in transaction!');
}
$stmt = self::getInstance()->prepare($sql);
return $stmt->execute($arr);
}
}