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); + } +}