You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
4.3 KiB
206 lines
4.3 KiB
4 years ago
|
<?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;
|
||
|
|
||
|
|
||
4 years ago
|
class MorphModelFinder
|
||
4 years ago
|
{
|
||
|
/**
|
||
|
* 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);
|
||
|
}
|
||
|
}
|