commit
e36fdf4231
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\MorphModelFinder;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CacheMorphMap extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'morphmap:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a cache file for faster morph mapping';
|
||||
|
||||
/**
|
||||
* @var MorphModelFinder
|
||||
*/
|
||||
private $morphModelFinder;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param MorphModelFinder $morphModelFinder
|
||||
*/
|
||||
public function __construct(MorphModelFinder $morphModelFinder)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->morphModelFinder = $morphModelFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$cache = $this->morphModelFinder->getCachePath();
|
||||
|
||||
$models = $this->morphModelFinder->getModels();
|
||||
$map = $this->morphModelFinder->getModelMap($models);
|
||||
|
||||
file_put_contents(
|
||||
$cache,
|
||||
'<?php return ' . var_export($map, true) . ';'
|
||||
);
|
||||
|
||||
$this->info('Morph map models cached!');
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\MorphModelFinder;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ClearCachedMorphMap extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'morphmap:clear';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove the morph map cache file';
|
||||
|
||||
/**
|
||||
* @var MorphModelFinder
|
||||
*/
|
||||
private $morphModelFinder;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param MorphModelFinder $morphModelFinder
|
||||
*/
|
||||
public function __construct(MorphModelFinder $morphModelFinder)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->morphModelFinder = $morphModelFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
@unlink($this->morphModelFinder->getCachePath());
|
||||
|
||||
$this->info('Morph map models cache cleared!');
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use ReflectionClass;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
|
||||
class MorphModelFinder
|
||||
{
|
||||
/**
|
||||
* Scan all model directories and automatically alias the polymorphic types of Eloquent models.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map(): void
|
||||
{
|
||||
if ($this->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);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue