Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

(Laravel第6回目)ファサード(Facade)からサービスコンテナを学ぶ

ファサード(Facade)とは?

前回でも書きましたが、デザインパターンの1つ。
ある処理や機能をClassに纏めておいて、どこからでも呼び出せる状態にしておく方法。これにより、メイン処理で使われるClassの独立性を高めることができます。

これは私が自作したファサード(Facade)ですが、以下のように、どこからもuse宣言されていないのに、HogeクラスのechoFuga()メソッドを呼ぶことができます。

■ welcome.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <title>HogePage</title>
    </head>
    <body>
        {{ Hoge::echoFuga() }}
    </body>
</html>

では、なぜこんなことができるのか、(自作した)上記ソースを追ってみましょう!

Hogeクラスはクラスではなく、エイリアス

上記でHogeクラスと書きましたが、実はこれはクラスではなく、エイリアスです。
config/app.configに以下のように定義しています。

■ config/app.conf

    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        ・
        ・
        ・
        'Hoge' => App\Facades\Hoge::class,
    ]

'Hoge'というエイリアス名で、'Hoge'クラスを登録しています。
この'Hoge'クラスにロジックが書かれて・・・いないです。

■ app/Facades/Hoge.php

<?php
namespace App\Facades;

use Illuminate\Support\Facades\Facade;


class Hoge extends Facade
{
  protected static function getFacadeAccessor() {
    return 'fuga';
  }
}

この'Hoge'クラスは、getFacadeAccessorメソッドで'fuga'を返しています。この'fuga'は何なのでしょうか。
ここで出てくるのが、サービスコンテナです。

サービスコンテナとは?

クラスのインスタンスインスタンスの生成方法を記入しておいて、呼び出しがあった際にインスタンスを作れる状態にしておくことです。
このインスタンスインスタンスの生成方法を記入しておく場所ですが、Laravelだとサービスプロバイダーになります。

■ app/Providers/TestServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class TestServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'fuga',
            'App\Http\Components\Fuga'
        );
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

サービスコンテナにインスタンス生成方法を登録することをbind(バインド)と呼びます。
インスタンスをサービスコンテナから返すことをresolve/resolving(解決する)と呼びます。

bind方法は色々とありますが、単純な方法は上記のようにapp->bindメソッドを使用して、第一引数に文字列(登録名)、第二引数にクロージャ(生成方法)を記入すればbindできます。
今回は、第二引数にクロージャを使用せずにFugaクラスを指定しました。最初に見たechoFuga()メソッドは、FugaクラスにあるechoFuga()メソッドだったのです。

■ app/Http/Components/Fuga.php

<?php
namespace App\Http\Components;

class Fuga
{
  public function echoFuga()
  {
    return 'HOGEと見せかけたFUGA';
  }
}

これで、上記'Hoge'クラスのgetFacadeAccessorメソッドで'fuga'を返している理由がわかりました。
このサービスコンテナの'fuga'を呼んでいたんですね。

あれ?サービスコンテナのインスタンスってどうやって作ってるの?

'Hoge'クラスの継承元のFacadesでインスタンスを作っています。
Facadesクラスには以下__callStatic()と呼ばれるマジックメソッドがあり、アクセス不能メソッドがあった場合にインスタンスを作るようになっています。

__callStatic() は、 アクセス不能メソッドを静的コンテキストで実行したときに起動します。

■以下より引用
https://www.php.net/manual/ja/language.oop5.overloading.php#object.callstatic

■ Illuminate\Support\Facades\Facade.php

    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }

なので、サービスコンテナ内でインスタンスを作るのではなく、サービスコンテナの呼び出し先でインスタンスを作るのが良さそう。

最後に整理

さて、ここまでファサードからサービスコンテナ、エイリアスを学んできました。
最後に図にしてみます。だいたいこんな感じ?

f:id:special-moucom:20191007231155p:plain

Laravelを学んでいると必ず出てくるファサードとサービスコンテナについて、なんとなくわかった気がしました。うーん、Laravelは奥が深い(= = ;;

Webエンジニアになって1年が経つので振り返ってみた話

はじめに

私は11年間勤めていた会社を辞め、34歳で客先常駐の金融系のSE(システムエンジニア)からWebエンジニアに転職しました。
以下、経緯等はこちらに纏めています。

kodak.hatenablog.com

Webエンジニアになって、1年が経ったので転職後の振り返ってみたいと思います。

転職して良かったの?悪かったの?

良かった。

一番良かったのは、子供を保育園へ送る時間ができたことだ。
転職後は、出社時間が遅くなり、通勤時間が短くなり、加えてフレックス制度があったので子供を保育園へ送る時間が確保できたのは凄く助かった。
また、上司にも3歳の子供がいるため、子供の急な発熱等での帰宅や看護休暇に理解があり、このあたりも大変助かっている。

次に、自身が望んでいたAWS関連の仕事やWeb周りの仕事ができたことだ。
このおかげでAWS周りの知識、Web周りの知識が増えた。
また、AWS関連のイベントにも会社の勤務時間扱いで参加させて貰えたことも凄く嬉しかった。

転職して1年間何してた?

仕事としては、主にAWSWordPressを使った開発をしていた。
開発環境はDockerを使う事が多く、転職後はAWSWordPress、Dockerの経験や知識が身についた。

稼働率は(現時点では)それ程高くはなく、空いている時間等でVue.jsやLaravelの勉強もやっていた。
あとは、帰宅後(子供が寝ている間)や隙間時間を使って、ブログや本、Vue.jsやLarabelを触ったりしていた。

こうやって、文字にすると時間に余裕があるように見えるのだが、実際はそれ程時間はなく、いつも身を削って何かやっているのが現実・・・

できたことは?

まず、Webエンジニアになれたこと。その上で、子育てと仕事の両立に成功したこと。
お客さんと話して要件を詰めるといったディレクター的なこと(前職に近い仕事)とコーディングを両立できたこと。

あとは上記でも書いたが、念願のAWSの仕事に関われたことやWordPressやDockerにも触れることができて、経験や知識が増えたことが上げられる。

できなかったことは?

色々と手を付けすぎていて各分野で知識が浅いこと。
勉強会やコミュニティーにあまり参加できなかったことが上げられる。

今後はどうしたい?

1つは、コーディング技術自体は1年前とそれ程上がっていないので、コーディング技術を上げたい。TDDとかしたい・・・
もう1つは、各分野の知識を深めたい・・・のだが、各分野の知識を深めるには歳を取りすぎてしまった気もしている(涙)
そこで何か1つ専門分野を身に付けて、そこから広げていけたらいいなと考えている。

最後に、勉強会やコミュニティの参加を増やしたい・・・という思いもあるのだが、これも引き続き難しいだろうと思うので、ブログでアウトプットするアクションを続けていけたらと思う。

AWSの資格のSAAの有効期限が迫っているので、こちらもなんとかしたい・・・(= = ;;)

おわりに

たまに考えるのが、「もし、転職せずに前職のままだったとしたら・・・」
仕事と子育ての両立なんて無理だったと思います。もしかすると妻と喧嘩して家庭崩壊・・・なんてことも!?

それだけ子供を育てるって大変なんですよね。

というのを、子供が産まれた後に理解しました・・・(もちろん想像はしていましたが、想像以上でした)

仕事と子育てを両立している人って、本当にすごいと思う。もし、これから子供が産まれる!子供を考えている!って方は、今の生活のまま子育てできるのか・・・というのを今一度考えてみるのをオススメします。

(Laravel第5回目)Laravelの認証機能について学んだ話

Laravel第5段です。
今回は(9)まで、進めたので、その復習(アウトプット)をしたいと思います。
このシリーズは今回が最後です。参考にしたサイトは以下。

www.hypertextcandy.com

(9)の作成時点

f:id:special-moucom:20190908003931p:plain

(8)から(9)までに学んだこと

  • Laravelの認証機能
  • 地味に便利なconfirmedルール(confirmation)
  • Auth::checkによる認証済みチェック
  • ファサード(Facade)とは?

Laravelの認証機能

Laravelには、認証機能がデフォルトで搭載されています。
コントローラ、ルーティングが最初から用意されているので、基本的にテンプレートを書くだけで認証機能が使えます。もちろん、カスタマイズして使えます。

地味に便利なconfirmedルール(confirmation)

あるサイトでユーザー登録を行う時、メールアドレスやパスワードを2回書く時ってありますよね?
そんなバリデーションが簡単にできるconfirmedルールというのがLaravelにはあります。

confirmedルールは、nameの値name + _confirmationの値に入力された値が同じであることを自動的にチェックしてくれます。
以下、実装サンプルです。
パスワードとパスワード(確認)に入力した値が同じであることを自動的にチェックしています。

■resources/views/auth/register.blade.php

<div class="form-group">
  <label for="password">パスワード</label>
  <input type="password" class="form-control" id="password" name="password">
</div>
<div class="form-group">
  <label for="password-confirm">パスワード(確認)</label>
  <input type="password" class="form-control" id="password-confirm" name="password_confirmation">
</div>

Auth::checkによる認証済みチェック

ユーザーが認証済みかどうかをチェックして、HTMLの表示を切り替えたい場合があります。
これもLaravelではAuth::check()と書くだけです。
以下、実装サンプルです。
@if(Auth::check())でユーザー認証済みかをチェックしてHTMLの表示を切り替えます

■resources/views/layout.blade.php

@if(Auth::check())
  <span class="my-navbar-item">ようこそ, {{ Auth::user()->name }}さん</span>
  |
  <a href="#" id="logout" class="my-navbar-item">ログアウト</a>
  <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
    @csrf
  </form>
@else
  <a class="my-navbar-item" href="{{ route('login') }}">ログイン</a>
  |
  <a class="my-navbar-item" href="{{ route('register') }}">会員登録</a>
@endif

ここで疑問があります。このAuthはどこを参照しているのでしょうか・・・
調べてみるとconfig/app.confに書いてありました。

■config/app.conf

  'Auth' => Illuminate\Support\Facades\Auth::class,

app.confにClassのパスに対してエイリアス名を書くことができます。
しかし、何でもかんでもClassにエイリアスをつけられるわけではなく、ファサード(Facade)だけのようです。

ファサード(Facade)とは?

デザインパターンの1つだそうです。
ある処理や機能をClassに纏めておいて、どこからでも呼び出せる状態にしておく方法。これにより、メイン処理で使われるClassの独立性を高めることができるそうです。
「ヘルパー関数」に似ていますね。Wikipediaにも書いてありました。

Facade パターン - Wikipedia

今回で言えばFacadeクラスを用意してAuthというエイリアスをつけておけば、Auth::check()と書くだけで、処理が呼ばれるということですね。
そうなるとFacadeクラスってstaticクラスだっけ?インスタンス化しなくていいの?など、色々と疑問が出てきます。このあたりは調べ出すと深い話になりそうなので、別記事にしたいと思います。

最後に

今回はじめてLaravelに触ってみて、Laravelの便利さ・面白さに気づくことができました。
Laravelをやろう!と決めて、何から手をつけるか色々と考えた結果、今回は上記サイトのチュートリアルを一通りやりましたが、やってよかったと思います。
引き続きLaravelを触って色々と試したいと思います。

(Laravel第4回目)フォーム、バリデーション、エラーメッセージ日本語化を学んだ話

Laravel第4段です。
今回は(7)まで、進めたので、その復習(アウトプット)をしたいと思います。 参考にしたサイトは以下。

www.hypertextcandy.com

(7)の作成時点

f:id:special-moucom:20190818221213p:plain

(4)から(7)までに学んだこと

(4)から(7)までに以下について学んだので、1つずつ振り返りたいと思います。

  • フォームのCSRF対策
  • フォームの入力値バリデーション
  • エラーメッセージ日本語化

フォームのCSRF対策

フォームに以下のように@csrfと書くことで、簡単にCSRF対策ができます。

<form action="{{ route('folders.create') }}" method="post">
  @csrf
  <div class="form-group">
    <label for="title">フォルダ名</label>
    <input type="text" class="form-control" name="title" id="title" value="{{ old('title') }}"/>
  </div>
  <div class="text-right">
    <button type="submit" class="btn btn-primary">送信</button>
  </div>
</form>

クロスサイトリクエストフォージェリCSRF)とは

