Skip to content

Commit

Permalink
Create PowerGrid Components in different directories following Livewi…
Browse files Browse the repository at this point in the history
…re Class Namespace (#1501)

* Create components following a custom Livewire namespace

* Add Auto-Discover Models config key

* List sub-classes of Model in path
  • Loading branch information
dansysanalyst authored Apr 13, 2024
1 parent 4436e3d commit 1581c97
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 22 deletions.
15 changes: 15 additions & 0 deletions resources/config/livewire-powergrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,19 @@
'csv' => \PowerComponents\LivewirePowerGrid\Components\Exports\OpenSpout\v3\ExportToCsv::class,
],
],

/*
|--------------------------------------------------------------------------
| Auto-Discover Models
|--------------------------------------------------------------------------
|
| PowerGrid will search for Models in the directories listed below.
| These Models be listed as options when you run the
| "artisan powergrid:create" command.
|
*/

'auto_discover_models_paths' => [
app_path('Models'),
],
];
2 changes: 1 addition & 1 deletion src/Actions/AskModelName.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static function handle(): array
{
while (self::$model === '') {
self::setModel(suggest(
label: 'Select a Model or enter its Name/FQN class.',
label: 'Select a Model or enter its Fully qualified name.',
options: ListModels::handle(),
required: true,
));
Expand Down
29 changes: 20 additions & 9 deletions src/Actions/ListModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,33 @@
final class ListModels
{
/**
* List files in Models folder
* List files in Models
*
*/
public static function handle(): array
{
$modelsFolder = app_path('Models');
$directories = config('livewire-powergrid.auto_discover_models_paths', [app_path('Models')]);

return collect(File::allFiles($modelsFolder))
/** @var Array<int,string> $directories */
return collect($directories)
->filter(fn (string $directory) => File::exists($directory))
->map(fn (string $directory) => File::allFiles($directory))
->flatten()
->reject(fn (SplFileInfo $file): bool => $file->getExtension() != 'php')
->map(function (SplFileInfo $file): array {
return [
'file' => $file->getFilenameWithoutExtension(),
'path' => 'App\\Models\\' . $file->getFilenameWithoutExtension(),
];

// Get FQN Class from source code
/** @phpstan-ignore-next-line */
->map(function (SplFileInfo $file): string {
$sourceCode = strval(file_get_contents($file->getPathname()));

return rescue(fn () => ParseFqnClassInCode::handle($sourceCode), '');
})
->flatten()
//Remove all unqualified PHP files code
->filter()

// Remove classes that do not extend an Eloquent Model
/** @phpstan-ignore-next-line */
->reject(fn (string $fqnClass) => rescue(fn () => (new \ReflectionClass($fqnClass))->isSubclassOf(\Illuminate\Database\Eloquent\Model::class), false) === false)
->toArray();
}
}
20 changes: 20 additions & 0 deletions src/Actions/ParseFqnClassInCode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace PowerComponents\LivewirePowerGrid\Actions;

final class ParseFqnClassInCode
{
/**
* Parse namespace from PHP source code
* Inspired by: https://gist.github.com/ludofleury/1886076
* @throws \Exception
*/
public static function handle(string $sourceCode): string
{
if (preg_match('#^namespace\s+(.+?);.*class\s+(\w+).+;$#sm', $sourceCode, $matches)) {
return $matches[1] . '\\' . $matches[2];
}

throw new \Exception('could not find a FQN Class is source-code');
}
}
4 changes: 2 additions & 2 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ function convertObjectsToArray(array $data): array
if (!function_exists('powergrid_components_path')) {
function powergrid_components_path(string $filename = ''): string
{
return app_path(
return base_path(
str(strval(config('livewire.class_namespace')))
->ltrim('App//')
->replace('App', 'app')
->append(DIRECTORY_SEPARATOR . $filename)
->replace('\\', '/')
->replace('//', '/')
Expand Down
28 changes: 18 additions & 10 deletions tests/Feature/Actions/ListModelsTest.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
<?php

use Illuminate\Support\Facades\File;
<?php

use PowerComponents\LivewirePowerGrid\Actions\ListModels;

beforeEach(function () {
File::cleanDirectory(base_path('app/Models'));

$this->artisan('make:model Demo');
});
it('list all Eloquent Models in a directory', function () {
app()->config->set('livewire-powergrid.auto_discover_models_paths', [
'tests/Concerns/Models',
]);

test('list models', function () {
expect(ListModels::handle())->toBe(
[
'Demo',
'App\Models\Demo',
'PowerComponents\LivewirePowerGrid\Tests\Concerns\Models\Category',
'PowerComponents\LivewirePowerGrid\Tests\Concerns\Models\Chef',
'PowerComponents\LivewirePowerGrid\Tests\Concerns\Models\Dish',
'PowerComponents\LivewirePowerGrid\Tests\Concerns\Models\Restaurant',
]
);
});

it('will not list non-Eloquent Models', function () {
app()->config->set('livewire-powergrid.auto_discover_models_paths', [
'tests/Concerns/Enums', //There are no models in this directory.

]);

expect(ListModels::handle())->toBe([]);
});
25 changes: 25 additions & 0 deletions tests/Feature/Actions/ParseFqnClassInCodeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use PowerComponents\LivewirePowerGrid\Actions\ParseFqnClassInCode;

it('can find the namespace in a PHP file source code', function () {
$code = <<<EOD
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class DemoModel extends Model
{
use HasFactory;
}
EOD;

expect(ParseFqnClassInCode::handle($code))->toBe('App\Models\DemoModel');
});

it('throws an exception when namespace cannot be found', function () {
ParseFqnClassInCode::handle('foobar');
})->throws('could not find a FQN Class is source-code');
20 changes: 20 additions & 0 deletions tests/Feature/Support/PowerGridMakerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,23 @@
]],
],
);

test('Livewire class namespace is App\Livewire')
->expect(fn () => config('livewire.class_namespace'))->toBe('App\Livewire');

it('can create component in a custom Livewire namespace', function () {
app()->config->set('livewire.class_namespace', 'Domains');

$component = PowerGridComponentMaker::make('System/Office/Users/Admin/Active/ListTable');

expect($component)
->name->toBe('ListTable')
->namespace->toBe('Domains\System\Office\Users\Admin\Active')
->folder->toBe('System\Office\Users\Admin\Active')
->fqn->toBe('Domains\System\Office\Users\Admin\Active\ListTable')
->htmlTag->toBe('<livewire:system.office.users.admin.active.list-table/>');

expect($component->createdPath())->toBe("Domains/System/Office/Users/Admin/Active/ListTable.php");

expect($component->savePath($component->filename))->toEndWith(str_replace('/', DIRECTORY_SEPARATOR, 'Domains/System/Office/Users/Admin/Active/ListTable.php'));
});

0 comments on commit 1581c97

Please sign in to comment.