カテゴリーアーカイブ Laravel

著者:杉浦

【Laravel】Laravel6.0がリリース

 Laravelのメジャーバージョンアップである6.0が正式リリースされました。
Laravel 6 Is Now Released – Laravel News
Installation – Laravel – The PHP Framework For Web Artisans
 Laravel6.0は運用者に優しい作りになっています。まずLTSであり、2022/9/3までセキュリティ面のサポートが得られます。また、セマンティック バージョニングに則ります。セマンティック バージョニングのルールが守られる限り、バージョン6.0の機能はバージョン6.1や6.2で損なわれません。6.*,*の最新になる様アップデートを続けるだけで機能を損なわずサポートを得られ長期間の運用に耐えます。
セマンティック バージョニング 2.0.0 | Semantic Versioning
 既存プロジェクトの更新のためにはUpgrade Guide – Laravel – The PHP Framework For Web Artisansを、新機能のざっくばらんとした把握のためにはLaravel 6 Is Now Released – Laravel Newsを読むと楽です。
 フロントエンド側のフレームワークとしてVue.jsが備え付けられていましたが、6.0からはVue.jsとReactの選択式になりました。業界のシェア的にはReactの方が多いので今後はLaravelとReactのセットを使うライブラリやプロトタイプも増えそうです。
JavaScript & CSS Scaffolding – Laravel – The PHP Framework For Web Artisans

  • この記事いいね! (2)
著者:杉浦

【Laravel】バージョンごとの変更点をリファレンスリポジトリ中のファイル差分から見つける

 Laravelは週単位でとても細かくリリースされていますが、それとは別に5.7, 5.8, 6.0などと中くらいの単位、大きな単位でも更新されています。そういった大きな更新の際には、それまでにあった小さな多数の更新の内の注目すべき変更点をリファレンスに載せます。LaravelはリファレンスもGitHubで管理されており、これを扱うことでバージョンごとの違いを容易に比べることができます。
 laravel/docs
 特に有効なのはバージョンを示すブランチ間の比較でしょう。例えば、最新の5.8と9/2にリリースの決まった6.0のブランチの差分が次のリンクから読めます。プルダウンで任意のブランチを選ぶこともできます。
 Comparing 5.8…6.0 · laravel/docs
 これのFiles Changedから見ていって、例えば次のようにあります。

 6.0ではLaravelデフォルトの認証機能プロトタイプをプロジェクト内に設置するコマンドはphp artisan make:authからphp artisan ui vue –authに代わり、composerパッケージのlaravel/uiを入れておく必要もあるとわかります。同様に順々にファイル差分を読んでいくことで大きな変更点を見つけられます。
 ちなみにlaravel/uiはVue.jsで作るLaravelデフォルトの画面を扱うためのリポジトリの様です。
laravel/ui

  • この記事いいね! (1)
著者:杉浦

【Laravel】リポジトリlaravel/laravelとlaravel/frameworkの違い

 Laravelのソースコードリポジトリには全く同じ対象について記述されたリポジトリであるlaravel/laravelとlaravel/frameworkの二つがあります。

 laravel/laravel: A PHP framework for web artisans
 laravel/framework
この二つには安定した製品版と開発版の様なものです。もちろんlaravel/frameworkにもmasterブランチがあり、定期的にリリースをしていますがlaravel/laravelはそれ以上に安定です。この二つの最も違う点はcomposerでの呼び出し方でしょう。laravel/laravelは次の様に呼び出され、Laravelプロジェクトの初期状態を作ります。

composer create-project laravel/laravel

 laravel/frameworkは次のように呼び出され、vendor以下にLaravelフレームワークのコードを配置するのみです。

composer require laravel/framework

 GitHubから確認できるリリースの頻度もまるで違います。laravel/laravelが下の様に一気にバージョンアップしてるのに対し、

laravel/frameworkは週一で定期的にアップデートしています。

 最新の機能や残存するバグなどを見るにはlaravel/frameworkを追うのがやりやすいです。

  • この記事いいね! (1)
asaba 著者:asaba

