我希望能够找到 Laravel 项目中所有模型和数据库表的列表备份。
我想这样做是为了构建一个仪表盘,显示所有模型数据随时间变化的方式,例如,如果有一个用户模型,过去90天每天新增或修改了多少用户。
我建议使用PHP反射来代替依赖于所有模型都位于名为Model的命名空间或目录中的方法。
下面的代码示例收集所有应用程序类,验证它们是否实际扩展了Eloquent模型类并且不是抽象的。
<?php
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
function getModels(): Collection
{
$models = collect(File::allFiles(app_path()))
->map(function ($item) {
$path = $item->getRelativePathName();
$class = sprintf('\%s%s',
Container::getInstance()->getNamespace(),
strtr(substr($path, 0, strrpos($path, '.')), '/', '\\'));
return $class;
})
->filter(function ($class) {
$valid = false;
if (class_exists($class)) {
$reflection = new \ReflectionClass($class);
$valid = $reflection->isSubclassOf(Model::class) &&
!$reflection->isAbstract();
}
return $valid;
});
return $models->values();
}
我会浏览你的文件系统并从 models 文件夹中提取所有的php文件。我将我的模型保存在 app/Models 文件夹中,所以可能是这样的:
$path = app_path() . "/Models";
function getModels($path){
$out = [];
$results = scandir($path);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
$filename = $path . '/' . $result;
if (is_dir($filename)) {
$out = array_merge($out, getModels($filename));
}else{
$out[] = substr($filename,0,-4);
}
}
return $out;
}
dd(getModels($path));
我刚试了一下,它输出了我所有模型的完整文件路径。如果您只想查看命名空间和模型名称,您可以剥离字符串以使其只显示这些内容。
app_path('Models')
代替字符串连接, Laravel 的方式是使用File::allFiles($path)
代替自己编写那些逻辑。 - Lupinity Labs<?php
function getModelNames(): array
{
$path = app_path('Models') . '/*.php';
return collect(glob($path))->map(fn ($file) => basename($file, '.php'))->toArray();
}
get_declared_classes
并过滤返回值以获取您模型的命名空间(我的类在\App\Models
命名空间中):$models = collect(get_declared_classes())->filter(function ($item) {
return (substr($item, 0, 11) === 'App\Models\\');
});
$appNamespace = Illuminate\Container\Container::getInstance()->getNamespace();
$modelNamespace = 'Models';
$models = collect(File::allFiles(app_path($modelNamespace)))->map(function ($item) use ($appNamespace, $modelNamespace) {
$rel = $item->getRelativePathName();
$class = sprintf('\%s%s%s', $appNamespace, $modelNamespace ? $modelNamespace . '\\' : '',
implode('\\', explode('/', substr($rel, 0, strrpos($rel, '.')))));
return class_exists($class) ? $class : null;
})->filter();
$modelNamespace
是为那些在其模型中有不同文件夹和命名空间的人准备的,这是强烈推荐的做法,或者使用 Laravel 8+ 的人可以使用此功能。那些只使用 Laravel 7 或更低版本的默认设置可以将其留空,但这样会导入应用程序目录中的所有类,而不仅仅是 Eloquent 模型。
在这种情况下,您可以通过像这样过滤列表来确保仅获取模型:
$models->filter(
function ($model) {
return !is_subclass_of($model, Illuminate\Database\Eloquent\Model::class);
}
);
根据题目答案如下:
<?php
use Illuminate\Support\Facades\File;
/**
* Get Available models in app.
*
* @return array $models
*/
public static function getAvailableModels()
{
$models = [];
$modelsPath = app_path('Models');
$modelFiles = File::allFiles($modelsPath);
foreach ($modelFiles as $modelFile) {
$models[] = '\App\\' . $modelFile->getFilenameWithoutExtension();
}
return $models;
}
标准 PHP 库 (SPL)
递归列出目录中的所有文件,你可以编写以下类似的函数。function getModels($path, $namespace){
$out = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(
$path
), \RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
/**
* @var \SplFileInfo $item
*/
if($item->isReadable() && $item->isFile() && mb_strtolower($item->getExtension()) === 'php'){
$out[] = $namespace .
str_replace("/", "\\", mb_substr($item->getRealPath(), mb_strlen($path), -4));
}
}
return $out;
}
getModels(app_path("Models/"), "App\\Models\\");
这是我的解决方案。不打算在子文件夹中搜索模型。
<?php
use Illuminate\Database\Eloquent\Model;
function getModels(?string $path = null): array
{
$path = $path ?? app_path();
// Se obtienen los archivos en el directorio, sin subdirectorios.
$files = File::files($path);
// Se obtienen los nombres sin rutas ni extensiones.
$filenames = array_map(function ($file): string {
$pathinfo = pathinfo($file);
return $pathinfo['filename'];
}, $files);
$namespace = "\App";
// Se conservan aquellos que sean modelos.
$mapped = array_map(
function ($filename) use ($namespace): ?string {
$class = "{$namespace}\\{$filename}";
if (!class_exists($class)) {
return null;
}
$object = new $class;
if (!$object instanceof Model) {
return null;
}
return $class;
},
$filenames
);
// Se filtran los que no son modelos.
$models = array_filter($mapped);
return array_values($models);
}
我认为这样更好、更简单(不使用反射类)
function get_all_models(): array
{
$namespace = app()->getNamespace() . "Models\\";
$files = File::allFiles(app_path('Models'));
return collect($files)
->map(function (SplFileInfo $file) use ($namespace) {
$filePath = str_replace('.' . $file->getExtension(), '', $file->getRelativePathname());
return $namespace . str_replace('/', '\\', $filePath);
})
->filter(function ($class) {
return class_exists($class) && is_subclass_of($class, Model::class);
})->values()->toArray();
}
$valid = $reflection->isSubclassOf(Model::class)
处报错“_Class App\Models\Traits\Model does not exist_”。 - Daniyal Nasir