こんにちは、しょいみんです。
Laravel フレームワーク内の処理で詰まったところがあったので知見を共有していきますm(_ _)m
概要
Laravel フレームワークはフレームワーク自体に様々なオープンソースのライブラリが含まれているため、それほどプラスα でライブラリをインストールすることなく簡単に開発できるようになっています。
しかし、開発を進めていくと**「既存のライブラリだと少し物足りないなぁ」、「既存のライブラリを少しだけ拡張したいなぁ」**といった要望が出てくると思います。
今回は、Laravel で標準インストールされている Flysystem のライブラリを拡張する方法を紹介いたします。この方法を利用すれば Flysystem に限らず Laravel 内の他のライブラリも同様に拡張することができます。
結論
サービスプロバイダーを既存のクラスから自分で定義したクラスに切り替えることでどんなライブラリでも自分で定義したクラスに差し替えて呼び出すことができます!!!!!
拡張したい関数だけ修正して他の処理は元のライブラリの処理のままコピペすることで既存の処理を壊さずに拡張したい関数だけ修正することができます。
環境
Laravel 5.8
php7.4.5
※検証してませんが Laravel 6, 7 でもサービスプロバイダーの考え方は一緒なので同様の方法で実現できると思います。
目標(拡張したい処理の内容)
画像一覧を取得する関数がデフォルトでファイル名ソートしかできないので任意のメタ情報で昇順・降順ソートできるように関数を拡張します
拡張前の元ファイルと関数
下記ライブラリの関数を修正します。
見ての通りオプションもなくファイルパスでしかソートできないですね (´・ω・`)
vendor/league/flysystem/src/Util/ContentListingFormatter.php
class ContentListingFormatter
{
・・・・・省略・・・・・・
/**
* @param array $listing
*
* @return array
*/
private function sortListing(array $listing)
{
usort($listing, function ($a, $b) {
return strcasecmp($a['path'], $b['path']);
});
return $listing;
}
}
拡張後のファイルと関数
拡張後のファイルと関数の内容です。
ファイルパスでしかソートできなかった sortListing()
関数を任意のメタ情報と昇順・降順フラグを引数にとって並び替えできるように拡張します。
app/Extensions/Flysystem/Util/ContentListingFormatter.php
class ContentListingFormatter
{
・・・・・省略・・・・・・
/**
* @param array $listing
* @param string $sortType
* @param bool $isAsc
*
* @return array
*/
private function sortListing(array $listing, string $key = 'path', $isAsc = true)
{
usort($listing, function ($a, $b) use ($key, $isAsc) {
$result = $isAsc ? strcasecmp($a[$key], $b[$key]) : strcasecmp($b[$key], $a[$key]);
return $result;
});
return $listing;
}
}
修正手順
修正手順です。この3つの手順に沿って修正手順を解説していきます。
- 拡張したい関数が記述されてるクラスとそのクラスを呼び出しているクラスをコピペして新しく作成
- 関数の修正やネームスペース、呼び出しクラスの参照パスを修正
- サービスプロバイダー(
FilesystemServiceProvider::class
)の切り替え
新しく作成するファイル一覧
下記ファイルパスの vendor 配下のライブラリのコードをコピー元として新規作成します。
修正箇所は後述します。
# league/flysystem ライブラリ
コピー元: vendor/league/flysystem/src/Filesystem.php
新規作成: app/Extensions/Flysystem/Filesystem.php
コピー元: vendor/league/flysystem/src/Util/ContentListingFormatter.php
新規作成: app/Extensions/Flysystem/Util/ContentListingFormatter.php
# Laravel 標準フレームワーク内の ファイルシステム
コピー元: vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemManager.php
新規作成: app/Extensions/Flysystem/FilesystemManager.php
# Laravel 標準フレームワーク内の サービスプロバイダー
コピー元: vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemServiceProvider.php
新規作成: app/Providers/FilesystemServiceProvider.php
※ 新規作成するファイルの保存ディレクトリはどこでもいいですが Extensions に格納することとします。
※ サービスプロバイダーは Providers フォルダに格納することとします。
1. 拡張したい関数が記述されてるクラスとそのクラスを呼び出しているクラスをコピペして新しく作成
ContentListingFormatter.php
を自身で定義したクラスに差し替えるために必要なファイルをすべてコピペします。
新規作成するファイルは上記の 「元ファイルと新規作成ファイル」 で記載されている 4 ファイルです。
Laravel のファイルシステム実行処理の関係で修正が必要なクラスファイルが増えています。
下記に画像一覧を表示させるための処理の流れを記述しておきます。
-
config/app.php
に記載されているFilesystemServiceProvider.php
が呼び出される -
FilesystemServiceProvider.php
のregisterManager()
がエイリアス'filesystem'
でFilesystemManager
インスタンスを作成する。 -
FilesystemManager.php
のcreateFlysystem()
関数でFlysystem
のインスタンスを返す。 -
Flysystem.php
のlistContents()
関数でContentListingFormatter
のインスタンスのformatListing()
関数を実行する。 -
ContentListingFormatter.php
のformatListing()
関数内でソートの関数である$this->sortListing()
が実行される。 ← 今回はここの関数の処理を改造します。
流れを確認したことで、5番で利用されている ContentListingFormatter.php
を自身で定義したクラスに差し替えるために 1 ~ 4 で呼び出されているクラスファイルの参照パスを修正する必要があることがわかります。
2. 関数の修正とネームスペース、呼び出しクラスの参照パスを修正
次にコピペして新規作成したファイルを一つずつ修正していきます。
ContentListingFormatter.php
<?php
namespace app\Extensions\Flysystem\Util;
# namespace League\Flysystem\Util;
use League\Flysystem\Util;
/**
* @internal
*/
class ContentListingFormatter
{
--------- 途中 vendor と同じなので省略 ---------
/**
* @param array $listing
* @param string $sortType
* @param bool $isAsc
*
* @return array
*/
# private function sortListing(array $listing)
private function sortListing(array $listing, string $key = 'path', $isAsc = true)
{
usort($listing, function ($a, $b) use ($key, $isAsc) {
$result = $isAsc ? strcasecmp($a[$key], $b[$key]) : strcasecmp($b[$key], $a[$key]);
return $result;
});
# usort($listing, function ($a, $b) {
# return strcasecmp($a['path'], $b['path']);
# });
return $listing;
}
}
ContentListingFormatter.php
では拡張したい関数を修正し namespace
を app フォルダ配下のパスに修正します。
それ以外のコードは元ファイルのコピペをそのまま利用します。
Filesystem.php
<?php
# namespace League\Flysystem;
namespace app\Extensions\Flysystem;
use InvalidArgumentException;
use League\Flysystem\Adapter\CanOverwriteFiles;
use League\Flysystem\Plugin\PluggableTrait;
# use League\Flysystem\Util\ContentListingFormatter;
use League\Flysystem\FilesystemInterface; # 追記
use League\Flysystem\ConfigAwareTrait; # 追記
use League\Flysystem\AdapterInterface; # 追記
use League\Flysystem\File; # 追記
use League\Flysystem\Directory; # 追記
use League\Flysystem\Handler; # 追記
use League\Flysystem\Util; # 追記
use League\Flysystem\Config; # 追記
use League\Flysystem\FileNotFoundException; # 追記
use League\Flysystem\FileExistsException; # 追記
use League\Flysystem\RootViolationException; # 追記
use App\Extensions\Flysystem\Util\ContentListingFormatter; # 追記
--------- 以下 vendor と同じなので省略 ---------
Filesystem.php
では namespace
と Filesystem.php
内で利用してるクラスを use 文で参照させます。
もともと vendor 配下の Filesystem.php
では同ディレクトリに他のクラスファイルの php が配置されていたので直で参照されていました。今回は app フォルダ配下に Filesystem.php
を新規作成したので vendor 配下のクラスファイルをしっかりと use 文で参照させています。
また、
use League\Flysystem\Util\ContentListingFormatter;
は vendor 配下のクラスファイルを参照してるのでコメントアウトし、今回自分で作成したファイル
use App\Extensions\Flysystem\Util\ContentListingFormatter;
に参照させ直します。
FilesystemManager.php
<?php
# namespace Illuminate\Filesystem;
namespace app\Extensions\Flysystem;
use Closure;
use Aws\S3\S3Client;
use OpenCloud\Rackspace;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Sftp\SftpAdapter;
use League\Flysystem\FilesystemInterface;
use League\Flysystem\Cached\CachedAdapter;
# use League\Flysystem\Filesystem as Flysystem;
use App\Extensions\Flysystem\Filesystem as Flysystem; # 追記
use League\Flysystem\Adapter\Ftp as FtpAdapter;
use League\Flysystem\Rackspace\RackspaceAdapter;
use League\Flysystem\Adapter\Local as LocalAdapter;
use League\Flysystem\AwsS3v3\AwsS3Adapter as S3Adapter;
use League\Flysystem\Cached\Storage\Memory as MemoryStore;
use Illuminate\Contracts\Filesystem\Factory as FactoryContract;
use Illuminate\Filesystem\FilesystemAdapter; # 追記
--------- 以下 vendor と同じなので省略 ---------
こちらも同様 namespace と参照している vendor 配下の Filesystem.php
の参照先を自分で作成した app 配下の Filesystem.php
に参照させ直しています。
FilesystemServiceProvider.php
<?php
# namespace Illuminate\Filesystem;
namespace App\Providers; # 追記
use Illuminate\Support\ServiceProvider;
use Illuminate\Filesystem\Filesystem; # 追記
use App\Extensions\Flysystem\FilesystemManager; # 追記
--------- 以下 vendor と同じなので省略 ---------
Filesystem のサービスプロバイダーです。 config/app.php
から呼び出されるやつですね。
こちらも同様、namespace と FilesystemManager.php
の参照パスを修正し、参照が切れてしまっているクラスファイルを参照させ直しましょう。
以上で、新規作成したファイルの修正が完了となります。
最後にサービスプロバイダーの切り替えを行いましょう。
3. サービスプロバイダー(FilesystemServiceProvider::class
)の切り替え
既存のファイルシステムサービスプロバイダーを自身で作成したサービスプロバイダーに切り替えて変更を反映させます。
app.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
|
| The service providers listed here will be automatically loaded on the
| request to your application. Feel free to add your own services to
| this array to grant expanded functionality to your applications.
|
*/
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
// 既存の Filesystem ではなくソートのコードを拡張した Filesystem を利用するため
// Illuminate\Filesystem\FilesystemServiceProvider::class,
App\Providers\FilesystemServiceProvider::class, # 追記
----------- 以下略 -------------
],
];
config/app.php
ファイルを開けて、'providers'
配列を見てみましょう。
既存のファイルシステムサービスプロバイダーであるIlluminate\Filesystem\FilesystemServiceProvider::class
クラスが記述されています。
ここを自身で作成した、
App\Providers\FilesystemServiceProvider::class
に変更します。
以上ですべての修正が完了です。あとはオートローダのリロードを行うと反映されます。
composer dump-autoload
まとめ
Laravel ではサービスプロバイダーの切り替えで vendor 配下のライブラリですら自身で定義したクラスファイルに切り替えできることがわかりました。
切り替えの手順は 3 ステップ
- 拡張したい関数が記述されてるクラスとそのクラスを呼び出しているクラスをコピペして新しく作成
- 関数の修正やネームスペース、呼び出しクラスの参照パスを修正
- サービスプロバイダー(
FilesystemServiceProvider::class
)の切り替え
これで vendor 配下を汚さずに拡張できるのでもし要件があったら参考にしてみてくださいm(_ _)m。
ただし、自身で拡張したクラスファイル(ここの場合は Filesystem.php
、ContentListingFormatter.php
、FilesystemManager.php
、FilesystemServiceProvider.php
の4ファイル) は composer でのライブラリアップデートには追随しないことに注意してください。