はじめてのルート定義
app/Http/routes.phpに下記のコードを加える。
<?php
Route::get('who', function(){
return 'Hello'.Request::input('name', 'World').' ,sir.';
});
いきなり動かない。
気を取り直して、サンプルプログラムをダウンロードして再度実行
$ git clone https://github.com/laravel-jp-reference/chapter2.git sample2
$ cd sample2/
$ composer install (書籍ではcomposer updateと書いてあったがupdateだとエラーが出て動かなかった)
$ mv .env.example .env
$ php artisan key:generate
$ php artisan serve --host 0.0.0.0
ここまでやって、app/Http/routes.php に上記のコードを書き足すと確かに上手くいった。
ということは、ルーティングに関してapp/Http/routes.phpに書き足すだけでは上手く動かないのである。困った...
ルートの確認
サンプル2では、$php artisan route:list するとずらずらっとルーティングの設定一覧が出てくる。
ところが自前Laravelは出てこない。先ほどのClosureで設定したものが出てこない。
検索すると、5.3でルーティングを設定するファイルが変わったらしいということである。5.1から5.3でこのレベルの変更かよ・・・激しいな。
[Laravel]routes.phpはどこに消えたのか(5.3)
ということで、routes/web.php に app/Http/routes.php に加えたコードを書くことで無事対応完了
$ php artisan route:list
+--------+----------+----------+------+---------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+----------+------+---------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | who | | Closure | web |
+--------+----------+----------+------+---------+--------------+
Closureによるルーティング
<?php
Route::get('who', function(){
return 'Hello'.Request::input('name', 'World').' ,sir.';
});
Route::get('/who/{name}', function(){
return 'Hello'.Request::input('name', 'World').' ,sir.';
});
- whoは/whoでも同じ。
- QueryStringsで取りたい場合は、Closure内でRequest::inputで取れる。
- Pathで取りたい場合は、pathを{xxx}のブレースで囲み、それをClosureの引数にする。
この辺りはJavaのアノテーションっぽい感じ。
ちなみに、このRoute::getとかの頭の悪そうなStaticメソッドは名前空間のお蔭で頭悪いような実装はされておらず、Facadeにより決められたInstanceがコールされている。分かりやすいけどコスト高くないですか?・・・と思ったらちゃんとObjectキャッシュを使っていた。
vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php
に存在するクラスである(多分)。
というか、単にこのweb.phpは app/Providers/RouteServiceProvider.php からrequireで単純読み込みをしているだけなのでこのクラスでuseしている名前空間のClassが使えるようになっている。そのため、上記のRoute.phpがいきなりRoute::でコールできている。
staticコールできるのは、
vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
の__callStaticによるオーバーロードであり、実際には、vendor/laravel/framework/src/Illuminate/Routing/Router.phpのgetメソッドをコールしている。
引数の「...」って可変引数なのね。関数側で受ける時は、以前のfunc_get_args()を使ってるのと同じ。関数をコールする時は call_user_func_array()でコールしているのと同じ。
はじめてのビュー
ビューはPHPファイルを直接。最近のFrameworkはSmartyなんて使わない。
ベタでグローバルに vendor/laravel/framework/src/Illuminate/Foundation/helpers.php で定義されているヘルパ関数view()を使ってViewインスタンスを返すのである。
RoutesやControllerでViewインスタンスを返さなかった場合、例えば文字列を返すとその文字列が表示される。
Controller
app/Http/Controllers/HomeController.php
<?php
class HomeController extends Controller
{
public function index()
{
return view('front');
}
routes/web.php
Routerで直接view()を使ってViewインスタンスを返す。
<?php
Route::get('/', function () {
return view('welcome');
});
レスポンスコード
Viewインスタンスを返す際に、responseヘルパでレスポンスコードを付与することができる。
<?php
response(view('welcome2'), 404);
こんな感じ。
Bladeテンプレート
Laravel独自の軽量テンプレート。またテンプレート憶えないといけないのか・・・。Smartyにしてくれや・・・。
レイアウトをbladeから指定する。
@extends('layouts.master')
@section('content')
Home
@endsection
とすると、resources/view/layouts/master.blade.php にて@section('content')で囲んだ部分が
<html>
<body>
<div class="container">
@yield('content')
</div>
</body>
</html>
というように得ることが出来る。
Controllerで設定する。
bladeでのレイアウト設定しかなかったので「レイアウトを決定するのって、ここに書くのか。。。Controllerごとに設定してくれた方が良いような気もするのだけど・・・」と思ったら当然ありますよね。
Laraval4.2 テンプレート:コントローラーでレイアウトを宣言する
....と思ったけど、Laravel5にはそれっぽいのが見当たらない。4.2の方法でやっても通用しない。どういうことだ?無くなったのか?
Laravel5でControllerからのLayout設定は無くなったっぽい。
Laravel 5: $this->layout->content not working?
ここのスレッドで、「何でLayout機能を無くしたんだろう?」「ControllerにViewのロジックが入っているのはおかしいからさ」っていう流れなんだけど、これはかなりショック・・・。
まぁでも後でapp/Http/Controllers/Controller.phpのcallActionメソッドをごにょっといじれば何とかなるのかなぁ。というのが↑のスレッド読んだ感じ。
Bladeテンプレートについて
要点だけ
* 構造式は @ で始める。
* <?php .... ?>でPHPがそのまま使える。
* {{$foo}}で変数$fooをエスケープして出力
* {!! $foo !!} で変数$fooをそのまま出力
* Smartyの{foreach} ... {foreachelse} ... {/foreach} に相当するのは、@forelse ... @empty ... @endforelse になる。
はじめてのORM
サンプルコードで、DBを作成する。
$ touch storage/database.sqlite
$ php artisan migrate
[PDOException]
could not find driver
んー、・・・と思ってphpinfo()を見ると、
| PDO support | enabled |
| PDO drivers | mysql |
ってなってた。
あれー、PHP5からSQLiteは標準で・・・と思ってたら、SQLite サポートの変更によると
以前のバージョンの PHP で動作している SQLite ベースのプロジェクトがあるのなら、 ext/sqlite を使用し続ければ何の問題もありません。しかし、PDO および sqlite を明示的に有効にする必要があります。新しいプロジェクトの場合は、PDO および 'sqlite' (バージョン 3) ドライバを使用すべきです。 これは SQLite 2 に比べて高速で、ロック処理が改善されており、 プリペアドステートメントやバイナリ列をネイティブにサポートしています。
SQLite3は違うようだ。
$ sudo apt-get install -y php5-sqlite
これで、$ php artisan serve --host 0.0.0.0 をし直すと確かにphpinfo()にpdo_sqliteができていた。改めてmigrateコマンドを実行。
$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
- Routeで「Illuminate\Database\Eloquent\Model」を継承したClassを返すとそのJSONを表示させるっぽい。
- all: 全件取得 find:IDでfind findOrFail:失敗時に例外ModelNotFoundExceptionを飛ばす
- Modelオブジェクトのsetter/getterにより、$mode->attrで取得できる。
はじめてのController
ルーティングでControllerを指定した場合
route.php / web.php で
<?php
Route::controller('user', 'UserController');
とすると、/user/以下のルーティングをこのControllerで担当することになる。
実行されるメソッドは、/user/foo にGETでアクセスされたら getFooメソッド。各メソッドで、view(テンプレート名)をreturnする。
暗黙のコントローラ参照については、HTTPコントローラー 5.1 Laravelを参考に。
であるが・・・5.3ではControllerルーティングが無くなった模様
なんとなく、HTTPメソッドをControllerメソッドにするのがダサいというかもにょりを感じていた。というのは、このControllerルーティングだと意味が被るからである。HTTPメソッドとControllerのメソッドを対応させようということらしい。
コントローラ 5.3 Laravelより
HTTPメソッド |
URI |
アクション |
ルート名 |
GET |
/photos |
index |
photos.index |
GET |
/photos/create |
create |
photos.create |
POST |
/photos |
store |
photos.store |
GET |
/photos/{photo} |
show |
photos.show |
GET |
/photos/{photo}/edit |
edit |
photos.edit |
PUT/PATCH |
/photos/{photo} |
update |
photos.update |
DELETE |
/photos/{photo} |
destroy |
photos.destroy |
当然ブラウザはPUT/DELETEメソッドが無いので、擬似的に作る。
Controller内で共通の処理をさせる場合は、ミドルウェアと呼ばれる機構を使うと便利。
ところで、サンプルでは
<?php
class UserController extends Controller {
public function __construct() {
$this->middleware('auth.first');
とミドルウェアを呼んでいる。
このauth.firstは、app/Http/Kernelで
<?php
class Kernel extends HttpKernel
{
protected $routeMiddleware = [
'auth.first' => \App\Http\Middleware\FirstUser::class,
というようにrouteMiddlewareで設定することで使えるようになる。このauth.firstはルーティングの際に使えるMiddlewareということらしい。
ミドルウェアのFirstUserクラスは、インスタンスを作成され、handleメソッドが自動実行される。
class FirstUser
{
public function handle($request, Closure $next)
{
if (Auth::guest()) {
....(処理)
}
return $next($request);
}
Authクラスについて
Authは、Facadeで実行される静的呼び出しっぽい処理。
実際にどのインスタンスが作られるかというのは、LaravelのFacadeがどうやってメソッドを決定しているかコード読んだのでメモ - tohokuaikiのチラシの裏に書いた。
また、このAuthがどのようにFacadeのAuthとしてautoloadされるかは、LaravelのClassのオートロードを読んでみて、Facadeがどうやって短い名前でCallされているかを考える - tohokuaikiのチラシの裏に書いた。
Viewへの遷移
- view(テンプレート名)->with('foo', $foo)で変数渡しを行う。
これは、view(テンプレート名)->withFoo($foo)と等価。
- redirect()->back()でRedirectレスポンスで、Refererを参照にした1つ前のLocationを作っている。
- redirect(action('UserController@getIndex'))->with('status', 'success') とかでなんで変数を引き回せるか・・・・は、RedirectResponse::withによると、session->flash()を使っている・・・これはおそらく1回で消えるセッション変数のStoreに使うメソッド。最初session->flushはクリアする方でこれと間違えた。
はじめてのフォーム
フレームワークにおけるフォームで大切なのはひとえにバリデーションとフォーム値の引継ぎだと思っている。
- 入力する際に、フォームのデフォルト値を如何にスムーズに設定するか
- エラーが起こった際に、当該エラーをどう表示するか?当該エラーを引き起こしたフォームにどう対応させるエラー表示を行えるか
- 各フォーム値のバリデーションをいかに簡単に行えるか
- CSRF対策
ここではそういうものがあるということをのべているだけだった。