クロスサイトリクエストフォージェリCSRF)とは、Webアプリケーションに存在する脆弱性、もしくはその脆弱性を利用した攻撃方法のことです。掲示板や問い合わせフォームなどを処理するWebアプリケーションが、本来拒否すべき他サイトからのリクエストを受信し処理してしまいます。

■参考URL

クロスサイトリクエストフォージェリ(CSRF) | トレンドマイクロ

@csrfは、フォームを表示した時にCSRFトークンを含んだinput要素を出力します。 そのCSRFトークンをセッション内に予め保存しておき、フォームからリクエストが送信されたときに、リクエストに含まれるCSRFトークンとセッション内に保存したトークンの一致確認をして、リクエストが本人から送信されたことを確認しているようです。 この1度しか使われないトークンは、ワンタイムトークンと呼ばれています。

フォームの入力値バリデーション

Laravelは、リクエストに対するバリデーションをコントローラから切り離して定義できます。
それが、FormRequestです。

以下コマンドを使えばFormRequest用のクラスを作成できます。

php artisan make:request CreateTask

詳細は以下を参照ください。
バリデーションの定義はrules関数内で定義します。attributes関数では、入力項目(name)に対する日本語名を定義できたり、message関数では、バリデーションのエラーメッセージを追加できたりします。便利だ(= = ;;)

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateTask extends FormRequest
{
    // リクエストを受け付ける場合はtrue
    public function authorize()
    {
        return true;
    }

    // ここでバリデーションルールを設定
    public function rules()
    {
        return [
            'title' => 'required|max:100',
            // after_or_equal の引数として today を指定することにより今日を含んだ未来日だけを許容
            'due_date' => 'required|date|after_or_equal:today',
        ];
    }

    // 入力項目(name)の日本語化 ・・・ 入力項目(name)でバリデーションエラーが発生した時に表示する日本語名を指定
    public function attributes()
    {
        return [
            'title' => 'タイトル',
            'due_date' => '期限日',
        ];
    }

    // バリデーションエラーのメッセージ追加
    public function message()
    {
        return [
            // キーでメッセージが表示されるべきルールを指定する。
            // ドット区切りで左側が項目、右側がルールを意味する。
            // due_date の after_or_equal ルールに違反した場合は、値に指定されたメッセージを出力する
            // 一般的なルールについては validation.php に記述するが、messagesメソッドでは個別の FormRequestクラスの内部でのみ有効なメッセージを定義できる
            'due_date.after_or_equal' => ':attribute には今日以降の日付を入力してください。',
        ];
    }
}

さて、このFormRequest・・・どこで呼ばれているのでしょう? FormRequestはコントローラから切り離して定義したものなので、コントローラを見てみましょう。

class TaskController extends Controller
{
// ・・・省略・・・ //

    public function create(int $id, CreateTask $request)
    {
        $current_folder = Folder::find($id);

        $task = new Task();
        $task->title = $request->title;
        $task->due_date = $request->due_date;
    
        $current_folder->tasks()->save($task);
    
        return redirect()->route('tasks.index', [
            'id' => $current_folder->id,
        ]);
    }

// ・・・省略・・・ //
}

ありました。 createの引数から呼ばれていますね。これがDIなんですね。

DIとは?

「依存性の注入」(Dependency injection)のこと。
クラスと依存関係にあるオブジェクトを外から渡す事で、依存レベルをさらに下げることができる方法です。
今回で言えば、TaskControllerクラスとCreateTaskクラスは依存関係にありますが、CreateTaskクラスはTaskControllerクラスから切り離して定義して、オブジェクトとして外から渡しています。

エラーメッセージ日本語化

メッセージ関連の設定は resources/lang ディレクトリで管理されています。 最初、enフォルダしかないので、jpフォルダを新しく作成して、そこにメッセージを格納するようにしていきます。

それに伴い、app.phpファイルも修正します。

'locale' => 'jp',

'fallback_locale' => 'en',

あとは、各々のphpファイルを日本語に修正するだけで、メッセージが日本語になります。

うーん、学びが多い(= = ;; )

(Laravel第3回目)データベース周りを理解した話

Laravel第3段です。
今回はデータベース周りを学んだので、その復習(アウトプット)をしたいと思います。参考にしたサイトは以下。

www.hypertextcandy.com

Laravelを使って何かアプリケーションを作るとき、必ず必要となるのがデータベースとデータベースに格納するデータです。

Laravelは簡単にPHPとデータベースの連携、データの操作、テストデータの作成ができるので、今回はその辺りを復習したいと思います。

PHPとデータベースの連携

データベースに格納しているデータに対して操作を行うとき、データベースに接続してSQLを使います。しかし、LaravelはPHPなので、PHPからデータベースへ接続、SQLを使う必要があります。
さらに、データベースには様々な種類があり、接続方法も種類によって異なります。
これらを自身で実装するのは、とても大変なのでLaravelにはORM(Object Relational Mapping)が搭載されています。

Eloquent ORM(Eloquent Object Relational Mapping)

Laravelに搭載しているORMの名称です。
ORMとは、名前の通り、オブジェクト指向プログラムとリレーショナルデータベースをマッピングしてくれる機能です。LaravelはORMのEloquentと呼ばれるものを使っています。これを使うことでPHPからデータベースへの接続、データの操作を簡単に行うことができます。

Active Recode

Active Recodeという言葉を目にすることがあります。
これはテーブルとクラスを一対一で結び付けて、CRUD(Create、Read、Update、Deleteのこと)操作をクラスでやりましょうというデザインパターンの1つです。Eloquentはこのデザインパターンにそって実装されています。

余談ですが、RubyRailsもActive Recodeのデザインパターンを採用したActive Recodeという名のORMがあり(LaravelでいうEloquentに代わるもの)、混在して書かれてる記事が多いので、ややこしいです。(= = ;;)

データの操作

前回のControllerのソースを一部抜粋しました。
Folderクラス(DBのテーブル名はfolders)のall()、find()、where()を使って、データベースに格納しているデータに対して操作していることがわかります。ちゃんとActive Recodeのデザインパターンになっていることがわかりますね。

■ TaskController(一部抜粋)
namespace App\Http\Controllers;

use App\Folder;
use App\Task;

class TaskController extends Controller
{
    public function index(int $id)
    {
        // ① (DB)Folderテーブルから全件取得
        $folders = Folder::all();

        // ② HTTPリクエストから取得した$idをキーに(DB)Folderテーブルから検索
        $current_folder = Folder::find($id);
        
        // ③ (DB)Taskテーブルから②と一致するデータを取得
        $tasks = Task::where('folder_id', $current_folder->id)->get();

テストデータの作成

Webアプリケーションやシステムを作成していると、「あー、データがほしいなぁ」と思うことがあります。やはり、データがないとアプリケーションやシステムが動いてる感がでないですよね。
LaravelにはSeederと呼ばれる簡単にテストデータを用意する機能が搭載されています。

Seeder

これはfoldersテーブルにデータを追加するSeederの例です。 runメソッドの中でデータを追加(insert)しているのがわかると思います。
ここでの学びは、Seederは上記で学んだEloquentを使っていない点です。Eloquentの継承先にあるDBクラスを直接呼び出して、PHPSQLっぽく書いています。これをデータベースクエリビルダと呼びます。

■ FoldersTableSeeder

use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class FoldersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $titles = ['プライベート', '仕事', '旅行'];

        foreach ($titles as $title) {
            ① DBクラスを直接呼び出してクエリビルダを使ってデータを追加(insert)
            DB::table('folders')->insert([
                'title'      => $title,
                'created_at' => Carbon::now(),
                'updated_at' => Carbon::now(),
            ]);
        }
    }
}

