こんばんは。 GW後半が始まりましたが、皆様はいかがお過ごしでしょうか。
僕はというと、タイトル通り、某企画サイトのバックエンド側を実装しておりました。 (仕事ではなく、プライベートでの趣味の一環です。とある方から依頼を受けました。)
- 本番環境の制約
- 今回の要件
- 要件の実現方法
- 今回利用した各フレームワーク、ライブラリのバージョン
- 開発環境
- Twitter OAuth認証によるユーザ識別
- Slimの導入
- データベース接続
- コントローラ作成
- ルーティングの設定
- ビューの設定
- 静的ファイルの配置
- 最後に、その他感想
本番環境の制約
依頼元の本番環境のサーバーがレンタルサーバーということもあり、 利用できる言語はPHPのみで、その中でも利用可能なWebフレームワークが限られていました。
特にサーバーの制約がなければ、比較的慣れている「Django」を 利用していたと思うので、普段使わないものを使うきっかけができて、逆に良かったと思います。
今回の要件
- ユーザの識別
- ただしメールアドレス等の個人情報は収集しない
- 識別したユーザごとの状態管理、ユーザ自身の操作による状態更新
要件の実現方法
- Twitter OAuth認証の導入
- ユーザの状態をDBテーブルで管理する
- ユーザの状態更新のために、然るべきAPIを準備し、HTTPリクエストによって処理を実行する
今回利用した各フレームワーク、ライブラリのバージョン
- Slim 3.1
- illuminate/database(Eloquent ORM) 5.5
- Twig 2.3
開発環境
- 仮想環境のUbuntu 14.04 - gistで提供されていたVagrantfile及びprovisionシェル
- Vagrant / PHP7.1 + MySQL5.6 + Apache · GitHub
- PHP7.1 + MySQL5.6 + Apache
- 仮想環境(ゲスト)の80番ポートをホストの8000番にポートフォワード
Twitter OAuth認証によるユーザ識別
実装を始める前には、Twitter認証のやり方が全くわからなかったのですが、こちらの記事を参照して なんとか実装することができました。
大変わかりやすい記事で、本当に助かりました。 誠にありがとうございました。
Slimの導入
ディレクトリ構成の検討
Slimを利用するにあたって、ディレクトリ構成を決める必要があります。 自分で適当に決めることもできるようです。
個人的なアプリ開発ならば適当に決めたものを利用するのですが、 一応「依頼を受けての開発」なので、素直にテンプレート(Slim-Skeleton)を利用することにしました。
Slim Skeletonプロジェクトの作成
Composerを利用することで、すばやくSlimアプリの作成ができます
$ composer create-project slim/slim-skeleton [appname]
作成後のディレクトリ構成
こんな感じのプロジェクトが作成されます。 public配下のindex.phpには各種設定ファイルの読み込みに関する処理が記述されています。
appname ├── composer.json ├── composer.lock ├── CONTRIBUTING.md ├── docker-compose.yml ├── .gitignore ├── logs/ │ └── README.md ├── phpunit.xml ├── public/ │ ├── .htaccess │ └── index.php ├── README.md ├── src/ │ ├── dependencies.php │ ├── middleware.php │ ├── routes.php │ └── settings.php ├── templates/ │ └── index.phtml └── tests/ └── Functional/ ├── BaseTestCase.php └── HomepageTest.php
データベース接続
「Slim DB」で検索してみると、PDO(PHP Data Objects)を利用した データベース接続の解説記事が出てきました。 しかし、「DBテーブルごとに、紐づくモデルクラスを作り、SQLを書く必要がありそうで面倒くさそう」的なことや 「DB接続に利用するコネクションオブジェクトの破棄等をうっかりと忘れてしまいそう」といったことを 考えてしまい、もっと別の方法はないか探すことにしました。
すると、公式ドキュメントに「Eloquent」という文字があったのです!
Using Eloquent with Slim - Slim Framework
Eloquent ORM(オブジェクト関係マッパー)は人気のPHPフレームワーク「Laravel」のORMとして提供されている アクティブレコードによるデータ操作の実装です。
(なお、Eloquentという言葉に反応した理由は下記の記事にもあるとおり、最近Laravelに入門していたからです。) www.rinsymbol.net
フレームワーク「Laravel」を使っていなくても、 データ操作部分に関係するモジュールを導入すれば、Slimでも使えるようになるとのことでした。
Eloquent ORMの導入(と、Twigの導入)
下記のURL先の記述従い、必要な設定を追記していきます。
Using Eloquent with Slim - Slim Framework
- Eloquentモジュールの導入
リンク先は5.1になっていますが、僕は5.5が使いたかったので、5.5を指定しました。 (5.5を指定した理由ですが、LaravelのLTS(Long Term Support)が5.5らしく、とりあえずそれに合わせといた方が良いのでは、と短絡的に考えたためです)
$ composer require illuminate/database=5.5.*
- 必要に応じて、twigモジュールの導入
今回、ビュー側のテンプレートエンジンはtwigを利用したかったので、僕はここでtwigも導入しました。
$ composer require twig/twig
- src/settings.phpファイルへのデータベース設定の追記
settings.phpファイルには、ログやレンダラに関する設定が連想配列で記載されています。 この中に、データベースに関する設定を追加します。('db'から始まる行~timezoneまで)
<?php 'settings' => [ 'displayErrorDetails' => true, // set to false in production 'addContentLengthHeader' => false, // Allow the web server to send the content-length header 'determinateRouteBeforeAppMiddleware' => false, // Renderer settings 'renderer' => [ 'template_path' => __DIR__ . '/../templates/', ], // ここから↓ 'db' => [ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'database_name', 'username' => 'user_name', 'password' => 'password', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'timezone' => '-09:00' ], // ここまで↑ // Monolog settings 'logger' => [ 'name' => 'appname', 'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log', 'level' => \Monolog\Logger::DEBUG, ],
- src/dependencies.phpファイルにサービスファクトリ情報を追記する
<?php // DIC configuration $container = $app->getContainer(); ... 中略 // ここから↓ $container['db'] = function($container) { $capsule = new \Illuminate\Database\Capsule\Manager; $capsule->addConnection($container['settings']['db']); $capsule->setAsGlobal(); $capsule->bootEloquent(); return $capsule; }; //コントローラから、$this->container['logger']->debug()といった形で呼び出すため、インスタンス情報を取得する $container->get('logger'); $container->get('db'); // twigモジュールを入れている場合 $container->get('view'); // ここまで↑
DBテーブルとモデルクラスの作成
DBテーブルを作成し、Eloquentの機能を利用するための、モデルクラスを作成します。
今回は、「users」テーブルと対応するモデルクラス「User」を作成しました。
- userテーブル
create table users ( id int(5) auto_increment primary key, token varchar(100) not null, hash_value varchar(100) not null, created_at datetime, updated_at datetime )
- Userクラス src/models/user.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $table = 'users'; protected $dates = ['created_at', 'updated_at']; public function getAttribute($key) { return parent::getAttribute(\snake_case($key)); } public function setAttribute($key, $value){ return parent::setAttribute(\snake_case($key), $value); } }
モデルの作成については下記の記事を参考にしました。 ピンポイントで求めていた情報でした!ありがとうございました!
コントローラ作成
リクエストを処理するため、コントローラを作成します。
配置するディレクトリはどこでも良いのですが、僕はsrc/app/controllersディレクトリを作成し、 hoge_controller.phpファイルを作成しました。
- src/app/controllers/hoge_controller.php
<?php namespace App\Controllers; use Psr\Container\ContainerInterface; use Illuminate\Database\Eloquent\ModelNotFoundException; require __DIR__ . "/../models/user.php"; class HogeController { protected $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function index($request, $response, $args) { $users = User::all(); return $this->container['view']->render($response, 'index.twig', ['users' => $users]); } }
Controllerクラスを作成し、コンストラクタでコンテナ(dependencies.phpで設定したオブジェクト)を 注入(依存性注入:DI?)している感じがしますが、詳細については調べきれていません。
ルーティングの設定がまだなのですが・・・indexにアクセスした場合 ユーザの変数をindex.twigに渡すようにしています。
僕の場合、コントローラでUserモデルクラスをわざわざ読み込んでいるのですが、 おそらく、下記の記事のようにindex.phpで読み込む形式が良いのだと思います。(ブログ投稿にあたって、改めて調べていた時にこの記述を見つけました。Eloquent\Modelを継承したモデルクラスにも適用できるかどうかは試せていません。。) qiita.com
ルーティングの設定
Slim Skeletonのプロジェクトテンプレートを作成した時から存在するsrc/app/routes.phpに設定を追記し、 /indexでアクセスした時に、HogeControllerのindexメソッドが呼ばれるようにマッピングを行います
<?php use \App\Controllers\HogeController; $app->get('/index', HogeController::class . ':index');
HogeController::class . ":index"という書き方で、 .(ドット)と,(カンマ)を間違えているのではないかと疑問に思ったのですが、 こちらは.(ドット)であっています。
これで、/indexでアクセスした時、HogeControllerのindexメソッドが呼ばれるようになります。
ビューの設定
今度は、src配下ではなく、appname配下のtemplatesフォルダにtwigファイルを作成します (今まではすべてsrc配下のファイルを更新していました)
- appname/templates/index.twigファイルを作成する
<!DOCTYPE html> <html lang="ja"> <head> <title>index</title> </head> <body> {% for user in users %} user.id - user.token {% endfor %} </body> </html>
上記の例では、/indexにアクセスすると、userのid列、token列が表示されます twigで利用できるタグやフィルタについては公式ドキュメントをご参照ください。
Documentation - Twig - The flexible, fast, and secure PHP template engine
静的ファイルの配置
JavaScriptやCSSといった静的ファイルは、src/public配下に配置すると良さそうです。
ちなみに僕は、開発時にはapache2を利用していたのですが、 下記のような構成にしていました。
Apache2の設定 /etc/apache2/sites-enabled/000-default.conf
<VirtualHost *:80> DocumentRoot "/var/www" ServerName app.dev ServerAlias app.dev <Directory "/var/www"> AllowOverride All Require all granted </Directory> </VirtualHost>
したがって、開発サーバーでWebアプリに接続する際には下記のような形となっていました。
http://localhost:8000/appname/public/index
(ポートフォワーディングで、ゲスト80番をホストの8000番に設定していることが前提となります)
最後に、その他感想
普段利用しないスキルセットで対応する機会がもらえたり、 TwitterのOAuth等、そもそも知見のないことに挑戦できて、すごく充実した日々でした。