Laravelでセッターインジェクションのような仕組みをしたい場合、どうやるのが正解なのか見つけられずに苦しんでいる。
例えば、Facade
を使わないという前提の開発環境で、ORMクラスにキャッシュを渡したい場合や、ロガーをセットしたい場合など、コンストラクタインジェクションではなくセッターインジェクションがしたい。
namespace App\ORM\Contracts;
use Illuminate\Cache\CacheManager;
interface CachingInterface
{
public function setCacheManager(CacheManager $cache): void;
protected function getCacheManager(): CacheManager;
}
namespace App\ORM\Concerns;
use Illuminate\Cache\CacheManager;
trait CacheableTrait
{
/** @var CacheManager */
protected $cacheManager;
public function setCacheManager(CacheManager $cache): void
{
$this->cacheManager = $cache;
}
protected function getCacheManager(): CacheManager
{
return $this->cacheManager;
}
}
namespace App\ORM;
use Illuminate\Database\Eloquent\Model;
use App\ORM\Concerns\CacheableTrait;
use App\ORM\Contracts\CachingInterface;
class User extends Model implements CachingInterface
{
use CacheableTrait;
}
こんな感じのセッターインジェクションをしたい。(このORMはリポジトリでコンストラクタインジェクションするイメージ)
できたらコンテナで解決したくて色々試してみたけど、Laravelのコンテナ動作の仕組みを勘違いしているようで、期待するような動作にできない……。
例えば、ORMクラスはコンテナを経由して生成されるんであれば、extend メソッドでセッターインジェクションすればいいんじゃないか?という単純なやつ。 (しかも無理やりファイルベースで対象クラスを検索するという頭の悪いパターン)
namespace App;
use RegexIterator;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use RecursiveRegexIterator;
use Illuminate\Cache\CacheManager;
use Illuminate\Support\ServiceProvider;
class CacheManagerInjectionProvider extends ServiceProvider
{
/**
* Bootstrap
*
* @return void
*/
public function boot()
{
}
/**
* Setter injection
*
* @return void
*/
public function register()
{
$cachingModels = $this->recursiveCachingModels();
foreach ($cachingModels as $class) {
$this->app->extend($class, function (ORM\Contracts\CachingInterface $concrete) {
/** @var CacheManager $cache */
$cache = $this->app['cache'];
$concrete->setCacheManager($cache);
return $concrete;
});
}
}
/**
* Recursive caching ORM models
*
* @return string[]
*/
private function recursiveCachingModels(): array
{
$path = realpath(__DIR__ . '/ORM');
$files = new RegexIterator(
new RecursiveIteratorIterator(new RecursiveDirectoryIterator("{$path}/")),
'!^' . preg_quote($path) . '/(.+/)?(.+)\.php$!i',
RecursiveRegexIterator::REPLACE
);
$files->replacement = '$1$2';
$cachingModels = [];
foreach ($files as $file) {
$class = sprintf('App\\ORM\\%s',str_replace('/', '\\', $file));
if (in_array(ORM\Contracts\CachingInterface::class, class_implements($class), true)) {
$cachingModels[] = $class;
}
}
return $cachingModels;
}
}