Skip to content

Instantly share code, notes, and snippets.

@youhey
Last active November 16, 2017 08:27
Show Gist options
  • Save youhey/25f16a3266fa8647a663c34ac7548234 to your computer and use it in GitHub Desktop.
Save youhey/25f16a3266fa8647a663c34ac7548234 to your computer and use it in GitHub Desktop.
Laravelでセッターインジェクション

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;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment