この記事について
Laravelについて社内で共有するためのものです。デフォルトのディレクトリ構成やどんな機能があるかを説明しています。
一応LTSのLaravel5.5準拠で書いてますが、自分の理解をベースに書いてるので何か間違ってるかもしれないです。見逃すかやんわり教えてください。
Lumenも同じようなものですが、違いについても少し言及できればいいなーとは思ってます。
デフォルトのディレクトリ構成
デフォルトで大体こんな感じです。
├── app
│ ├── Console
│ ├── Exceptions
│ ├── Http
│ │ ├── Controllers
│ │ └── Middleware
│ ├── Models
│ └── Providers
├── bootstrap
│ └── cache
├── config
├── database
│ ├── factories
│ ├── migrations
│ └── seeds
├── public
│ ├── css
│ └── js
├── resources
│ ├── assets
│ │ ├── js
│ │ └── sass
│ ├── lang
│ │ └── en
│ └── views
├── routes
├── storage
│ ├── app
│ │ ├── protos
│ │ └── public
│ ├── framework
│ │ ├── cache
│ │ ├── sessions
│ │ ├── testing
│ │ └── views
│ └── logs
├── tests
│ ├── Feature
│ └── Unit
├── vendor
└─ .env
大事なところから解説します。名前から自明なところは省きます。
.env
ディレクトリ構成と言いつつ早速ファイルです。
Laravel5のconfigurationは環境変数を利用してます。この .env
ファイルでは環境変数を定義します。以下のような感じです。
APP_NAME=Laravel
APP_ENV=local
この値が環境変数としてセットされ、フレームワークから読まれます。フレームワークからは.env
ファイルのみ読み取るので
- .env.local
- .env.staging
- .env.production
などのように環境ごとに用意して、デプロイのタイミングで .env
ファイルを置き換えるのが一般的じゃないかなと思います。
database
factories
ほぼテストのためのものだと思っています。テストで使いたいDBのレコードを生成するとき何かに使えます。
migrations
DB Schemaを管理します。変更内容を1つずつ残してどこまで実施したかなど管理してます。設計としてはFlywayに近いんじゃないかと思います。以下のような感じです。
class CreateFlightsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('flights', function (Blueprint $table) {
$table->increments('id'); // auto_incrementがつくので自動的にPKとして設定される
$table->string('name'); // varchar. デフォルトのlengthは255
$table->string('airline');
$table->timestamps(); // created_at, updated_atというdatetimeカラムを作成 timestampだったっけな…?
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('flights');
}
}
upはmigration時、downはmigrationのrollback時に呼ばれます。ここでのrollbackっていうのは、migrationの状態を1つ前の状態に戻すことを言います。クエリのTransactionとは関係ありません。
seeds
ある決まったデータセットを生成するための機能です。例えば、DBのマスタデータ的なデータを一括で生成したりとか、そういうのに使えます。
routes
以下のような感じでWebなどのルーティング定義を書きます。
middleware
については後述します。
Route::middleware(['AuthenticationMiddleware'])->group(function () {
Route::get('/', function () {
// do something
});
Route::get('user/profile', 'UserController@profile');
});
get, post, put, delete (patchもあった気がする) でHTTPメソッドを定義し、記述したURLパターンに沿ってクロージャで処理を書いたり、処理を行うController@メソッド を記述します。
Route::resources
という指定方法もあり、RESTに対応した一覧取得、単体取得、作成画面、作成処理、更新、削除 のRoutesを定義したのと同様の動きをします。その時にハンドリングされるメソッド名がそれぞれ
index
, show
, create
, store
, update
, destroy
なので、僕が手動でControllerを作るときにもこのメソッド名を使うようにしています。
app
アプリケーションのコードを書くエリアです
Console
バッチ処理とかCUI処理を書くところです。
Http
Web系の処理を置くところです。CUI側と処理を共通化させるため、ビジネスロジックなどはここに置かない方が良いでしょう。
Http/Controllers
いわゆるControllerです。
Http/Middleware
Controllerの前に行いたい処理を書きます。例えばAuthenticationMiddlewareというのを作ったとします。
class AuthenticationMiddleware extends
{
public function handle($request, Closure $next, $guard = null)
{
// check authentication
if (authenticated()) {
return $next($request);
}
return redirect('/login');
}
}
のようなイメージです。 ログイン済みかどうかを判定して、ログイン済みであれば次のミドルウェアに処理を流します。 次のミドルウェアがなければControllerに処理が移ります。 ログイン済みでなければログイン画面にリダイレクトしてるイメージです。
ミドルウェアとして処理を抽出できるので、例えばログインしているかどうかなどの判定をControllerの責務から外すことができ、共通化できます。
次のミドルウェア
と書いたように、ミドルウェアは任意の順番で複数置くことができます。例えばAPIアクセス(i.e. Content-Type: application/json
)かどうか、など。
Http/Requests
Httpのフォームリクエスト処理のハンドリングをするレイヤーです。デフォルトでは存在しませんが、コマンドから自動生成することができます。以下のようなものが生成されます。
class TestRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}
通常、Controllerのroutingに対応したメソッドには\Illuminate\Http\Requestを渡して(F/Wがメソッドインジェクションしてくれる)httpのリクエスト値を取得したりします。 フォームリクエストの場合、フォームに対応したRequestをクラスを作り、そのクラスをメソッドの引数に指定すると、自動的に記述したルールに沿ってバリデーションして通ったものだけControllerに処理が渡ります。つまり、Controllerからフォームのバリデーション責務を外すことができます。
Lumenにはこの機能は無い(はず)ので、通常Controllerでvalidationを行います。 こういう便利機能はパフォーマンスとトレードオフになる部分もあるので、Lumenにはこういう便利機能が存在しません。
Models
ActiveRecordのモデル層のようなものです。こんな感じでusersテーブルにアクセスできます。
$user = User::find(1);
$users = User::limit(10)->get();
$users
はCollectionとして返ってきます。Collectionは様々なarray関数のラッパーのようなものです。
PHPはよくarray_filterとかarray_mapの引数の順番が違って気持ち悪いとか言われますが、C言語に合わせているからそうなっている、そうです。いい感じに書きたいならフレームワークでラップしてくれ、という思想なので、このCollectionなんかはそれを体現しています。こんな感じになります。filter, mapなどもCollecitonを返すのでメソッドチェーンできます。
// collectionsのfilter, map
$filtered = $users->filter(function (User $user) {
return !$user->isSomething();
});
$userNamesWithSuffix = $users->map(function (User $user) {
return $user->name . ' さん';
});
LaravelではデフォルトではEloquentというORMを使っています。EloquentはQueryBuilderという層をラップしています。 Eloquentを使うと遅くなったり、論理削除とか余計なものもついてくるので使わないで、QueryBuilderを直接使う派もあります。
relationが便利なので僕は使うことが多いです。
Lumenだと明示的に有効にしないと使えなかったはずですが、有効にするとAPIが10ms遅くなります。
Providers
LaravelがLaravelとして動くために最も重要ですが最も分かりづらい層です。ただフレームワークを使いたいだけならばそんなに意識しなくても良いです。
例えばLaravelだとこんな風に書くと
class FooController extends Controller
{
public function index($request)
{
$value = Cache::get('key');
}
}
Cacheストレージからkey
に対応した値を取得できます。
CacheストレージがRedisだろうとMemcacheだろうとDBだろうとこれで取ってこれます。設定ファイルでどのキャッシュエンジンにするか指定しておくだけでいい感じにしてくれます。
ものによって違いはありますが、このCache::
で呼び出される「設定ファイルなどの何かしらで指定された内容をもとにクラスインスタンスを作る」ような層が、このProviderです。フレームワーク上ではService Providerと呼ばれています。
なお、Cache::get
のように一見staticメソッドで呼ばれてるけど裏側で作られているクラスインスタンスのメソッドをコールするような処理をFacadeと呼びます。GoFのデザパタのFacadeとは感覚が違うので注意が必要です。
このFacadeはstaticメソッドのような感覚で呼べるので、Cache、Session、その他いろいろ好きな場所で好きなように呼べてしまうので綺麗に設計しようとすると邪魔になります。うかつにFacadeを使うと怒る人(Facade警察)もいます。
Facadeはユニットテスト時にモックに差し替えることもできるため、staticメソッドのような見た目なのにモックを使ったテストができます。 ちなみにFacadeもLumenではオプションです。有効にすると10ms以上遅くなります。
設計としてはFlyweightとProxyを合わせたようなものです。
生成したインスタンスはApplicationというクラスで管理されますが、ApplicationはContainerを継承しているのでLaravelは基本的にContainerだと言えそうです?
bootstrap
フレームワークを開始するためのスクリプトが入ります。Laravelでは触る必要はそんなに無いはずです。
Lumenではbootstrap/appで使う機能とか使わない機能とかいろいろ設定します。Lumenではパフォーマンスのためにファイルを分散させないようにしてあるので、ファイルを分割するのは良くないでしょう。
とまぁ、デフォルトでこういうものが用意されていますが、使うのも使わないのも自由です。
フレームワークのブレーキングチェンジへの対応が難しくなるのでapp以下を使わない人もいます。
以上!何かあれば追加します。