PHP
slim
slimphp

私家版 Slim Framework チュートリアル (3) 〜 表示編

More than 1 year has passed since last update.


この記事について

PHP のマイクロフレームワークのひとつである、Slim Framework ですが、チュートリアルがいまいちイケてないので、お題を流用しつつ、初学者にももう少し分かりやすくなるよう、アレンジしてみようという試みです。

本記事は第3回で、前回はこちら。

私家版 Slim Framework チュートリアル (2) 〜 ルーティングと新規作成編 - Qiita


概要

今回は、チケットの表示用ページ(一覧および1件)を実装していきます。


5. 一覧表示


5.1. View ファイルの作成

View ファイルをつくります。

templates/tickets/index.phtml というファイルを作成してください。

<!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>
<div class="card">
<div class="card-body">
<h2 class="card-title">チケット一覧</h2>
<div class="card-text">
<ul class="list-group">
<?php foreach ($tickets as $ticket): ?>
<li class="list-group-item"><?= htmlspecialchars($ticket['subject'], ENT_QUOTES, 'UTF-8', false) ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
</div>
</body>
</html>

htmlspecialchars という長ったらしい関数は、HTML に文字列を出力する際に、HTML や JavaScript にとって意味のある記号を、エンティティと呼ばれる文字列に変換し、悪意あるスクリプトが実行されないようにするために必要です。

関数の詳しい説明は以下をご覧ください。

PHP: htmlspecialchars - Manual

引数は前から順に、表示させたい文字列、変換する特殊記号の種類、文字コード、既存のエンティティを変換するかどうか、です。

たとえば、

<script>alert('XSS!');</script> という文字列が入っていたときに、そのまま出力してしまうと、JavaScript がブラウザ上で実行されてしまいます。

ですので、 htmlspecialchars 関数を用いて、以下のような文字列に変換することで、スクリプトが実行されないようにします。

&lt;script&gt;alert(&#039;XSS!&#039;);&lt;/script&gt;

多くのフレームワークやテンプレートエンジンでは、自動的にこの変換処理を行ってくれるものが多いですが、 Slim はマイクロフレームワークなので、自分でやる必要があります。


5.2. ルーティング関数にチケットの一覧を取得する処理を書く

src/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, 'tasks/index.phtml', $data);
});

新規作成のところでは、 INSERT 文を使用しましたが、今度はデータの取得なので SELECT 文を使用します。

今回は SQL に渡すパラメータがないので、 prepare の代わりに query メソッドを使います。

また、データを取得するには fetch メソッドを使います。

fetch メソッドは連想配列でレコードを返しますので、格納されるデータは以下のようになっています。

['id' => '1', 'subject' => '一覧にチケットが表示されない'];

複数件レコードがある場合は、レコード分上記連想配列が取得されますので、以下のように、連想配列の配列が格納されます。

$tickets = [

['id' => '1', 'subject' => '一覧にチケットが表示されない'],
['id' => '2', 'subject' => '詳細にチケットが表示されない'],
:
:
];


5.3. チケットを登録して表示されるか確認しましょう

http://localhost/tickets/create を開いて、チケットを登録してください。

image.png

リダイレクトされて、いま登録したチケットの件名が表示されましたでしょうか?

image.png

こんな感じで出てくれば成功です。


5.4. ヘルパー関数の定義

ページに文字列を表示する際に htmlspecialchars 関数を使わなければいけない、と書きましたが、毎回こんな長ったらしい関数を書いてられない、と思うのが人情というものです。というわけで、 一度脱線して、この処理を関数化してしまいましょう。

まず、src/helpers.php というファイルを作成して、以下のように記述してください。

<?php

if (!function_exists('e')) {
function e(string $s): string {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false);
}
}

e という名前の関数が存在しなければ、新たに e という名前の関数を定義します。 PHP では、同名の関数を複数定義すると実行時エラーになってしまうので、このような措置が必要になります。

続いて、 composer.json に以下の記述を追加します("autoload-dev"の上がいいんじゃないかと思います)。

    "autoload": {

"files": ["src/helpers.php"]
},

これは、アプリケーションの実行時に、自動的に PHP ファイルをロードして、中に書かれた関数を利用できるようにするための記述です。

最後に、ターミナルから以下のコマンドを実行します。

$ composer dump-autoload

du と省略して書くこともできます。めんどくさければ以下のように実行してください。

$ composer du

これで、テンプレートでは以下のように短く書けるようになります。

            <?php foreach ($tickets as $ticket): ?>

