自分だけのクイズ作成 - Quipha公開中

【Laravel】ファサードの作成と実践・検証

Laravel
スポンサーリンク

はじめに

Laravelのファサードを作成する手順をまとめました。
Laravelを使用する上で、避けては通れないほど重要な機能の一つです。

Laravelは標準で色々なファサードを用意しており、知らず知らずのうちにコーディングで使用していると思います。

実際に独自のファサードを作成し、使い方もまとめましたので、実践に役立つと思います。

Laravelの構築方法は以下をご覧ください。

使用するバージョン
  • Laravel Framework 8.69.0

Quipha

個人開発ですがQuiphaというサービスを開発しました。
Webアプリケーションであり、LaravelとVue3(Composition APIも使用)で作成しました。

良かったら、会員登録して動作を試してみて下さい。

ファサードのメリット

一番分かりやすいメリットとしては、自分でインスタンスを生成しなくても使える点でしょう。

// インスタンスを手動で作成
$book = new Book();
$book->getBook(1);

// インスタンスをnewせずに取得
\Book::getBook(1);

また、Laravelのファサードは、モックするための便利な方法が提供されているため、テストを行う際にも役立ちます。

それだけ?と思うかも知れませんが、あまり深く考えずに、ビジネスロジックや共通処理など、役割ごとにクラスを作成し、ファサードを介して取得するようにしておけば、良いのではないでしょうか。

サービスクラスの作成

今回は、ビジネスロジックを想定し、サービスクラスとして作成しますが、クラスであれば何でも良いです。

Servicesフォルダを作成しました。

app\Services\

上記のフォルダに以下のような簡単なクラスを作成しました。

<?php

namespace App\Services;

/**
 * Book Service
 */
class BookService
{

    /**
     * get book
     * 
     * @param string $book_no no
     */
    public function getBook($book_no)
    {
        return "get book: " . $book_no;
    }
}
スポンサーリンク

サービスプロバイダーの作成

クラスをサービスコンテナに登録します。以下のコマンドで、サービスプロバイダーを作成します。

php artisan make:provider BookServiceProvider

ファイルが作成されました。

app\Providers\BookServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class BookServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

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

registerメソッドの中で、サービスコンテナへの登録を行います。
名前とクラスパスでサービスコンテナに登録することができます。

    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('book', 'App\Services\BookService');
    }

サービスプロバイダーの登録

configファイルを開きましょう。

config\app.php

先ほど作成したサービスプロバイダーをconfigに追加します。

    'providers' => [
...
        App\Providers\BookServiceProvider::class,
    ],

これによりサービスプロバイダーが読み込まれます。

今回はファサードを介して取得するので関係ありませんが、この時点でサービスコンテナからインスタンスを取得することができます。

    $bookService = app()->make('book');
    $book = $bookService->getBook(1);
    echo $book; // get book: 1

ファサードの作成

ファサードを作成します。Facadesフォルダを作成しました。

app\Facades\

サービスプロバイダーの、サービスコンテナーに指定した名前を戻り値に指定します。

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Book extends Facade
{

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'book';
    }
}

これでファサードを介してインスタンスを取得する準備ができました。

エイリアスの登録

エイリアスに登録しましょう。configファイルを開きます。

config\app.php

    'aliases' => [
...
        'Book' => App\Facades\Book::class,
    ],

これにより、長いクラスのパスを毎回指定しなくて済みます
例えばuseに指定しなくても、バックスラッシュだけでFacadeにアクセスできるようになります。

\Book

ファサードを使ってみる

ファサードを介して、サービスクラスのインスタンスを取得してみます。
エイリアスに登録したため、useに宣言は不要で、以下のようにファサードを使用することができます。

    $book = \Book::getBook(1);
    echo $book; // get book

newを使用して、インスタンスを生成する必要がありません。
また、::でメソッドを呼んでいますが、静的メソッドではなく、インスタンスメソッドが呼ばれています。

VS Codeで確認

エイリアスについては、別記事で詳細にまとめていますので、こちらをご覧ください。
今回は以下の手順を行い、自作のファサードがVS Code上でエラーにならないか確認しましょう。