ルーティング処理について

最近はバックエンド側も触れるようになりたいと思い、少ない時間を見つけて

自宅でlaravelをいじっております。

mvcの概念を頭に入れこむのに少し時間がかかりましたがおおよその流れだけは理解

できたのかなと感じています。とはいってもまだ覚える必要のあるものもたくさん

あるので忘れないように今回みたいにちょくちょく書いていきます。(でないと忘れるので)

 

という訳でルーティング処理の概念について少しおさらいします。

ルーティングというのは、各ページにて処理をあらかじめ決めておき、どのアドレスでどのような処理を

返すかを選定する決め事のようなものです。

書く場所としてはroutes/web.phpの中が適正です。つまり、このページを見ることで

処理をするページのアドレスと処理の内容(返す値)を一括で知ることが出来るので

いちいちページに行って確認する必要がなくなります。

ただ単純に文字列を返すだけだとこんな感じになります。


Route::get(<span class="hljs-string">'posts/{id}'</span>,

<span class="hljs-function"><span class="hljs-keyword">  function</span> <span class="hljs-params">($id)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'あなたのidは</span><span class="hljs-string">'</span>$id.<span class="hljs-string">'番です'</span>; }

);

一つ目の引数にはアドレスの隣にページに渡す値を持たせています。保持することによって

このアドレスのページで受け取って使うことができます。

二つ目の引数は受け取った値で処理をした結果を持たせています。

ざっくり答えるとposts/{id}ページはidを教えてくれるページとなっており、受け取った

値によって結果を変える動的なファイルとなっています。

 

これだけでは実用性があるとまではいえないので、viewを返すルーティング処理を

書いていきます。

なんにもない普通のviewを返すコードです。


<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Laravel</title>
</head>
<body>
<form>
<h1>フォーム</h1>
<div>
<input type="text" placeholder="asaba"/>
</div>
<div>
<textarea placeholder="お問い合わせ"></textarea>
</div>
</form>
</body>
</html>

ディレクトリは「resources/views/sample.blade.php」です。

(ここではBladeというテンプレートエンジンを使っていますが自分でも

まだわかっていない部分があるので割愛させていただきます。)

 

お次はまたルーティングを書いていきます。


Route::get('/sample', function() {
return view('sample');
});

第一引数でパスを決めるとこまでは同じですが、returnの中ではviewそのもの

を返しているのが分かります。

 

これでhtmlひとかたまりを表示することはできました。後はこれとmvcを組み合わせてきれいな

コードを書きあげれば一歩踏み出したことになります。

今回はページをルーティングを使って値を返すだけの内容でしたが、いずれはmodelで

DBを操作したりcontorollerで非同期処理をしてみたりということも書けたらなと思っています。

 

  • この記事いいね! (0)
著者:杉浦

【Laravel】Laravel 6.0がリリース間近

 Laravel6.0はLaravelのメジャーアップデートであり新たなLTSバージョンです。リリースは8月中だとかなんだとか。


 メジャーアップデートですが予想されているアップデート所要時間は1時間程です(参考として5.5から5.6は10~30分、5.6から5.7が10~15分、5.7から5.8が1時間)。変更点は配列、文字列のヘルパ関数の置き換えやEloquent(laravelのデータベースと密結合したベースモデル)の一部と多用される部分にあるので多くのユーザが変更の影響を受けるでしょう。

 Upgrade Guide – Laravel – The PHP Framework For Web Artisans
 前回のメジャーアップデートである4.2から5.0へのアップデート方法には5.0のプロジェクトを新規作成して適宜ファイルを移動しろ、とあります。ユーザビリティの発展を感じますね。
 アップグレードガイド 5.0 Laravel

  • この記事いいね! (2)
著者:杉浦

【Laravel】データベースと無関係のEloquentっぽいモデルを作るためのFluentクラス

 LaravelはEloquentクラスにモデルをまとめています。Eloquentはデータベース中のテーブルに対応して扱われることを前提としたORMです。
