Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/Support/Autodiscovery/ArtisanPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Console\Application as Artisan;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;
use InterNACHI\Modular\Support\FinderFactory;
use InterNACHI\Modular\Support\ModuleConfig;
use InterNACHI\Modular\Support\ModuleFileInfo;
use InterNACHI\Modular\Support\ModuleRegistry;
use ReflectionClass;

class ArtisanPlugin extends Plugin
{
public function __construct(
protected Artisan $artisan,
protected ModuleRegistry $registry,
) {
}

public function discover(FinderFactory $finders): iterable
{
return $finders
->commandFileFinder()
->withModuleInfo()
->values()
->map(fn(ModuleFileInfo $file) => $file->fullyQualifiedClassName())
->filter($this->isInstantiableCommand(...));
}

public function handle(Collection $data): void
{
$data->each(fn(string $fqcn) => $this->artisan->resolve($fqcn));

$this->registerNamespacesInTinker();
}

protected function registerNamespacesInTinker(): void
{
if (! class_exists('Laravel\\Tinker\\TinkerServiceProvider')) {
return;
}

$namespaces = $this->registry
->modules()
->flatMap(fn(ModuleConfig $config) => $config->namespaces)
->reject(fn($ns) => Str::endsWith($ns, ['Tests\\', 'Database\\Factories\\', 'Database\\Seeders\\']))
->values()
->all();

Config::set('tinker.alias', array_merge($namespaces, Config::get('tinker.alias', [])));
}

protected function isInstantiableCommand($command): bool
{
return is_subclass_of($command, Command::class)
&& ! (new ReflectionClass($command))->isAbstract();
}
}
14 changes: 14 additions & 0 deletions src/Support/Autodiscovery/Attributes/AfterResolving.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class AfterResolving
{
public function __construct(
public string $abstract
) {
}
}
10 changes: 10 additions & 0 deletions src/Support/Autodiscovery/Attributes/OnBoot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class OnBoot
{
}
35 changes: 35 additions & 0 deletions src/Support/Autodiscovery/BladePlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Support\Collection;
use Illuminate\View\Compilers\BladeCompiler;
use InterNACHI\Modular\Support\Autodiscovery\Attributes\AfterResolving;
use InterNACHI\Modular\Support\FinderFactory;
use InterNACHI\Modular\Support\ModuleFileInfo;

#[AfterResolving(BladeCompiler::class)]
class BladePlugin extends Plugin
{
public function __construct(
protected BladeCompiler $blade
) {
}

public function discover(FinderFactory $finders): iterable
{
return $finders
->bladeComponentFileFinder()
->withModuleInfo()
->values()
->map(fn(ModuleFileInfo $component) => [
'prefix' => $component->module()->name,
'fqcn' => $component->fullyQualifiedClassName(),
]);
}

public function handle(Collection $data)
{
$data->each(fn(array $row) => $this->blade->component($row['fqcn'], null, $row['prefix']));
}
}
61 changes: 61 additions & 0 deletions src/Support/Autodiscovery/EventsPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Support\Providers\EventServiceProvider;
use Illuminate\Support\Collection;
use InterNACHI\Modular\Support\Autodiscovery\Attributes\AfterResolving;
use InterNACHI\Modular\Support\DiscoverEvents;
use InterNACHI\Modular\Support\FinderFactory;
use InterNACHI\Modular\Support\ModuleFileInfo;

#[AfterResolving(Dispatcher::class)]
class EventsPlugin extends Plugin
{
public function __construct(
protected Application $app,
protected Dispatcher $events,
protected Repository $config,
) {
}

public function discover(FinderFactory $finders): array
{
if (! $this->shouldDiscoverEvents()) {
return [];
}

return $finders
->listenerDirectoryFinder()
->withModuleInfo()
->reduce(fn(array $discovered, ModuleFileInfo $file) => array_merge_recursive(
$discovered,
DiscoverEvents::within($file->getPathname(), $file->module()->path('src'))
), []);
}

public function handle(Collection $data): void
{
$data->each(function(array $listeners, string $event) {
foreach (array_unique($listeners, SORT_REGULAR) as $listener) {
$this->events->listen($event, $listener);
}
});
}

protected function shouldDiscoverEvents(): bool
{
return $this->config->get('app-modules.should_discover_events') ?? $this->appIsConfiguredToDiscoverEvents();
}

protected function appIsConfiguredToDiscoverEvents(): bool
{
return collect($this->app->getProviders(EventServiceProvider::class))
->filter(fn(EventServiceProvider $provider) => $provider::class === EventServiceProvider::class
|| str_starts_with(get_class($provider), $this->app->getNamespace()))
->contains(fn(EventServiceProvider $provider) => $provider->shouldDiscoverEvents());
}
}
53 changes: 53 additions & 0 deletions src/Support/Autodiscovery/GatePlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use InterNACHI\Modular\Support\Autodiscovery\Attributes\AfterResolving;
use InterNACHI\Modular\Support\FinderFactory;
use InterNACHI\Modular\Support\ModuleFileInfo;

#[AfterResolving(Gate::class)]
class GatePlugin extends Plugin
{
public function __construct(
protected Gate $gate
) {
}

public function discover(FinderFactory $finders): iterable
{
return $finders
->modelFileFinder()
->withModuleInfo()
->values()
->map(function(ModuleFileInfo $file) {
$fqcn = $file->fullyQualifiedClassName();
$namespace = rtrim($file->module()->namespaces->first(), '\\');

$candidates = [
$namespace.'\\Policies\\'.Str::after($fqcn, 'Models\\').'Policy', // Policies/Foo/BarPolicy
$namespace.'\\Policies\\'.Str::afterLast($fqcn, '\\').'Policy', // Policies/BarPolicy
];

foreach ($candidates as $candidate) {
if (class_exists($candidate)) {
return [
'fqcn' => $fqcn,
'policy' => $candidate,
];
}
}

return null;
})
->filter();
}

public function handle(Collection $data): void
{
$data->each(fn(array $row) => $this->gate->policy($row['fqcn'], $row['policy']));
}
}
43 changes: 43 additions & 0 deletions src/Support/Autodiscovery/LivewirePlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use InterNACHI\Modular\Support\FinderFactory;
use InterNACHI\Modular\Support\ModuleFileInfo;
use Livewire\LivewireManager;

class LivewirePlugin extends Plugin
{
public function __construct(
protected LivewireManager $livewire,
) {
}

public function discover(FinderFactory $finders): iterable
{
return $finders
->livewireComponentFileFinder()
->withModuleInfo()
->values()
->map(fn(ModuleFileInfo $file) => [
'name' => sprintf(
'%s::%s',
$file->module()->name,
Str::of($file->getRelativePath())
->explode('/')
->filter()
->push($file->getBasename('.php'))
->map([Str::class, 'kebab'])
->implode('.')
),
'fqcn' => $file->fullyQualifiedClassName(),
]);
}

public function handle(Collection $data): void
{
$data->each(fn(array $d) => $this->livewire->component($d['name'], $d['fqcn']));
}
}
31 changes: 31 additions & 0 deletions src/Support/Autodiscovery/MigratorPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Database\Migrations\Migrator;
use Illuminate\Support\Collection;
use InterNACHI\Modular\Support\Autodiscovery\Attributes\AfterResolving;
use InterNACHI\Modular\Support\FinderFactory;
use Symfony\Component\Finder\SplFileInfo;

#[AfterResolving(Migrator::class)]
class MigratorPlugin extends Plugin
{
public function __construct(
protected Migrator $migrator
) {
}

public function discover(FinderFactory $finders): iterable
{
return $finders
->migrationDirectoryFinder()
->values()
->map(fn(SplFileInfo $file) => $file->getRealPath());
}

public function handle(Collection $data): void
{
$data->each(fn(string $path) => $this->migrator->path($path));
}
}
13 changes: 13 additions & 0 deletions src/Support/Autodiscovery/Plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Support\Collection;
use InterNACHI\Modular\Support\FinderFactory;

abstract class Plugin
{
abstract public function discover(FinderFactory $finders): iterable;

abstract public function handle(Collection $data);
}
43 changes: 43 additions & 0 deletions src/Support/Autodiscovery/PluginRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Container\Container;

class PluginRegistry
{
protected array $plugins = [];

public static function instance(): self
{
$container = Container::getInstance();

if (! $container->has(static::class)) {
$container->instance(static::class, new self());
}

return $container->make(static::class);
}

/** @param class-string<\InterNACHI\Modular\Support\Autodiscovery\Plugin> ...$class */
public static function register(string ...$class): void
{
static::instance()->add(...$class);
}

/** @param class-string<\InterNACHI\Modular\Support\Autodiscovery\Plugin> ...$class */
public function add(string ...$class): static
{
foreach ($class as $fqcn) {
$this->plugins[] = $fqcn;
}

return $this;
}

/** @return class-string<\InterNACHI\Modular\Support\Autodiscovery\Plugin>[] */
public function all(): array
{
return $this->plugins;
}
}
23 changes: 23 additions & 0 deletions src/Support/Autodiscovery/RoutesPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace InterNACHI\Modular\Support\Autodiscovery;

use Illuminate\Support\Collection;
use InterNACHI\Modular\Support\FinderFactory;
use Symfony\Component\Finder\SplFileInfo;

class RoutesPlugin extends Plugin
{
public function discover(FinderFactory $finders): iterable
{
return $finders
->routeFileFinder()
->values()
->map(fn(SplFileInfo $file) => $file->getRealPath());
}

public function handle(Collection $data): void
{
$data->each(fn(string $filename) => require $filename);
}
}
Loading
Loading