対応する前は、VS Code上でソースを確認するとエラー表示になっていました。
ちなみに、ソース上ではこのようになっていますが、実際は問題なく実行できます

以下のコマンドで、定義を最新化します。

php artisan ide-helper:generate

VS Code上で、エラーもなく、PHPDocも正常に表示され、自作した関数へジャンプすることができました。

スポンサーリンク

ファサードのモック

ファサードのメリットの一つとして、ファサードへの呼び出しをモックできます。

先程と同様、通常の実行を見てみましょう。

    $book = \Book::getBook(1);
    echo $book; // get book: 1

それでは、getBookメソッドの呼び出しをモックしてみましょう。
もとのクラスは全く変更しておりません。shouldReceiveとandReturnを使用するだけで、getBookの戻り値を変更することができました。

    \Book::shouldReceive('getBook')->andReturn('test code!');
    $book = \Book::getBook(1);
    echo $book; // test code!

ファサードのモックは、テストを行う際に便利です。

Laravelのモックについては、以下を参考ください。

モック 8.x Laravel

ファサードを介して取得するインスタンスはシングルトン?

検証

ここからは色々と検証してみます。

インスタンスの挙動を試すために、クラス内に変数の追加と、getter/setterを追加しました。

<?php

namespace App\Services;

/**
 * Book Service
 */
class BookService
{
    var $book_name = "default name";

    /**
     * get book name
     * 
     * @return string book name
     */
    public function getBookName()
    {
        return $this->book_name;
    }

    /**
     * set book name
     * 
     * @param string $name name
     */
    public function setBookName($name)
    {
        $this->book_name = $name;
    }

    /**
     * get book
     * 
     * @param string $book_no no
     */
    public function getBook($book_no)
    {
        return "get book: " . $book_no;
    }
}

サービスコンテナの確認

サービスコンテナには、「$this->app->bind()」で登録しました。
これはサービスコンテナから取得する度に、新しいインスタンスが取得されます。

    $bookService = app()->make('book');
    echo spl_object_id($bookService); // インスタンスID: 282
    $bookService->setBookName("laravel");
    echo $bookService->getBookName(); // laravel

    // もう一度取得すると、別インスタンスで取得される
    $bookService = app()->make('book');
    echo spl_object_id($bookService); // インスタンスID: 289
    echo $bookService->getBookName(); // default name

今度は、サービスコンテナにシングルトンで登録してみます。

$this->app->singleton('book', 'App\Services\BookService');

試してみると、同じインスタンスが取得できました。

    $bookService = app()->make('book');
    echo spl_object_id($bookService); // インスタンスID: 282
    $bookService->setBookName("laravel");
    echo $bookService->getBookName(); // laravel

    // もう一度取得すると、同じインスタンスが取得される
    $bookService = app()->make('book');
    echo spl_object_id($bookService); // インスタンスID: 282
    echo $bookService->getBookName(); // laravel

ここまでは想定通りの動きです。

ファサードの確認

では、サービスコンテナには、「$this->app->bind()」で登録した場合、ファサードを介して取得するインスタンスは毎回変わるのでしょうか。

以下のコードで試してみました。

    \Book::setBookName("laravel");
    echo \Book::getBookName(); // laravel

上記のことから、ファサードを介して取得できるインスタンスは毎回同じようです。
(シングルトンのような挙動)

需要があるかどうかは別として、ファサードを介して新しいインスタンスを取得するにはどうすればよいでしょうか。
ファサードクラスに、リフレッシュを行う関数を作成しました。

app\Facades\Book.php

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Book extends Facade
{

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'book';
    }

    /**
     * refresh
     */
    public static function refresh()
    {
        static::clearResolvedInstance(static::getFacadeAccessor());
    }
}

refresh関数を呼ぶことで、新しいインスタンスを取得できることが確認できました。

    \Book::setBookName("laravel");
    echo \Book::getBookName(); // laravel
    
    // インスタンスをリフレッシュ
    \Book::refresh();
    // 新しいインスタンスを取得
    echo \Book::getBookName(); // default name

さいごに

今回は、Laravelのファサードを作成する手順をまとめました。
Laravelは色々と便利な機能や仕組みを提供していますので、是非理解して活用しましょう。

コメント

タイトルとURLをコピーしました