Eloquentとデータベースクエリビルダの違い

Eloquentを使用して取得したデータとデータベースクエリビルダを使用して取得したデータには違いがあります。
Eloquentを使用して取得したデータは、テーブルのクラスのオブジェクトとしてデータを取得しますが、データベースクエリビルダを使用して取得したデータは、stdClassのオブジェクトとしてデータを取得します。
そのため、データベースクエリビルダを使用すると前回で学んだアクセサ等が使えなくなります。

■データベースクエリビルダを使用してデータを取得
>>> var_dump(DB::table('folders')->get());
object(Illuminate\Support\Collection)#2939 (1) {
  ["items":protected]=>
  array(4) {
    [0]=>
    object(stdClass)#2932 (4) {
      ["id"]=>
      int(1)
      ["title"]=>
      string(18) "プライベート"
      ["created_at"]=>
      string(19) "2019-07-04 15:02:24"
      ["updated_at"]=>
      string(19) "2019-07-04 15:02:24"
    }
    [1]=>
    object(stdClass)#2938 (4) {
      ["id"]=>
      int(2)
      ["title"]=>
      string(6) "仕事"
      ["created_at"]=>
      string(19) "2019-07-04 15:02:24"
      ["updated_at"]=>
      string(19) "2019-07-04 15:02:24"
    }
    [2]=>
    object(stdClass)#2949 (4) {
      ["id"]=>
      int(3)
      ["title"]=>
      string(6) "旅行"
      ["created_at"]=>
      string(19) "2019-07-04 15:02:24"
      ["updated_at"]=>
      string(19) "2019-07-04 15:02:24"
    }
    [3]=>
    object(stdClass)#2946 (4) {
      ["id"]=>
      int(4)
      ["title"]=>
      string(9) "テスト"
      ["created_at"]=>
      string(19) "2019-07-13 05:08:33"
      ["updated_at"]=>
      string(19) "2019-07-13 05:08:33"
    }
  }
}
=> null

■Eloquentを使用してデータを取得
>>> Folder::all();
[!] Aliasing 'Folder' to 'App\Folder' for this Tinker session.
=> Illuminate\Database\Eloquent\Collection {#2950
     all: [
       App\Folder {#2952
         id: 1,
         title: "プライベート",
         created_at: "2019-07-04 15:02:24",
         updated_at: "2019-07-04 15:02:24",
       },
       App\Folder {#2953
         id: 2,
         title: "仕事",
         created_at: "2019-07-04 15:02:24",
         updated_at: "2019-07-04 15:02:24",
       },
       App\Folder {#2954
         id: 3,
         title: "旅行",
         created_at: "2019-07-04 15:02:24",
         updated_at: "2019-07-04 15:02:24",
       },
       App\Folder {#2955
         id: 4,
         title: "テスト",
         created_at: "2019-07-13 05:08:33",
         updated_at: "2019-07-13 05:08:33",
       },
     ],
   }
>>>

最後に

今回はデータベース周りについて学びました。Eloquentとデータベースクエリビルダの違いは、私もすごく勉強になりました。できる限りEloquentを使用する方がコードをキレイに書くことができそうです。
では、また自分自身の理解も兼ねて引き続きアウトプットしていきたいと思います。( = = )ノ