PHPでSQL: らくらくPDOクラス（初心者向け）

###データベース操作をらくらくに！
####・[PDOStatementクラス（公式）](https://www.php.net/manual/ja/class.pdostatement.php)を呼び出す

```php:PDOクラスの呼び出しに必要な情報
//PDOクラス呼び出し時には引数が必要です。(ここでは最低限の説明とします)
$dsn = '{ホスト};{データベース};{エンコード}';
$username = '{ユーザー}';
$password = '{パスワード}';

//呼び出し例１: 上の変数を使った例。
$dbh = new PDO($dsn, $username, $password);

//呼び出し例２: １行で纏めてみた例。注:$dsnはスペースで区切らない。
$dbh = new PDO('mysql:host={ホスト名};dbname={データベース名};charset=utf8', '{ユーザー名}', '{パスワード}');

//エラーをスローする設定
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
```
####・DBに接続する！

```php:DBに接続してみる
//try catchを使う -> 接続できなければエラーメッセージを出力して終了。
try {
    $dbh = new PDO('mysql:host={ホスト名};dbname={データベース名};charset=utf8', '{ユーザー名}', '{パスワード}');
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo $e->getMessage();
    exit();
}
//確認
var_dump($dbh); //-> object(PDO)として確認できれば接続OK。
```

