Skip to content

Instantly share code, notes, and snippets.

@mathieutu
Created November 15, 2020 00:51
Show Gist options
  • Save mathieutu/a5f1d2df288d1157f6291d8be1bea5b7 to your computer and use it in GitHub Desktop.
Save mathieutu/a5f1d2df288d1157f6291d8be1bea5b7 to your computer and use it in GitHub Desktop.
List Laravel models attributes and relations.
<?php
namespace App\Console\Commands;
use App\Helpers\RelationsInModelFinder;
use App\Models\Model;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\VarExporter\VarExporter;
class ListeModelsAttributesAndRelations extends Command
{
protected $signature = 'gen:model-fields';
protected $description = 'Generate Models Attributes And Relations.';
public function handle()
{
$models = $this->listModels();
$fields = $models->mapWithKeys(fn(string $modelClass) => [
$modelClass => [
'attributes' => $this->getDatabaseAttributes($modelClass),
'relations' => $this->getRelations($modelClass),
],
])->toArray();
$export = VarExporter::export($fields);
File::put(config_path('models.php'), "<?php \n return $export;");
}
private function listModels(): \Illuminate\Support\Collection
{
return collect(File::allFiles(app_path('Models')))
->map(function (SplFileInfo $item) {
$path = $item->getRelativePathName();
return sprintf('%sModels\%s',
$this->getLaravel()->getNamespace(),
str_replace( '/', '\\', substr($path, 0, strrpos($path, '.'))));
})
->filter(function (string $class) {
if (class_exists($class)) {
$reflection = new \ReflectionClass($class);
return $reflection->isSubclassOf(Model::class) && !$reflection->isAbstract();
}
return false;
})
->values();
}
private function getDatabaseAttributes(string $modelClass): array
{
return Schema::getColumnListing((new $modelClass())->getTable());
}
private function getRelations(string $modelClass): array
{
return RelationsInModelFinder::find($modelClass);
}
}
<?php
namespace App\Helpers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class RelationsInModelFinder
{
private Model $model;
private array $relationsType;
public function __construct(Model $model, array $relationsType)
{
$this->model = $model;
$this->relationsType = $relationsType;
}
public static function find(string $modelClass): array
{
return (new static(new $modelClass(), ['hasMany', 'hasManyThrough', 'hasOneThrough', 'belongsToMany', 'hasOne', 'belongsTo', 'morphOne', 'morphTo', 'morphMany', 'morphToMany', 'morphedByMany']))->_find();
}
protected function _find(): array
{
return collect(get_class_methods($this->model))->sort()
->reject(function ($method) {
return $this->isAnEloquentMethod($method);
})->filter(function ($method) {
$code = $this->getMethodCode($method);
return collect($this->relationsType)->contains(function ($relation) use ($code) {
return Str::contains($code, '$this->' . $relation . '(');
});
})->values()->toArray();
}
protected function isAnEloquentMethod($method): bool
{
return Str::startsWith($method, 'get') ||
Str::startsWith($method, 'set') ||
Str::startsWith($method, 'scope') ||
method_exists(Model::class, $method);
}
protected function getMethodCode($method): string
{
$reflection = new \ReflectionMethod($this->model, $method);
$file = new \SplFileObject($reflection->getFileName());
$file->seek($reflection->getStartLine() - 1);
$code = '';
while ($file->key() < $reflection->getEndLine()) {
$code .= $file->current();
$file->next();
}
$code = trim(preg_replace('/\s\s+/', '', $code));
return $code;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment