From 18bd18ddc6bcee2e9291e0cc26bb3ccfe8236970 Mon Sep 17 00:00:00 2001 From: Farid Saravi Date: Fri, 18 Sep 2020 21:00:09 +0430 Subject: [PATCH 1/2] feature: add alias to all polymorphic types of models --- app/Console/Commands/CacheMorphMap.php | 60 ++++++ app/Console/Commands/ClearCachedMorphMap.php | 52 +++++ app/Providers/AppServiceProvider.php | 3 +- app/Services/ModelFinder.php | 205 +++++++++++++++++++ 4 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 app/Console/Commands/CacheMorphMap.php create mode 100644 app/Console/Commands/ClearCachedMorphMap.php create mode 100644 app/Services/ModelFinder.php diff --git a/app/Console/Commands/CacheMorphMap.php b/app/Console/Commands/CacheMorphMap.php new file mode 100644 index 0000000..17e751f --- /dev/null +++ b/app/Console/Commands/CacheMorphMap.php @@ -0,0 +1,60 @@ +modelFinder = $modelFinder; + } + + /** + * Execute the console command. + * + * @return void + */ + public function handle(): void + { + $cache = $this->modelFinder->getCachePath(); + + $models = $this->modelFinder->getModels(); + $map = $this->modelFinder->getModelMap($models); + + file_put_contents( + $cache, + 'info('Morph map models cached!'); + } +} diff --git a/app/Console/Commands/ClearCachedMorphMap.php b/app/Console/Commands/ClearCachedMorphMap.php new file mode 100644 index 0000000..2996222 --- /dev/null +++ b/app/Console/Commands/ClearCachedMorphMap.php @@ -0,0 +1,52 @@ +modelFinder = $modelFinder; + } + + /** + * Execute the console command. + * + * @return void + */ + public function handle(): void + { + @unlink($this->modelFinder->getCachePath()); + + $this->info('Morph map models cache cleared!'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 35471f6..fa82b72 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Services\ModelFinder; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -13,7 +14,7 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { - // + app(ModelFinder::class)->map(); } /** diff --git a/app/Services/ModelFinder.php b/app/Services/ModelFinder.php new file mode 100644 index 0000000..60c6cb8 --- /dev/null +++ b/app/Services/ModelFinder.php @@ -0,0 +1,205 @@ +useCache()) { + return; + } + + $models = $this->getModels(); + + $map = $this->getModelMap($models); + + $this->mapModels($map); + } + + /** + * @return string + */ + public function getCachePath(): string + { + return base_path('bootstrap/cache/morphmap.php'); + } + + /** + * @return string + */ + public function getPackageBasePath(): string + { + return base_path('vendor/wm/'); + } + + /** + * @return array + */ + public function getModels(): array + { + $paths = $this->getModelPaths(); + + if (count($paths) === 0) { + return []; + } + + return $this->scan($paths); + } + + /** + * @param array $models + * + * @return array + */ + public function getModelMap(array $models): array + { + $map = []; + + foreach ($models as $modelName => $namespace) { + Arr::set($map, $this->getModelAlias($modelName), $namespace); + } + + return $map; + } + + /** + * @return bool + */ + private function useCache(): bool + { + if (!file_exists($cache = $this->getCachePath())) { + return false; + } + + $this->mapModels(include $cache); + + return true; + } + + /** + * @return array + */ + private function getModelPaths(): array + { + $vendorDirectory = $this->getPackageBasePath(); + $scannedDirectory = array_diff(scandir($vendorDirectory), array('..', '.')); + + $dirs = []; + foreach ($scannedDirectory as $dir) { + + $dirs[$dir] = $vendorDirectory . $dir . '/src/Models'; + } + + return $dirs; + } + + /** + * @param array $paths + * + * @return array + */ + private function scan(array $paths): array + { + $models = []; + foreach ($paths as $moduleName => $path) { + + if (!is_dir($path)) { + continue; + } + + $modelFiles = array_diff(scandir($path), array('..', '.')); + + foreach ($modelFiles as $modelFileName) { + if (!is_file($path . DIRECTORY_SEPARATOR . $modelFileName)) { + continue; + } + + $modelNamespace = 'WM\\' . Str::studly($moduleName) . '\\Models\\' . basename($modelFileName, '.php'); + if ($this->isModel($modelNamespace)) { + $models[basename($modelFileName, '.php')] = $modelNamespace; + } + } + } + + return $models; + } + + /** + * @param array $map + * + * @return bool + */ + private function isModel($namespace) + { + if (!class_exists($namespace)) { + return false; + } + + $reflection = new ReflectionClass($namespace); + + if ($reflection->isAbstract() || !is_subclass_of($namespace, Model::class)) { + return false; + } + + return true; + } + + /** + * @param array $map + * + * @return void + */ + private function mapModels(array $map): void + { + $existing = Relation::morphMap() ?: []; + + if (count($existing) > 0) { + $map = collect($map) + ->reject(function (string $class, string $alias) use ($existing): bool { + return array_key_exists($alias, $existing) || in_array($class, $existing, true); + }) + ->toArray(); + } + + Relation::morphMap($map); + } + + /** + * @param string $model + * + * @return string + */ + private function getModelAlias(string $model): string + { + $callback = ''; + + if ($callback && is_callable($callback)) { + return $callback($model); + } + + return $this->getModelName($model); + } + + /** + * @param string $model + * + * @return string + */ + private function getModelName(string $model): string + { + return Str::snake($model); + } +} From f9facdab6810f808f940d63823648877e92d02a0 Mon Sep 17 00:00:00 2001 From: farid saravi Date: Sat, 19 Sep 2020 17:20:09 +0430 Subject: [PATCH 2/2] fix: rename ModelFinder to MorphModelFinder --- app/Console/Commands/CacheMorphMap.php | 18 +++++++++--------- app/Console/Commands/ClearCachedMorphMap.php | 14 +++++++------- .../{ModelFinder.php => MorphModelFinder.php} | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) rename app/Services/{ModelFinder.php => MorphModelFinder.php} (99%) diff --git a/app/Console/Commands/CacheMorphMap.php b/app/Console/Commands/CacheMorphMap.php index 17e751f..a2db64d 100644 --- a/app/Console/Commands/CacheMorphMap.php +++ b/app/Console/Commands/CacheMorphMap.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Services\ModelFinder; +use App\Services\MorphModelFinder; use Illuminate\Console\Command; class CacheMorphMap extends Command @@ -22,20 +22,20 @@ class CacheMorphMap extends Command protected $description = 'Create a cache file for faster morph mapping'; /** - * @var ModelFinder + * @var MorphModelFinder */ - private $modelFinder; + private $morphModelFinder; /** * Create a new command instance. * - * @param ModelFinder $modelFinder + * @param MorphModelFinder $morphModelFinder */ - public function __construct(ModelFinder $modelFinder) + public function __construct(MorphModelFinder $morphModelFinder) { parent::__construct(); - $this->modelFinder = $modelFinder; + $this->morphModelFinder = $morphModelFinder; } /** @@ -45,10 +45,10 @@ class CacheMorphMap extends Command */ public function handle(): void { - $cache = $this->modelFinder->getCachePath(); + $cache = $this->morphModelFinder->getCachePath(); - $models = $this->modelFinder->getModels(); - $map = $this->modelFinder->getModelMap($models); + $models = $this->morphModelFinder->getModels(); + $map = $this->morphModelFinder->getModelMap($models); file_put_contents( $cache, diff --git a/app/Console/Commands/ClearCachedMorphMap.php b/app/Console/Commands/ClearCachedMorphMap.php index 2996222..02fcb78 100644 --- a/app/Console/Commands/ClearCachedMorphMap.php +++ b/app/Console/Commands/ClearCachedMorphMap.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Services\ModelFinder; +use App\Services\MorphModelFinder; use Illuminate\Console\Command; class ClearCachedMorphMap extends Command @@ -22,20 +22,20 @@ class ClearCachedMorphMap extends Command protected $description = 'Remove the morph map cache file'; /** - * @var ModelFinder + * @var MorphModelFinder */ - private $modelFinder; + private $morphModelFinder; /** * Create a new command instance. * - * @param ModelFinder $modelFinder + * @param MorphModelFinder $morphModelFinder */ - public function __construct(ModelFinder $modelFinder) + public function __construct(MorphModelFinder $morphModelFinder) { parent::__construct(); - $this->modelFinder = $modelFinder; + $this->morphModelFinder = $morphModelFinder; } /** @@ -45,7 +45,7 @@ class ClearCachedMorphMap extends Command */ public function handle(): void { - @unlink($this->modelFinder->getCachePath()); + @unlink($this->morphModelFinder->getCachePath()); $this->info('Morph map models cache cleared!'); } diff --git a/app/Services/ModelFinder.php b/app/Services/MorphModelFinder.php similarity index 99% rename from app/Services/ModelFinder.php rename to app/Services/MorphModelFinder.php index 60c6cb8..a66f1bc 100644 --- a/app/Services/ModelFinder.php +++ b/app/Services/MorphModelFinder.php @@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; -class ModelFinder +class MorphModelFinder { /** * Scan all model directories and automatically alias the polymorphic types of Eloquent models.