Posted at

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

More than 1 year has passed since last update.


この記事について

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

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

私家版 Slim Framework チュートリアル (1) 〜 特徴と準備編 - Qiita


概要

今回はルーティングについて説明し、新規作成フォームからチケットを作成してデータベースに保存するところまでを実装していきます。


3. ルーティング


3.1. ルーティングの書き方について

src/routes.php を開いてください。

インストール直後は、以下のような内容になっています。


routes.php

<?php

use Slim\Http\Request;
use Slim\Http\Response;

// Routes

$app->get('/[{name}]', function (Request $request, Response $response, array $args) {
// Sample log message
$this->logger->info("Slim-Skeleton '/' route");

// Render index view
return $this->renderer->render($response, 'index.phtml', $args);
});


ルーティングは、 $app->get(URL, 関数) の形式で記述し、 get の他に、post, put, patch, delete などがあります。

これらは HTTP リクエストメソッドに対応しており、上記5つが主に使われますが、ひとまずここでは基本となる getpost のみ覚えてください。

HTTP リクエストメソッド - HTTP | MDN

GET と POST の違いが分からない方はこちらの記事を参考に。

GETとPOSTの違いについて - Qiita

上の例では、名前付きプレースホルダーと呼ばれる(ルートパラメータと呼ばれることもあります)、変数が URL に含まれています。

試しに、ブラウザにて http://localhost:8080/John を開いてみてください。

Try SlimFramework と表示されていた部分が、Hello John! に置き換わりました。

templates/index.phtml の中を見ると、以下のように条件分岐されており、 $name という変数が使われています(この変数に、URL の末尾に追加した文字列が格納されて表示されます)。

        <?php if (isset($name)) : ?>

<h2>Hello <?= htmlspecialchars($name); ?>!</h2>
<?php else: ?>
<p>Try <a href="http://www.slimframework.com">SlimFramework</a></p>
<?php endif; ?>


3.2. ルーティングの追加

今回は以下のルーティングを使用します。チケットの一覧表示、新規作成、1件表示、編集、削除が一通りできるようになります。


  • GET /tickets - チケット一覧の表示

  • GET /tickets/create - チケットの新規作成用フォームの表示

  • POST /tickets - チケットの新規作成

  • GET /tickets/{id} - チケットの表示

  • GET /tickets/{id}/edit - チケット編集用フォームの表示

  • PUT /tickets/{id} - チケットの更新

  • DELETE /tickets/{id} - チケットの削除

$app->get('/[{name}]', ...) の行よりに、以下のルーティングを追加してください。


routes.php

// 一覧表示

$app->get('/tickets', function (Request $request, Response $response) {
});

// 新規作成用フォームの表示
$app->get('/tickets/create', function (Request $request, Response $response) {
});

// 新規作成
$app->post('/tickets', function (Request $request, Response $response) {
});

// 表示
$app->get('/tickets/{id}', function (Request $request, Response $response, array $args) {
});

// 編集用フォームの表示
$app->get('/tickets/{id}/edit', function (Request $request, Response $response, array $args) {
});

// 更新
$app->put('/tickets/{id}', function (Request $request, Response $response, array $args) {
});

// 削除
$app->delete('/tickets/{id}', function (Request $request, Response $response, array $args) {
});


routes.php 全体は以下をご覧ください。

https://github.com/nunulk/Tutorial-First-Application/blob/chapter3/src/routes.php


4. 新規作成


4.1. チケットの新規作成フォーム

まず最初に、チケットを作成するためのフォームをつくります。

新規作成用フォームを表示するためのルーティングを編集していきましょう。


routes.php

// 新規作成用フォームの表示

$app->get('/tickets/create', function (Request $request, Response $response) {
return $this->renderer->render($response, 'tasks/create.phtml');
});

続いて、テンプレートファイルを作成します。

templates ディレクトリの下に、tasks ディレクトリをつくり、そこに create.phtml というファイルをつくります。

拡張子が .phtml となっていますが、単なる PHP ファイルですので、 .php でも構いません(本記事では公式に倣って .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>
<form method="POST" action="/tickets">
<div class="card">
<div class="card-body">
<h2 class="card-title">チケット作成</h2>
<div class="card-text">
<div class="form-group">
<label for="subject">件名</label>
<input type="text" name="subject" id="subject" class="form-control">
</div>
</div>
</div>
<div class="card-body">
<button class="btn btn-primary">作成</button>
</div>
</div>
</form>
</div>
</body>
</html>

form 要素の method と action を、

<form method="POST" action="/tickets">

新規作成用ルーティングのメソッドと URL に一致するようにしてください。

// 新規作成

$app->post('/tickets', function (Request $request, Response $response) {


4.2. 新規作成

では、新規作成用ルートの中身を実装していきます。

// 新規作成

$app->post('/tickets', function (Request $request, Response $response) {
$subject = $request->getParsedBodyParam('subject');
// ここに保存の処理を書く
// 保存が正常にできたら一覧ページへリダイレクトする
return $response->withRedirect("/tickets");
});

正常にリダイレクトされたことが分かるように、一覧表示のレスポンスにも少し手を加えておきます。

$app->get('/tickets', function (Request $request, Response $response) {

return $response->write('tickets');
});

これで、 新規作成フォームの「作成」ボタンを押すと、


  1. 新規作成が行われる(未実装)

  2. チケット一覧表示ページにリダイレクトされる

  3. "tickets" と表示される

という流れになるはずです。

では、「作成」ボタンをクリックしてみましょう。

ページの左上に "tickets" と表示されましたでしょうか?

Slim では基本的に以下のようにレスポンスを返します。


  1. 単なるテキスト $response->write()

  2. PHP View $this->renderer->render($response, $templatePath)

  3. リダイレクト $response->withRedirect($url)

  4. JSON $response->withJson($data)

場合に応じて使い分けてください。


4.3. データベースに保存する

フォームからリクエストを送信して、データを受け取り、一覧表示へリダイレクトするところまで書きました。

ここからは、受け取ったデータをデータベースへ保存する処理を書いていきます。


4.3.1. データベースとテーブルの作成

データベースは MySQL (5.7) を使用します。インストールされてない場合は、お使いの OS 向けガイドにしたがってインストールしてください(5.7 の日本語版がまだないので 5.6 のを載せますが、インストール方法は同じなはずです)。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 2 MySQL のインストールと更新

macOS の場合は homebrew でインストールするのがいいでしょう。

$ brew install mysql

インストールができたら、ターミナルから MySQL クライアントを起動して、データベースとテーブルをつくります。

$ mysql -uroot

mysql> create database slim_tutorial default charset utf8mb4 collate utf8mb4_general_ci;
mysql> use slim_tutorial;
mysql> create table tickets(id int unsigned not null auto_increment, subject varchar(255) not null, primary key(id));

以降で、データベースの中身を確認することが度々あるので、Sequel Pro などの GUI クライアントをインストールしておくといいでしょう。

https://sequelpro.com/download


4.3.2. データベースの設定

src/settings.php を開いてください。

// database 以下を下記の場所に記述してください。

        // Monolog settings

'logger' => [
'name' => 'slim-app',
'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
'level' => \Monolog\Logger::DEBUG,
],

// database
'db' => [
'host' => 'localhost',
'user' => 'root',
'pass' => '',
'dbname' => 'slim_tutorial'
],
],
];


4.3.3. コンテナへの追加

続いて、 src/dependencies.php を開きます。

同様に、 //database 以下の行を、以下の場合に記述してください。


// monolog
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
return $logger;
};

// database
$container['db'] = function ($c) {
$db = $c['settings']['db'];
$pdo = new PDO('mysql:host=' . $db['host'] . ';dbname=' . $db['dbname'],
$db['user'], $db['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $pdo;
};

データベースアクセスは、 PDO を使用します。

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

PHP: PDO - Manual

これで準備は OK です。


4.3.4. 保存処理を書く

src/routes.php にある、新規作成用ルートの処理に、データベースへの保存処理を追加します。


// 新規作成
$app->post('/tickets', function (Request $request, Response $response) {
$subject = $request->getParsedBodyParam('subject');
// ここに保存の処理を書く
$sql = 'INSERT INTO tickets (subject) values (:subject)';
$stmt = $this->db->prepare($sql);
$result = $stmt->execute(['subject' => $subject]);
if (!$result) {
throw new \Exception('could not save the ticket');
}

// 保存が正常にできたら一覧ページへリダイレクトする
return $response->withRedirect("/tickets");
});

コンテナに登録した PDO のオブジェクトは、 $this->db でアクセスできます。

$this->db->prepare() でプリペアドステートメントを用意し、 $stmt->execute() で INSERT 文を実行する、という流れになります。

エラーが起きた場合の対処は、いくつかやり方がありますが、今回は手間を惜しんで例外をスローするようにしました。

再度新規作成フォームから適当に件名を入力し、作成ボタンをクリックしてください。

tickets テーブルにレコードが生成されていたら成功です。

次回は、一覧表示と1件表示を実装していきます。