tohokuaikiのチラシの裏

技術的ネタとか。

「Laravelリファレンス」 2-2 はじめてのアプリケーション

はじめてのルート定義

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対策

ここではそういうものがあるということをのべているだけだった。

「Laravelリファレンス」 2-1 はじめてのLaravel

この本の2章に沿った勉強。基本的にこの本は手取り足取りの入門書では無くて、WEBの情報を本としてまとめてくれたくらいの感じで取っ付くと良いかも。まぁ、「Laravelリファレンス」だもんね。

Laravel リファレンス[Ver.5.1 LTS 対応] Web職人好みの新世代PHPフレームワーク

Laravel リファレンス[Ver.5.1 LTS 対応] Web職人好みの新世代PHPフレームワーク

Composerや開発環境は一通りそろえた。VagrantでDebian8を作成した。

Laravelのインストール

とりあえず、

$ composer global require "laravel/installer:~1.1"

書籍には、installer=~1.1って書いてあったのだけど、調べても=~っていうシンタックスは無いし、:でも大丈夫だったので=は:のシノニムかな?

特に何もエラーっぽいのでなかったので大丈夫かな?

が、Laravelコマンドが使えない

そもそも環境変数の$COMPOSER_HOMEが無い。無いので、$composer global info とかしても何も情報で無いっぽい。仕方ないので、.bashrcに

export COMPOSER_HOME=~/.composer

を記述。

ていうか、そもそもこのディレクトリなかったし。

改めて先ほどのコマンドを実行

すると最初の一行に

Changed current directory to /home/vagrant/.composer

あれっ?ということは、・・・と思って先ほどの実行結果を見てみると

Changed current directory to /home/vagrant/.config/composer

って出ていた。

なるほど、$COMPOSER_HOMEを指定してないとこのディレクトリが仮のCOMPOSER_HOMEとして使われるのか。多分、HOMESTEAD使ってる前提なのかすっ飛ばしてるけど、この環境変数設定を書いておいてくれると嬉しい。

ということで、laravel installerの再インストール完了。

.bashrcに

export COMPOSER_HOME=~/.composer
export PATH=$PATH:~/bin:$COMPOSER_HOME/vendor/bin

と追加。source .bashrcでlaravelコマンドが使えるようになった。

vagrant@debian:~$ laravel new laravel/20161214

としてLaravelプロジェクトを作成。laravel/20161214/vendorにものすごい勢いでライブラリが蓄積されていく

vagrant@debian:~/laravel/20161214$ composer info laravel/framework
name     : laravel/framework
descrip. : The Laravel Framework.
keywords : framework, laravel
versions : * v5.3.26

最新安定板の5.3.26がインストールされた。

日本語パッケージの追加

vagrant@debian:~/laravel/20161214$ composer require laravel-ja/comja5:~1

問題無くインストール完了。

アプリケーションの設定

本書では「Laravelは規約よりも設定を重視」とあるけど、これは最近のFrameworkなら「設定より規約」なんじゃないか?文章の意味も通らなくなってるし。

設定

メモ * envファイルにデフォルトDBの設定値を記述 * php artisan clear-comliedでコンパイル済みコアClassファイルの削除 php artisan optimizeコマンドだと.envのAPP_DEBUG=trueの場合にコンパイル済みコアClass削除する。

Whoopsの利用

エラー表示にWhoopsを使う場合。

本書では、$composer require しているけど、折角なのでcomposer.jsonに書いてcomposer updateする。

    "require-dev": {
        ...
        "filp/whoops": "~2.1"

まぁ同じことですが。ただ、whoopsは開発環境だけで良いはずなので、

$ composer require filp/whoops:~2.1 --dev

とした方がいいかも。

app/Exceptions/Handler.php のサンプルコードに無かったので写経。TYPOが心配。

<?php
    public function render($request, Exception $exception)
    {
        if (config('app.debug')){
            $whoops = new \Whoops\Run;
            $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler());
            return new \Illuminate\Http\Response(
                $whoops->handleException($exception),
                $exception->getStatusCode(),
                $exception->getHeaders()
                );
        }
        return parent::render($request, $exception);
    }

artisanコマンド

php artisanで実行できるコマンド。コマンド無しで実行するとコマンドリストが出る。

artisan serve

Railsのビルトインと同じようにアクセス可能ホストの制限が掛かっているので下記のようにして起動すると良い。

$ php artisan serve --host 0.0.0.0

Laravelのartisanでサーバを立ち上げた時にプライベートIPでアクセスできない問題と、その対処法 - Front-end beginner's luck

今更ですが、Composer.pharがinstallとupdateでcomposer.json/.lockを見て何をしているかを図解

基本的に、下記の事項を覚えておけば挙動は理解しやすい。

  • installは.lockを見てその通りに実行する
  • updateは、.jsonを見て依存性を解決する

composer.phar install を実行した場合にcomposer.lockの有無による挙動の違い

f:id:tohokuaiki:20161213171954p:plain

.lockがあれば待ち時間は少なくて済む。

composer.json を更新した際に、installとupdateでは何が違うのか?

ライブラリを追加したい場合にcomposer.jsonに記載したあと、それぞれのコマンドを打った場合。

install 実行

installは.lockファイルを見るだけなのでライブラリの追加は行われない。

f:id:tohokuaiki:20161213172108p:plain

update 実行

updateは.jsonを見て依存性を解決する。そのため追記したライブラリの依存性を解決しダウンロードが行われる。

f:id:tohokuaiki:20161213172150p:plain

LinuxのディストリビューションとデフォルトパッケージのPHPバージョンの一覧

ディストリビューション バージョン サポート終了 PHPのバージョン
CentOS 5 2017/3/31 5.1
CentOS 6 2020/11/30 5.3
CentOS 7 2024/6/30 5.4
Debian 7 2018/5/31 5.4
Debian 8 2020/4/30 ? 5.6
Ubuntu(LTS) 14 2019/4/30 5.5
Ubuntu(LTS) 16 2021/4/30 7

Laravel が必要とするPHPのバージョンの一覧

Release Notes - Laravel - The PHP Framework For Web Artisansから作成

Laravel PHPのバージョン
4.2 5.4
5.0 5.4
5.1 5.5.9
5.2 5.5.9
5.3 5.5.9

んー、でも現時点で https://laravel.com/docs/5.3#server-requirements を見ると 5.6.4が必要らしい。

開発環境でのConfluenceがクソ重いのでメモリを増やしてみた

Atlassian SDK使っているノートパソコンが古くなってきたのかな?と思ったくらいだったのでとりあえずメモリ増やす。

現状のメモリ使用量

管理画面の「システム情報」を見てみる。空きが14%しかない。

f:id:tohokuaiki:20161209123936p:plain

ということで、変更。

pom.xmlにて

    <build>
        <plugins>
            <plugin>
                <configuration>
                    <jvmArgs>-Xms512m -Xmx1g -XX:MaxPermSize=512m</jvmArgs>

これで、1Gのメモリを使ってくれるはず。

システムが8Gしかないのでちょっと心配なんだけど。。。 f:id:tohokuaiki:20161209124206p:plain

再起動してみる

f:id:tohokuaiki:20161209124359p:plain

これで少しでも快適になるかな・・・。

Windowsの方がやや厳しいかな・・・。f:id:tohokuaiki:20161209124914p:plain