Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する（4.PDO使用）

# はじめに
[Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する
（3. LoggingとController）](https://qiita.com/erik_t/items/308091f436e74e2a232e)の続きです。

# 前提
下記記事で構築した環境を前提とします。

- Windows10にVagrantをを入れてCentOS7をインストールしよう([1](https://qiita.com/erik_t/items/d7f7abbf7deafa4c6fe4)、[2](https://qiita.com/erik_t/items/909efb63fd389f486ec3)、[3](https://qiita.com/erik_t/items/2e3c43cfe8731621b82d)、[4](https://qiita.com/erik_t/items/8d006b599815c8fbf38b)、[5](https://qiita.com/erik_t/items/9fbe7b5630b1f62893a1)、[6](https://qiita.com/erik_t/items/11f24633f4248e4f33f0)) 
- ローカルでLAMP環境を構築しよう（[0](https://qiita.com/erik_t/items/9c7c49db061a250cbc32)、[1](https://qiita.com/erik_t/items/e36235896b327f5cb461)、[2](https://qiita.com/erik_t/items/8dc3f2a5e8e8c9376e27)、[3](https://qiita.com/erik_t/items/617503557020a4a6ba82)、[4](https://qiita.com/erik_t/items/a898d0f547bff6b3d8ff)）
- [CentOS7にComposerをインストールしよう](https://qiita.com/erik_t/items/0b3492f38f6cd8605a4c)

- 私家版 Slim Framework チュートリアル ([1](https://qiita.com/nunulk/items/4b5c15f13ade660cafbc)、[2](https://qiita.com/nunulk/items/8492548678aac697d0aa#_reference-1dc558a7a78d9b00868b)、[3](https://qiita.com/nunulk/items/33adfbba55057a7d0c5c#_reference-8903d1023c2501e00d0b)、[4](https://qiita.com/nunulk/items/fb5ef759c1337e99d878#_reference-56dbec1889beef3f7f4d)、[5](https://qiita.com/nunulk/items/92d06bada7657ce212e7#_reference-1e4198f2501e274dbdd8)、[6](https://qiita.com/nunulk/items/99c12ac8e7b631bd8ed3#_reference-11f698acaecbfe32d5ff))
[@nunulk](https://qiita.com/nunulk)さんのチュートリアルを一通り。
ここで作成したDBを使いまわします。
- Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する([1](https://qiita.com/erik_t/items/14d0103e343e440f6dc5)、[2](https://qiita.com/erik_t/items/9d896867234c904409de)、[3](https://qiita.com/erik_t/items/308091f436e74e2a232e)）

# 手順
~~1. 専用のユーザーを作成~~
~~2. プロジェクトディレクトリを作成・slim3インストール~~
~~3. 各種設定~~
~~4. DocumentRootを変更＆表示確認~~
~~5. Twigを使ってみよう~~
~~6. Loggingしてみよう~~
~~7. Controllerを作成しよう~~
**8. PDOを使用してデータベースに接続しよう**
9. @nunulkさんのチュートリアルで作成したチケット管理システムをtwigを使って再現しよう

# やってみよう
今回の記事では、手順8を行います。
## 8. PDOを使用してデータベースに接続しよう
### settings.phpに設定を追加
以下の記述を追加します。

```:config/settings.php
$settings['db'] = [
  'driver' => 'mysql',
  'host' => 'localhost',
  'user' => 'root',
  'pass' => 'P@ssw0rd',
  'dbname' => 'slim_tutorial',
  'charset' => 'utf8',
  'collection' => 'utf8_unicode_ci',
  'flags' => [
    PDO::ATTR_PERSISTENT => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  ],
];

$settings['renderer'] = [
  'template_path' => __DIR__ . '/../templates/',
];
```
### container.phpに設定を追加
以下の記述を追加します。

```:config/container.php
$container['db'] = function (Container $container) {
    $settings = $container->get('settings');

    $host = $settings['db']['host'];
    $dbname = $settings['db']['dbname'];
    $username = $settings['db']['user'];
    $password = $settings['db']['pass'];

    $dsn = "mysql:host=$host;dbname=$dbname";

    $options = [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_PERSISTENT => false,
        PDO::ATTR_EMULATE_PREPARES => true,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ];

    return new PDO($dsn, $username, $password, $options);
};

$container['renderer'] = function (Container $c) {
    $settings = $c->get('settings')['renderer'];
    return new \Slim\Views\PhpRenderer($settings['template_path']);
};
```

### routes.phpにルートを追加
以下の記述を追加します。

```:config/routes.php
$app->get('/tickets', function (Request $request, Response $response) {
    $sql = 'SELECT * FROM tickets';
    $stmt = $this->db->query($sql);
    $tickets = [];
    while($row = $stmt->fetch()) {
        $tickets[] = $row;
    }
    $data = ['tickets' => $tickets];
    return $this->renderer->render($response, 'tickets/index.phtml', $data);
});
```
## #@nunulkさんのチュートリアルで作成したチケット管理システムのチケット一覧だけ表示してみる
さて、ここまででDBに接続する準備はできました。
早速@nunulkさんのチュートリアルで作成したチケット管理システムのチケット一覧を表示して、
DBに接続できるか試してみましょう。

templatesディレクトリの下にticketsディレクトリを作成し、
事前に作っておいたファイルを以下のコマンドでコピーしてきましょう。
コピー元のパスはご自分の環境に置き換えてください。

```:index.phtmlコピー
cp /home/slimuser/projects/slim/Tutorial-First-Application/templates/tickets/index.phtml /home/slimuser/projects/slim/SampleApplication/templates/tickets/index.phtml
```
```:header.phtmlコピー
cp /home/slimuser/projects/slim/Tutorial-First-Application/templates/header.phtml /home/slimuser/projects/slim/SampleApplication/templates/header.phtml
```
```footer.phtmlコピー
cp /home/slimuser/projects/slim/Tutorial-First-Application/templates/footer.phtml /home/slimuser/projects/slim/SampleApplication/templates/footer.phtml
```

```helpers.phpコピー
cp /home/slimuser/projects/slim/Tutorial-First-Application/src/helpers.php /home/slimuser/projects/slim/SampleApplication/src/helpers.php
```
composer.jsonには下記を追記します。

```composer.json
    "autoload": {
      "psr-4": {
        "App\\":"src/"
      }
      ,"files":["src/helpers.php"] ←追記
    },
```
composer.jsonを編集したので、下記コマンドを実行します。

```
composer dump-autoload
```
### 動作確認
ブラウザで以下のURLを開いてください。
以下の画像のように表示されたらOKです。

```
http://192.168.33.90/tickets
```
![ticketindex.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409907/a597c503-063c-e435-8972-2eec20f3dda9.png)

## ついでに先程表示したチケット一覧をtwig化してみよう
### base.twigを作成
@nunulkさんのチュートリアルではheader.phtmlとfooter.phtmlを作成し、
各ファイルから呼び出していましたが、せっかくtwigを使用するのでアプローチを変えてみましょう。
`templates/base.twig`というファイルを作成し、以下のように記述してください。

```:templates/base.twig
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <title>チケット管理</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="container">
  <h1>チケット管理</h1>
  {% block content %}{% endblock %}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>
```
`templates/base.twig`という共通のテンプレートを作成し、
それぞれのファイルから`templates/base.twig`を呼び出し、
{% block content %}～{% endblock %}の中にそれぞれのメインコンテンツを記述します。

header.phtmlとfooter.phtmlはもう使用しないので削除しておきましょう。

```
rm templates/header.phtml
rm templates/footer.phtml
```

### index.phtmlをtwig化
index.phtmlの拡張子をtwigに変更します。

```
mv /home/slimuser/projects/slim/SampleApplication/templates/tickets/index.phtml /home/slimuser/projects/slim/SampleApplication/templates/tickets/index.twig
```

`templates/tickets/index.twig`を以下のように変更します。

```:templates/tickets/index.twig（変更前）
<?= $this->fetch('header.phtml') ?>
  <div class="card">
    <div class="card-body">
      <h2 class="card-title">チケット一覧</h2>
      <div class="card-text">
        <a href="/tickets/create">新規作成</a>
        <ul class="list-group">
          <?php foreach($tickets as $ticket): ?>
            <li class="list-group-item">
              <a href="/tickets/<?= $ticket['id'] ?>">
                <?= e($ticket['subject']) ?>
              </a>
            </li>
          <?php endforeach ?>
        </ul>
      </div>
    </div>
  </div>
<?= $this->fetch('footer.phtml') ?>
```

```:templates/tickets/index.twig（変更後）
{% extends 'base.twig' %}
{% block content %}
  <div class="card">
    <div class="card-body">
      <h2 class="card-title">チケット一覧</h2>
      <div class="card-text">
        <a href="/tickets/create">新規作成</a>
        <ul class="list-group">
          {% for ticket in tickets %}
            <li class="list-group-item">
              <a href="/tickets/{{ticket.id}}">
                {{ticket.subject|escape('html')}}
              </a>
            </li>
         {% endfor %}
        </ul>
      </div>
    </div>
  </div>
{% endblock %}
```
### helpers.php削除
helpers.phpから、htmlspecialchars関数を呼び出すようにしていましたが、
twigではhtmlspecialchars関数を使わないので、helpers.phpを以下のコマンドで削除しましょう。

```
rm src/helpers.php
```
composer.jsonからもhelpers.phpの記述を削除します。

```composer.json（変更前）
    "autoload": {
      "psr-4": {
        "App\\":"src/"
      }
      ,"files":["src/helpers.php"]
    },
```

```composer.json（変更後）
    "autoload": {
      "psr-4": {
        "App\\":"src/"
      }
    },
```
composer.jsonを編集したので、下記コマンドを実行します。

```
composer dump-autoload
```

### routes.phpのルートを修正
以下の記述になるように修正します。

```:config/routes.php
$app->get('/tickets', function (Request $request, Response $response) {
    $sql = 'SELECT * FROM tickets';
    $stmt = $this->db->query($sql);
    $tickets = [];
    while($row = $stmt->fetch()) {
        $tickets[] = $row;
    }
    $data = ['tickets' => $tickets];
    return $this->get(Twig::class)->render($response, 'tickets/index.twig', $data);
});
```
### 動作確認
ブラウザで以下のURLを開いてください。
先程のチケット一覧画面が表示されたらOKです。

```
http://192.168.33.90/tickets
```

### twig文法解説
今回使用したtwigの文法を解説します。
#### 継承(extends)
twigではHTML全体を記述したベーステンプレートを用意して、
子テンプレートで上書きできる部分を blockタグで定義することができます。
今回はベーステンプレートが`templates/base.twig`、`templates/tickets/index.twig`が子テンプレートです。
子テンプレートでは以下のように記述することでベーステンプレートを継承します。

```
{% extends 'ベーステンプレート名' %}
```
blockタグは以下のように記述します。
子テンプレートでは、ベーステンプレートで記述した同名のブロック名を持つblockタグ内にメインコンテンツを記述します。

```
{% block ブロック名 %}{% endblock %}
```

#### for文
Twig でループを作るには for 〜 in を用います。
{% for key, value in users %} のようにしてキー名を取り出すことも出来ます

```
{% for ticket in tickets %}
  //　処理
{% endfor %}
```
このような書き方も。

```
{% for i in 0..10 %}
  //　処理
{% endfor %}
```

#### HTMLエスケープ
phpのhtmlspecialchars関数でやっていたことはtwigでは以下のように記述します。

```
{{ ticket.subject|escape }}
または
{{ ticket.subject|e }}
```
デフォルトでhtmlエスケープを行いますが、コンテクストに応じたエスケープをしたい場合は、以下のように記述します。

```
{{ ticket.subject|e('html') }}
{{ ticket.subject|e('js') }}
{{ ticket.subject|e('css') }}
など
```

# 参考サイト
[Creating your first Slim 3 Framework Application](https://odan.github.io/2017/11/30/creating-your-first-slim-framework-application.html)
私家版 Slim Framework チュートリアル ([1](https://qiita.com/nunulk/items/4b5c15f13ade660cafbc)、[2](https://qiita.com/nunulk/items/8492548678aac697d0aa#_reference-1dc558a7a78d9b00868b)、[3](https://qiita.com/nunulk/items/33adfbba55057a7d0c5c#_reference-8903d1023c2501e00d0b)、[4](https://qiita.com/nunulk/items/fb5ef759c1337e99d878#_reference-56dbec1889beef3f7f4d)、[5](https://qiita.com/nunulk/items/92d06bada7657ce212e7#_reference-1e4198f2501e274dbdd8)、[6](https://qiita.com/nunulk/items/99c12ac8e7b631bd8ed3#_reference-11f698acaecbfe32d5ff))
[[PHP]Twigテンプレートの実践的な構成と作り方](https://php-archive.net/php/twig-advanced/)
[Twig テンプレート](https://microengine.jp/cms/manual/2.0/twig_template.html)
[escape](https://twig.symfony.com/doc/2.x/filters/escape.html)

# 関連ページ
## Windows10にVagrantをを入れてCentOS7をインストールしよう
[1. VagrantインストールからVagrantfileを設置まで](https://qiita.com/erik_t/items/d7f7abbf7deafa4c6fe4)
[2. 仮想マシンの操作](https://qiita.com/erik_t/items/909efb63fd389f486ec3)
[3. WinSCP、Tera Termに秘密鍵でログイン ](https://qiita.com/erik_t/items/2e3c43cfe8731621b82d)
[4. WinSCP、Tera Termにrootユーザーでパスワードログイン](https://qiita.com/erik_t/items/8d006b599815c8fbf38b)
[5. zip/unzipをインストール](https://qiita.com/erik_t/items/9fbe7b5630b1f62893a1)
[6. Vagrantにて仮想環境を配布](https://qiita.com/erik_t/items/11f24633f4248e4f33f0)

## ローカルでLAMP環境を構築しよう
[0. 事前準備](https://qiita.com/erik_t/items/9c7c49db061a250cbc32)
[1. Apacheをインストール](https://qiita.com/erik_t/items/e36235896b327f5cb461)
[2. MySQLをインストール](https://qiita.com/erik_t/items/8dc3f2a5e8e8c9376e27)
[3. PHPをインストール](https://qiita.com/erik_t/items/617503557020a4a6ba82)
[4. ファイアウォールとか停止する](https://qiita.com/erik_t/items/a898d0f547bff6b3d8ff)

## Composerをインストール
[CentOS7にComposerをインストールしよう](https://qiita.com/erik_t/items/0b3492f38f6cd8605a4c)

## PHP Slim3フレームワークのサンプルアプリを作ろう
[2-1. First Application Walkthrough Getting Set Upまで](https://qiita.com/erik_t/items/9e9f6c93d2403ac90242)

## Apache
 [DocumentRootを変更しよう](https://qiita.com/erik_t/items/d8f4746d2dc2992f329e)

## Slim3 Framework×slim-skeleton不使用×twigでプロジェクトを作成する
[1. プロジェクト作成～各種設定](https://qiita.com/erik_t/items/14d0103e343e440f6dc5)
[2. DocumentRoot変更～Twigを使用](https://qiita.com/erik_t/items/9d896867234c904409de)
[3. LoggingとController](https://qiita.com/erik_t/items/308091f436e74e2a232e)
4.PDO使用
[5. チケット管理システムを再現➀](https://qiita.com/erik_t/items/fff0edee3ddfa509b2a5)
[6. チケット管理システムを再現➁](https://qiita.com/erik_t/items/28f2cabc460312ea5111)