<li class="list-group-item"><?= e($ticket['subject']) ?></li>
<?php endforeach; ?>

ブラウザをリロードして、エラーにならないか確認してください。


5.5. 1件表示ページへのリンクを張る

リスト要素をクリックしたら当該チケットの詳細ページへ飛べるようにしていきます。

以下のように、a 要素を追加してリンクが表示されるようにしてください。

            <?php foreach ($tickets as $ticket): ?>

<li class="list-group-item"><a href="/tickets/<?= $ticket['id']
?>"><?= e($ticket['subject']) ?></a></li>
<?php endforeach; ?>

image.png

リンクをクリックして、アドレスバーの URL が "/tickets/1" となり、エラーにならないか確認してください。


5.6. 新規作成ページへのリンクを張る

ついでに、一覧表示ページに新規作成ページへのリンクを張りましょう。

この辺ですかね。

      <h2 class="card-title">チケット一覧</h2>

<div class="card-text">
<a href="/tickets/create">新規作成</a>

image.png

ここまでの変更のすべては以下から閲覧できます。

https://github.com/nunulk/Tutorial-First-Application/tree/chapter5


6. 1件表示

いまはまだ1件表示用のルーティングに何も処理を書いていないので、真っ白なページが表示されています。

ここから、1件表示用の View を実装していきます。


6.1. View ファイルの作成

templates/tickets/show.phtml というファイルを作成し、以下のように記述してください。

<!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>
<div class="
card">
<div class="
card-body">
<h2 class="
card-title">チケット詳細</h2>
<div class="
card-text">
<table class="
table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>件名</th>
</tr>
</thead>
<tbody>
<tr>
<td><?= e(
$ticket['id']) ?></td>
<td><?= e(
$ticket['subject']) ?></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>

$ticket という変数に、以下のような形式でデータが格納されていることを想定しています。

['id' => '1', 'subject' => '一覧にチケットが表示されない'],


6.2. ルーティング関数にチケットデータを取得する処理を書く

続いて、 src/routes.php を開いて、1件表示用のルーティング関数に処理を追加していきます。

// 表示

$app->get('/tickets/{id}', function (Request $request, Response $response, array $args) {
$sql = 'SELECT * FROM tickets WHERE id = :id';
$stmt = $this->db->prepare($sql);
$stmt->execute(['id' => $args['id']]);
$ticket = $stmt->fetch();
if (!$ticket) {
return $response->withStatus(404)->write('not found');
}
$data = ['ticket' => $ticket];
return $this->renderer->render($response, 'tasks/show.phtml', $data);
});

一覧表示同様、 SELECT 文を使います。

今回は SQL に渡すパラメータがありますので、 prepare メソッドを使います。

名前付きプレースホルダーのことを覚えていますか?

忘れていたら「3. ルーティング」の章へ戻ってください。

https://qiita.com/nunulk/items/8492548678aac697d0aa#3-%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0

{id} という名前付きプレースホルダーが定義されていて、URL に 1 という ID が指定されていれば、id=1 と渡ってきます。これはルーティング関数の第3引数にある $args の中に id というキーで格納されていますので、 $args['id'] で取得することができます。

SQL の実行の際に引数で渡してやりましょう。

    $stmt->execute(['id' => $args['id']]);

一覧表示の際と同様、 fetch メソッドを使って、レコードを取り出します。

    $ticket = $stmt->fetch();

データが正しく取得できれば、以下のようなデータが格納されます。

['id' => '1', 'subject' => '一覧にチケットが表示されない'],


6.3. 表示の確認


6.3.1. データが存在する場合

では、一覧表示ページから、リンクをクリックしてみてください。

image.png

正しく表示されたでしょうか?


6.3.2. データが存在しない場合

もし、データが空なら(存在しない ID を指定した場合、など)、 HTTP ステータスコード 404 をブラウザに返してください。

HTTP ステータスコードについてはこちらを参考に。

HTTP レスポンスステータスコード - HTTP | MDN

Slim では、以下のように withStatus メソッドを使って、任意のステータスコードでレスポンスを返すことができます。

        return $response->withStatus(404)->write('not found');

ためしにブラウザのアドレスバーに http://localhost/tickets/2 と打ってみてください。

"not found" と表示されるはずです。

ここまでの変更のすべては以下から閲覧できます。

https://github.com/nunulk/Tutorial-First-Application/tree/chapter6

次回は、編集と削除機能を実装していきます。