####[・PDO->executeメソッド（公式）](https://www.php.net/manual/ja/pdostatement.execute.php)
***[プリペアドステートメントを利用してストアドプロシージャをコールする！（公式）](https://www.php.net/manual/ja/pdo.prepared-statements.php)***

なんのこっちゃとお思いでしょうが、`サーバーリソースにやさしいDBの使い方`だと覚えてください笑

####SELECT
```php:読み込み処理
$name = $_POST['name'];

//$dbh -> 上で初期化済み＆接続済みのPDOクラス。
//プリペアドステートメントの作成（SQL構文をセットしてPDOStatementの生成）
//prepareメソッド（引数: SQL構文）->（返り値: PDOStatement）
$stmt = $dbh->prepare('SELECT * FROM `users` WHERE name = ? AND height = ?');

//ストアドプロシージャをコールする（SQLの実行）
//$stmt = $stmt->execute([{?}, {?}]); -> プレースホルダの数だけ配列を引数で渡す。
//executeメソッド -> （引数: 配列）->（振る舞い: SQL構文の実行）
$stmt->execute([$name, $height]);
//ここでDBからの呼び出し結果がPDOStatementクラス内プロパティに格納されます。

//呼び出し結果を連想配列に直して出力 -> 一つずつ試してみてください。
$user = $stmt->fetch(PDO::FETCH_ASSOC);     //最初の１行分だけを取り出す。-> 連想配列（１record）
$users = $stmt->fetchAll(PDO::FETCH_ASSOC); //呼び出し結果の全行を取り出す。-> 2次元配列（all record）
//注： 変数名は呼び出すテーブル名を踏襲しましょう! -> `users` -> $users

//結果の確認
var_dump($users); //若しくは$user
//配列が確認できればOK！

//プログラム内での確認: nullチェック不要
if ($user) {
    //$user配列が見つかったときの処理
}

//結果の表示
//fetch -> $user: DBレコード１行分。
echo $user['id'] . ' : ';
echo $user['name'] . '<br>';

//fetchAll -> $users: DBレコードから呼び出した全行分（2次元配列）。
foreach ($users as $user) {
    echo $user['id'] . ' : ';
    echo $user['name'] . '<br>';
}
```
####INSERT
```php:インサート処理
$name = 'daisuke';
$height = '184';
$birthday '2000-03-01';
//insert set構文
$sql = 'INSERT INTO users SET name = ?, height = ?, birthday = ?';
//prepareメソッド（引数: SQL構文）->（返り値: PDOStatement）
$stmt = $dbh->prepare($sql);
//executeメソッド（引数: 配列）->（振る舞い: SQL構文の実行）
$stmt->execute([$name, $height, $birthday]);
//-> データの書き込みをphpMyAdmin等で確認してみてください。

//insert values構文 -> 個人的にはset構文のほうがわかりやすいです。
$sql = 'INSERT INTO users (name, height, birthday) VALUES (?, ?, ?)';
```
####UPDATE
```php:アップデート処理
$id = 20;
$name = 'ichiro';
$height = '180';
$birthday = '2000-02-02';

$sql_update = 'UPDATE users SET height = ?, birthday = ? WHERE id = ?';
//prepareメソッド（引数: SQL構文）->（返り値: PDOStatement）
$stmt = $dbh->prepare($sql_update);
//executeメソッド -> （引数: 配列）->（振る舞い: SQL構文の実行）
$stmt->execute([$height, $birthday, $id]);
//データが上書きされたかphpMyAdmin等で確認してみてください。
```

####DELETE
```php:デリート処理
$id = 20;

$sql_delete = 'DELETE FROM users WHERE id = ?';
//prepareメソッド（引数: SQL構文）->（返り値: PDOStatement）
$stmt = $dbh->prepare($sql_delete);
//executeメソッド -> （引数: 配列）->（振る舞い: SQL構文の実行）
$stmt->execute([$id]);
//データが削除されたかphpMyAdmin等で確認してください。
```

####SELECTでのデータの存在確認: 例
```php:SELECT
$sql_select = 'SELECT count(*) FROM {table名} WHERE id = ?';
$stmt = $dbh->prepare($sql_select);
$stmt->execute([$id]);
$count = (int) $stmt->fetchColumn();
if (0 < $count) {
    //データが存在するときの処理
}

//データがたくさんヒットする条件の場合
//top関数は最初の１つ目を見つけたときに検索を終了する -> DB負荷が低い
$sql_select_many = 'SELECT top(1) FROM {table名} WHERE id <= ?';
```

####SELECT以外で結果を確認して処理: 例 -> [rowCount](https://www.php.net/manual/ja/pdostatement.rowcount.php)
```php:INSERT_UPDATE_DELETE
$sql = '{insert, update, deleteいずれかのsql文}';
$stmt = $dbh->prepare($sql);
$stmt->execute([パラメータ]);
$count = $stmt->rowCount(); //-> SQL文が作用した行数を返す :int
if (0 < $count) {
    //SQL文が成功したときの処理
} else {
    //成功しなかったときの処理
}
//selectでも実行可能ですが公式に推奨されていません。
```
####[bindParam（公式）](https://www.php.net/manual/ja/pdostatement.bindparam.php)

```php:bindParamの有無
//コストの違いがあります。
//サーバーの負荷を考えて使い分ければいいと思います。

$sql = 'UPDATE users SET name = ?, birthday = ?, WHERE id = ?';
$stmt = $dbh->prepare($sql);

//executeに配列で引数を与えた例 -> 型キャストが行われる -> 高コスト
$stmt->execute([$name, $birthday, $id]);

//bindParam -> 型を静的に指定 -> 低コスト
$stmt->bindParam(1, $name, PDO::PARAM_STR);
$stmt->bindParam(2, $birthday, PDO::PARAM_STR);
$stmt->bindParam(3, $id, PDO::PARAM_INT);
$stmt->execute();
```


####[・PDO->queryメソッド（公式）](https://www.php.net/manual/ja/pdo.query.php)

```php:queryメソッド
//プレースホルダが使えないのであまり使う場面はありません。
//引数のままSQLを実行します。
//試しに使ってみるのにいいと思います。
$sql = 'SELECT * FROM users';
$users = $dbh->query($sql);
```

####・ショートコードでちょっとだけDBの負荷を減らす。
***[Traversableインターフェース（公式）](https://www.php.net/manual/ja/class.traversable.php)***

```php:$stmt->execute();から直接出力する
//Traversableを実装しているためfetchAllは省略できる。 
$users = $stmt->execute([$name]);
foreach ($users as $user) {
    echo $user['id'] . ' : ' . $user['name'] . '<br>';
}
//上記のようにfetchAllで配列に変更しなくとも出力できます。
//-> 配列に変換する分の負荷が減ります。
//ITraversableはvar_dumpで配列の確認ができません。
//使い分けしてください。
```
####・おまけ知識[（接続、および接続の管理）](https://www.php.net/manual/ja/pdo.connections.php#pdo.connections)
```php:持続的な接続でキャッシュを効かせてパフォーマンスの向上
<?php
$option = [PDO::ATTR_PERSISTENT => true]; //-> コンストラクタに入れる必要あり。
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, $option);
?>
```


`LGTMお願いします！`
`モチベーションがあがります！`