Eloquent:利用の開始 5.8 Laravel
 webサービスにおいてモデルが欲しい時は大体、対応したテーブルを用意するのですが、時にはデータベースに関係なくモデルを作りたい時があります。具体的には別のサービスで吐き出されたファイルの中身を扱う時などです。そういった時に手製のクラスファイルを作成するのも一つの手ですが、何かしたいことが増える度に簡単なことでも一々コードを書く必要があります。何かしら継承なりuseしたいですがEloquentを継承するのは危険です。誤った呼び出し一つで存在しない場所を参照するSQLを投げるがPHP的には正しいコードが出来上がります。
 FluentはLaravelの\Illuminate\Support\Fluentに位置しているEloquentの様なプロパティ制御とjson、array扱いの要素(ArrayAccess, Arrayable, Jsonable, JsonSerializableのinterfaceを満たしている)を持った素朴なクラスです。Laravel内部で扱われているのが基本でドキュメントなしのクラスです(Laravel4とかの頃のモデルクラスっぽい?)。Fluentのコンストラクタは次の様になっており、適宜new Fluent(パラメータ配列)をすることでデータベースに関係ないEloquentライクなモデル実体を扱えます。

    /**
     * Create a new fluent instance.
     *
     * @param  array|object  $attributes
     * @return void
     */
    public function __construct($attributes = [])
    {
        foreach ($attributes as $key => $value) {
            $this->attributes[$key] = $value;
        }
    }

 継承して、そのクラスをnewしただけでインスタンスにさせたいこと、インスタンスから知りたいことの設定が完了します。クラスだけ作って何も中身がないのに自在に動く様はEloquentを彷彿とさせます。
 簡単にnewする対象を探索する処理を入れるとなお便利です。例えば次の様なstaticメソッドを生やすことでなお扱いやすくなります。

    /**
     * ファイル名で探す。
     * @param  string $filename
     * @return mixed
     */
    public static function find(string $filename)
    {
        $disk = Storage::disk('hoge_files');
        if(!$disk->exist($filename)){
            return null;
        }
        $meta_data = $disk->getMetadata($filename);
        $body_data = self::parse($disk->get($filename));
        
        return new self(array_merge($meta_data, $body_data);
    }

 バリデータを作ってコンストラクタ中で呼び出す様な仕組みを作るとプロパティに何があるのかきっちり決まり更に更に安全安心に使えます。

  • この記事いいね! (1)
著者:杉浦

【Laravel】Duskのテストケースを楽に作るChrome拡張Laravel TestToolsの紹介

 DuskはLaravelと密にかかわっている自動ブラウザテストツールです。実際にブラウザを開いて、あるページへ移動できるか、そのページである操作はできるか、操作の結果は想定した通りか、といったテストを自動で行ってくれます。ブラウザを用いる以上しょうがないかもしれませんが実行時間が長いのが玉に瑕です。
Laravel Dusk 5.8 Laravel
 テストコードは例えば次の様に書けます。

    public function testBasicExample()
    {
        $user = factory(User::class)->create([
            'email' => 'taylor@laravel.com',
        ]);

        $this->browse(function ($browser) use ($user) {
            $browser->visit('/login')
                    ->type('email', $user->email)
                    ->type('password', 'secret')
                    ->press('Login')
                    ->assertPathIs('/home');
        });
    }
    /**
     * <a href="https://readouble.com/laravel/5.8/ja/dusk.html">Laravel Dusk 5.8 Laravel</a>から引用
     */

 テストユーザを作成。/loginへ訪れて、name=”email”のinputタグにテストユーザのemailを入力、name=”password”のinputタグに’secret’と入力、Loginと書かれたボタンを押し、押した後のページのアドレスが/homeであることを確かめるコードです。簡単に記述するために相当暗黙知が入っています。慣れれば楽なのですが、慣れるまで具体的にどこの何が何であれば期待した通りに動作するのか、少し戸惑います。Laravel TestToolsは操作の記述を楽にしてくれます。
Laravel TestTools – Chrome ウェブストア
 具体的に何をするかというと操作の記録とコード起こしです。次のgifの様に録画ボタンを押すと動作を記録します。

 書き起こされたコードをコピペするだけで操作分の記述完成です。これでtype()やpress()を記述する際にそう悩む必要がありません。後はassertで確かめるだけです。

  • この記事いいね! (1)
著者:杉浦

【Laravel】一プロセス内限定のSQLのキャッシュを作る

 ORMは便利ですが、何度も重複するクエリを発行しがちです。複数のモデルオブジェクトeachしてリレーション先を参照する時は特に危険です。どれくらい重複してるかは
 barryvdh/laravel-debugbar: Laravel Debugbar (Integrates PHP Debug Bar)
 などで調べるのが楽です。duplicatedとuniqueに分けてくれます。

 やり方の方針はクエリが発行される度にそのクエリに対応するキャッシュはあるかチェック、あるならばキャッシュを結果として取得、なければ実際に発行して結果を取得してキャッシュに保存、です。
 一つのクエリに使う場合は次です。

        $key = __FILE__.' '.__FUNCTION__.' '.__LINE__ .' '.$id;
        app()->offsetExists($key) ?: app()->singleton($key, static function () use ($id){
            return Member::where('id',$id)->get();
        });

 コードの場所と引数に関して一意になるキーを設定。クエリの結果をシングルトンに登録することによって、同じ場所が同じ引数で繰り返し呼ばれる際、同じクエリを発行することなく、Laravel内にバインドされたシングルトンを呼ぶようになります。変数なのでプロセスが終わったら揮発します。
 作った後しばらくして気づきましたがarrayドライバ(array型の変数内にキャッシュを保存する機能)でキャッシュ使った方がシンプルですね。
 全体のクエリに使う場合はDBのselect等データ取得時のリスナーに同じ処理を入れます。updateした時のリスナーにキャッシュ消す処理忘れるとバグの元になります。

  • この記事いいね! (1)
著者:杉浦

【PHP】DI(依存性注入)を用いたコードの追い方

 DI(Dependency injection)(依存性注入)とはコード内で外部への依存を直書きするのでなく、外部から引数、セッターなどで依存する部分を与える実装のことです。例えば、次です。

use Illuminate\Contracts\Hashing\Hasher as HasherContract;

class DatabaseUserProvider implements UserProvider
{
    /**
     * The hasher implementation.
     *
     * @var \Illuminate\Contracts\Hashing\Hasher
     */
    protected $hasher;

    /**
     * Create a new database user provider.
     *
     * @param  \Illuminate\Contracts\Hashing\Hasher  $hasher
     * @return void
     */
    public function __construct(HasherContract $hasher)
    {
        $this->hasher = $hasher;
    }
    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {
        return $this->hasher->check(
            $credentials['password'], $user->getAuthPassword()
        );
    }
}

 DatabaseUserProviderクラスがインスタンス化された際にHasherContract型の引数$hasherを要求し、$hasherを保持します。保持した$hasherをvalidateCredentialsメソッド内で用います。
 外部から依存するモノを与えるため、テストし易かったり変更し易かったりするのですが、あまりに自由で今稼働しているコードでは何が実体化しているのかわかりにくく、追うのが少し難しいです。上記コードなら$this->hasherって何?となってIDEの機能で型を追ってもHasherContractというインターフェースが現れて終わりです。HasherContractを満たすどのHasherクラスが使われているか分かりません。
 hasherの中身、ひいては実際の動作を知りたい時は、まず依存性が実際に注入された際のコードを動かし、その時のhasherの中身に何が入ってるのか調べる必要があります。PHPならば次の様に出来ます。

    public function __construct(HasherContract $hasher)
    {
        var_dump(get_class($hasher));exit;
        $this->hasher = $hasher;
    }

PHP: get_class – Manual
 get_class関数でインスタンスそのものに、あなたのクラス名は何ですか、と尋ねます。得られたクラス名が今稼働しているシステムで注入されているクラスの名前です。例えば、ここでBcryptHasherという実装クラスが呼ばれているとわかったら次の様に型付けをします。

class DatabaseUserProvider implements UserProvider
{
    /**
     * The hasher implementation.
     *
     * @var \Illuminate\Hashing\BcryptHasher
     */
    protected $hasher;

    /**
     * Create a new database user provider.
     *
     * @param  \Illuminate\Hashing\BcryptHasher  $hasher
     * @return void
     */
    public function __construct(BcryptHasher $hasher)
    {
        $this->hasher = $hasher;
    }
    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {
        return $this->hasher->check(
            $credentials['password'], $user->getAuthPassword()
        );
    }
}

 こうするとIDEのジャンプ機能はインターフェースHasherContractでなくクラスBcryptHasherを参照するようになり、静的にコードを追うことが格段に楽になります。注意点として直し忘れがあります。DIを駆使したコードほど直し忘れると型エラーで派手に事故ります。

  • この記事いいね! (1)
著者:杉浦

【PHP】Adapterパターンであるカラムの値によって大きく振る舞いを変えるテーブルの扱いを多少マシにする

 ときおり扱い難い定義のデータベースを扱う必要が出てきます。特定のカラムによって値の意味や他テーブルとの関係が大きく変わるテーブルはその一つです。
 例えば、usersテーブルにtypeカラムがあり、typeによって仮会員か一般会員か特別会員か区別するとします。それぞれ似た様で確かに違うモノです。仮会員はメールアドレス等いくつかのデータがnullでも良し、の様に別のふるまいが要求され、それが増えてくると、usersテーブルを扱うのはだんだん面倒になってきます。早い時点でuserテーブルをリファクタリングできていれば問題にならないのですが、そうならないまま既存データやコードが積み重なり今更変える時間がない、ということもあります。アダプターパターンを用いることでデータベース定義を変えないままこれを緩和できます。
 アダプターパターンは増補改訂版Java言語で学ぶデザインパターン入門 | 結城 浩 |本 | 通販 | Amazonでは次のように説明されています。

「すでに提供されているもの」と「必要なもの」の間の「ずれ」を埋めるようなデザインパターン、これが
Adapterパターン
です。

 この考えに従えば「すでに提供されているもの」はtypeによってデータの意味が大きく変わるテーブル、「必要なもの」はデータの意味ごとのテーブルです。多くのフレームワークで採用されているORM(オブジェクト関係マッピング)は1テーブルに1モデルの原則で使いやすい様に作られています。これを1つの意味に対して1モデルになる様に用います。
 具体的にはマッピングの時点でwhere句を用いて、あるテーブルの特定のtype1つに対して1モデルの様にします。
 例えばLaravelならば

/** User.php */
class User{
  // 仮会員、一般会員、特別会員に関する大量のメソッド
}

となっている1つのモデル定義ファイルを明示的なテーブル指定とグローバルスコープによって

/** TemporaryUser.php */
class TemporaryUser{
    protected $table = 'users';
    public funtion boot(){
        parent::boot();
        self::addGlobalScope('decide_user_type', static function (Builder $query) {
            $query->where('type', config('const.user.type.temporary'));
        });
    }
    // 仮会員に関するメソッド
}
/** GeneralUser.php */
class GeneralUser{
    protected $table = 'users';
    public funtion boot(){
        parent::boot();
        self::addGlobalScope('decide_user_type', static function (Builder $query) {
            $query->where('type', config('const.user.type.general'));
        });
    }
    // 一般会員に関するメソッド
}
/** SpecialUser.php */
class SpecialUser{
    protected $table = 'users';
    public funtion boot(){
        parent::boot();
        self::addGlobalScope('decide_user_type', static function (Builder $query) {
            $query->where('type', config('const.user.type.special'));
        });
    }
    // 特別会員に関するメソッド
}

と分割した3ファイルに分割します。完全に共有する部分は、ユーザ特性としてtraitを用意し、3会員モデルクラスそれぞれにuseさせることで整理できます。
 テーブルを分割する例を挙げましたが逆に複数のテーブルや設定等をまとめた上でカプセル化されたEntityクラスを作るというのもまた別のAdapterパターンの活用です。

  • この記事いいね! (1)