Skip to content

Commit b5758b2

Browse files
Modular v3 (#115)
* Proper caching (#114) * wip * wip * wip * wip * Move config * Delete Cache.php * Code style * Improve event auto-discovery * Update phpunit.yml * Remove unnecessary 'getCachePath' * Code style * Update phpunit.yml * Update actions * debug * More debugging * Update phpunit.yml * Introduce Auto-discovery Plugins (#123) * Introduce Auto-discovery Plugins * Tweaks * Refactor plugin architecture Co-Authored-By: Bogdan Kharchenko <32746389+bogdankharchenko@users.noreply.github.com> * Move modules to plugin Co-Authored-By: Bogdan Kharchenko <32746389+bogdankharchenko@users.noreply.github.com> * Update plugin discovery to support dependency injection and prevent duplicate execution * Code style * Reset handled array in clearCache to prevent stale state * Add parameter name to AfterResolving attribute and use named parameters for plugin dependency injection * Update ModulesCache test assertions to use plugin-based cache structure * Implement HandlesAutodiscovery interface for plugin attribute classes and refactor bootPlugins to use polymorphic attribute handling * Drop support for Laravel versions below 8.0.0 and remove version checks * Introduce plugin-based architecture for module auto-discovery with unified caching and attribute-driven lifecycle control * Rename readData to readCacheIfExists for clarity --------- Co-authored-by: Chris Morrell <inxilpro@users.noreply.github.com> * Update MakeModule.php * Remove example stubs and use .gitkeep placeholders * Update ModuleRegistry.php * Refactor autodiscovery plugin booting to use static boot methods * Refactor autodiscovery to centralize plugin management in PluginRegistry * Extract cache operations from AutodiscoveryHelper into dedicated CacheHelper class * Refactor autodiscovery system to separate concerns into PluginDataRepository and PluginHandler * Remove unused imports and fix trailing whitespace * Move plugins * Rename HandlesAutodiscovery interface to HandlesBoot * Sort imports alphabetically * Add view and factory namespace handling in make commands * Fix code style * Handle model resolution for root namespace and fully qualified class names * Add test for Artisan command registration in modules * Add comprehensive plugin tests for module discovery and Laravel feature integration * Update phpunit.xml to version 10.5 schema and add cache directory configuration * Move livewire to separate plugin * Simplify event listener test to verify actual event dispatching and message handling * Update Blade component reference to use namespace syntax * Update PHPUnit attributes syntax and refactor gate policy tests to verify authorization behavior * Refactor MigratorPluginTest to use more concise assertions and rename migration file to match convention * Update route naming convention from dot notation to namespace syntax * Normalize whitespace in ViewPluginTest * Code style * Code style * Code style * Extract bootable attribute logic into dedicated method * Rename `cache_path` property to `path` in Cache class * Add boost support * Fix path separator handling in MigratorPluginTest for cross-platform compatibility * Update CHANGES-V3.md to reflect new plugin lifecycle architecture and class renames * Update GitHub Actions matrix strategy to use base OS names with dynamic latest suffix * Update MigratorPluginTest assertion message to include actual paths for debugging * Fix path separator handling in MigratorPluginTest assertion by normalizing backslashes to forward slashes * Remove 3.x branch from phpunit workflow triggers * Simplify CHANGES-V3.md documentation with concise formatting and tables * Extract modularize functionality to separate package dependency * Add blade component directory discovery and registration to BladePlugin * Refactor plugin registry and handler to use container singletons * Remove unused PluginHandler import from ModulesClear command * Update internachi/modularize dependency to ^1.1.0 --------- Co-authored-by: Bogdan Kharchenko <32746389+bogdankharchenko@users.noreply.github.com> Co-authored-by: Bogdan Kharchenko <scriptor@gmail.com>
1 parent e7ff407 commit b5758b2

130 files changed

Lines changed: 1847 additions & 1263 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/coverage.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313

1414
steps:
1515
- name: Checkout code
16-
uses: actions/checkout@v3
16+
uses: actions/checkout@v4
1717

1818
- name: Setup PHP
1919
uses: shivammathur/setup-php@v2
@@ -22,13 +22,16 @@ jobs:
2222
extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, intl, iconv
2323
coverage: pcov
2424

25+
- name: Get composer cache directory
26+
id: composer-cache
27+
run: |
28+
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
29+
2530
- name: Cache dependencies
26-
uses: actions/cache@v2
31+
uses: actions/cache@v4
2732
with:
28-
path: |
29-
vendor
30-
${{ steps.composer-cache-files-dir.outputs.dir }}
31-
key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
33+
path: ${{ steps.composer-cache.outputs.dir }}
34+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
3235
restore-keys: |
3336
${{ runner.os }}-composer-
3437
@@ -38,11 +41,10 @@ jobs:
3841
run: composer install --no-progress --no-interaction --prefer-dist
3942

4043
- name: Run and publish code coverage
41-
uses: paambaati/codeclimate-action@v5.0.0
44+
uses: paambaati/codeclimate-action@v9.0
4245
env:
4346
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
4447
with:
4548
coverageCommand: vendor/bin/phpunit --coverage-clover ${{ github.workspace }}/clover.xml
46-
debug: true
4749
coverageLocations:
4850
"${{github.workspace}}/clover.xml:clover"

.github/workflows/php-cs-fixer.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
name: Code Style
22

3-
on: [ pull_request, push ]
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- 3.x
8+
pull_request:
49

510
jobs:
611
coverage:
@@ -10,21 +15,25 @@ jobs:
1015

1116
steps:
1217
- name: Checkout code
13-
uses: actions/checkout@v3
18+
uses: actions/checkout@v4
1419

1520
- name: Setup PHP
1621
uses: shivammathur/setup-php@v2
1722
with:
1823
php-version: 8.3
1924
extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, intl, iconv
25+
coverage: none
26+
27+
- name: Get composer cache directory
28+
id: composer-cache
29+
run: |
30+
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
2031
2132
- name: Cache dependencies
22-
uses: actions/cache@v2
33+
uses: actions/cache@v4
2334
with:
24-
path: |
25-
vendor
26-
${{ steps.composer-cache-files-dir.outputs.dir }}
27-
key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
35+
path: ${{ steps.composer-cache.outputs.dir }}
36+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
2837
restore-keys: |
2938
${{ runner.os }}-composer-
3039

.github/workflows/phpunit.yml

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: PHPUnit
22

33
on:
44
push:
5+
branches:
6+
- main
57
pull_request:
68
schedule:
79
- cron: '0 14 * * 3' # Run Wednesdays at 2pm EST
@@ -11,19 +13,14 @@ jobs:
1113
strategy:
1214
matrix:
1315
dependency-version: [ stable, lowest ]
14-
os: [ ubuntu-latest, windows-latest ]
15-
laravel: [ 10.*, 11.*, 12.* ]
16-
php: [ 8.1, 8.2, 8.3, 8.4 ]
17-
exclude:
18-
- php: 8.1
19-
laravel: 11.*
20-
- php: 8.1
21-
laravel: 12.*
22-
23-
runs-on: ${{ matrix.os }}
16+
os: [ ubuntu, windows ]
17+
laravel: [ 11.*, 12.* ]
18+
php: [ 8.3, 8.4 ]
19+
20+
runs-on: "${{ matrix.os }}-latest"
2421
timeout-minutes: 10
2522

26-
name: "${{ matrix.php }} / ${{ matrix.laravel }} (${{ matrix.dependency-version }})"
23+
name: "${{ matrix.os }}: ${{ matrix.php }} / ${{ matrix.laravel }} (${{ matrix.dependency-version }})"
2724

2825
steps:
2926
- name: Checkout code
@@ -37,23 +34,22 @@ jobs:
3734
tools: composer:v2
3835

3936
- name: Register composer cache directory
40-
id: composer-cache-files-dir
37+
id: composer-cache
4138
run: |
42-
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
39+
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
4340
4441
- name: Cache dependencies
45-
uses: actions/cache@v3
42+
uses: actions/cache@v4
43+
if: ${{ steps.composer-cache.outputs.dir != '' }}
4644
with:
47-
path: |
48-
vendor
49-
${{ steps.composer-cache-files-dir.outputs.dir }}
50-
key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
45+
path: ${{ steps.composer-cache.outputs.dir }}
46+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
5147
restore-keys: |
5248
${{ runner.os }}-composer-
5349
5450
- name: Install dependencies
5551
run: |
56-
composer require --no-progress --no-interaction --prefer-dist --update-with-all-dependencies --prefer-${{ matrix.dependency-version }} "illuminate/support:${{ matrix.laravel }}"
52+
composer require --no-interaction --prefer-dist --update-with-all-dependencies --prefer-${{ matrix.dependency-version }} "illuminate/support:${{ matrix.laravel }}"
5753
5854
- name: Execute tests
5955
run: vendor/bin/phpunit

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
.php_cs.cache
55
.php-cs-fixer.cache
66
.composer-history
7+
.phpunit.cache
78

89
/vendor*
910
/node_modules

CHANGES-V3.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Version 3.0 Architecture Changes
2+
3+
## Overview
4+
5+
Version 3.0 introduces a **plugin-based architecture** for module auto-discovery. The monolithic `ModularServiceProvider` has been refactored into a collection of focused, composable `Plugin` classes.
6+
This improves extensibility, testability, and separation of concerns.
7+
8+
## Breaking Changes
9+
10+
### Requirements
11+
12+
- **PHP 8.3+** (was 8.0+)
13+
- **Laravel 11+** (dropped Laravel 9/10 support)
14+
15+
### Removed Features
16+
17+
| Removed | Replacement |
18+
|----------------------------------|---------------------------------------|
19+
| `ModularEventServiceProvider` | `EventsPlugin` (auto-registered) |
20+
| `AutoDiscoveryHelper` | `FinderFactory` |
21+
| `ModuleRegistry::getCachePath()` | `Cache::path()` |
22+
| `make:livewire --module` | Install `internachi/modular-livewire` |
23+
| Breadcrumbs integration | Load manually in service provider |
24+
25+
### Cache Changes
26+
27+
| Before | After |
28+
|-------------------------------|-----------------------------------|
29+
| `bootstrap/cache/modules.php` | `bootstrap/cache/app-modules.php` |
30+
| Module paths only | All plugin discovery data |
31+
32+
## Plugin Architecture
33+
34+
### Core Classes
35+
36+
```
37+
PluginRegistry → Registration point for plugins
38+
PluginHandler → Orchestrates plugin lifecycle
39+
PluginDataRepository → Manages discovery data (cached or fresh)
40+
Cache → File I/O for unified cache
41+
FinderFactory → Creates FinderCollection instances
42+
ModuleFileInfo → Decorator with module() and fullyQualifiedClassName()
43+
```
44+
45+
### Plugin Base Class
46+
47+
```php
48+
abstract class Plugin
49+
{
50+
abstract public function discover(FinderFactory $finders): iterable;
51+
abstract public function handle(Collection $data);
52+
}
53+
```
54+
55+
### PHP 8 Attributes
56+
57+
| Attribute | Behavior |
58+
|-------------------------------------|---------------------------------------------|
59+
| `#[AfterResolving(Service::class)]` | Defer until service resolved |
60+
| `#[OnBoot]` | Execute during `booting()` hook |
61+
| *(none)* | Explicit call via `PluginHandler::handle()` |
62+
63+
### Built-in Plugins
64+
65+
| Plugin | Trigger | Responsibility |
66+
|--------------------|---------------------------------|-------------------------------------------------|
67+
| `ModulesPlugin` | Eager | Discover `composer.json`, create `ModuleConfig` |
68+
| `RoutesPlugin` | `!routesAreCached()` | Load route files |
69+
| `ViewPlugin` | `AfterResolving(ViewFactory)` | Register view namespaces |
70+
| `BladePlugin` | `AfterResolving(BladeCompiler)` | Register Blade components |
71+
| `TranslatorPlugin` | `AfterResolving(Translator)` | Register translations |
72+
| `EventsPlugin` | `AfterResolving(Dispatcher)` | Discover event listeners |
73+
| `MigratorPlugin` | `AfterResolving(Migrator)` | Register migration paths |
74+
| `GatePlugin` | `AfterResolving(Gate)` | Register model policies |
75+
| `ArtisanPlugin` | `Artisan::starting()` | Register commands |
76+
77+
## Boot Flow
78+
79+
```
80+
ModularServiceProvider::register()
81+
├─ Register singletons
82+
├─ PluginRegistry::add(built-in plugins)
83+
└─ $app->booting(PluginHandler::boot)
84+
└─ For each plugin:
85+
└─ Plugin::boot(handler, app)
86+
└─ Read attributes, schedule execution
87+
```
88+
89+
## Extensibility
90+
91+
Register custom plugins in a service provider:
92+
93+
```php
94+
public function register(): void
95+
{
96+
PluginRegistry::register(MyPlugin::class);
97+
}
98+
```
99+
100+
Custom plugins:
101+
102+
- Must extend `InterNACHI\Modular\Plugins\Plugin`
103+
- Are automatically integrated into caching
104+
- Can use lifecycle attributes
105+
106+
## Laravel Integration
107+
108+
Modular 3.0 integrates with Laravel's optimize commands via the `optimizes()` method:
109+
110+
- `php artisan optimize` → runs `modules:cache`
111+
- `php artisan optimize:clear` → runs `modules:clear`
112+
113+
## Migration Checklist
114+
115+
1. Upgrade PHP to 8.3+ and Laravel to 11+
116+
2. Run `php artisan modules:clear`
117+
3. Run `composer update internachi/modular`
118+
4. If using Livewire: `composer require internachi/modular-livewire`
119+
5. Update any `AutoDiscoveryHelper` references to `FinderFactory`
120+
6. Remove any `ModularEventServiceProvider` references

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,6 @@ for your seeder within your module namespace:
195195
- `php artisan db:seed --module=[module name]` will try to call `Modules\MyModule\Database\Seeders\DatabaseSeeder`
196196
- `php artisan db:seed --class=MySeeder --module=[module name]` will try to call `Modules\MyModule\Database\Seeders\MySeeder`
197197

198-
#### Vendor Commands
199-
200-
We can also add the `--module` option to commands in 3rd-party packages. The first package
201-
that we support is Livewire. If you have Livewire installed, you can run:
202-
203-
- `php artisan make:livewire counter --module=[module name]`
204-
205198
### Blade Components
206199

207200
Your [Laravel Blade components](https://laravel.com/docs/blade#components) will be

composer.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@
1616
"type": "library",
1717
"license": "MIT",
1818
"require": {
19-
"php": ">=8.0",
19+
"php": ">=8.3",
2020
"ext-simplexml": "*",
2121
"ext-dom": "*",
2222
"composer/composer": "^2.1",
23-
"illuminate/support": "^9|^10|^11|^12|13.x-dev|dev-master|dev-main"
23+
"illuminate/support": "^11|^12|13.x-dev|dev-master|dev-main",
24+
"internachi/modularize": "^1.1.0"
2425
},
2526
"require-dev": {
26-
"orchestra/testbench": "^7.52|^8.33|^9.11|^10.0|dev-master|dev-main",
27+
"orchestra/testbench": "^9.11|^10.0|dev-master|dev-main",
2728
"friendsofphp/php-cs-fixer": "^3.14",
2829
"mockery/mockery": "^1.5",
29-
"phpunit/phpunit": "^9.5|^10.5|^11.5",
30-
"ext-json": "*",
31-
"livewire/livewire": "^2.5|^3.0"
30+
"phpunit/phpunit": "^10.5|^11.5",
31+
"ext-json": "*"
3232
},
3333
"autoload": {
3434
"psr-4": {
@@ -48,8 +48,7 @@
4848
"laravel": {
4949
"providers": [
5050
"InterNACHI\\Modular\\Support\\ModularServiceProvider",
51-
"InterNACHI\\Modular\\Support\\ModularizedCommandsServiceProvider",
52-
"InterNACHI\\Modular\\Support\\ModularEventServiceProvider"
51+
"InterNACHI\\Modular\\Support\\ModularizedCommandsServiceProvider"
5352
],
5453
"aliases": {
5554
"Modules": "InterNACHI\\Modular\\Support\\Facades\\Modules"
File renamed without changes.

phpunit.xml

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
bootstrap="vendor/autoload.php"
5-
colors="true"
6-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
7-
>
8-
<coverage processUncoveredFiles="true">
9-
<include>
10-
<directory suffix=".php">./src</directory>
11-
</include>
12-
</coverage>
13-
<testsuites>
14-
<testsuite name="Tests">
15-
<directory>./tests</directory>
16-
</testsuite>
17-
</testsuites>
18-
<php>
19-
<env name="APP_ENV" value="testing"/>
20-
<env name="BCRYPT_ROUNDS" value="4"/>
21-
<env name="CACHE_DRIVER" value="array"/>
22-
<env name="SESSION_DRIVER" value="array"/>
23-
<env name="QUEUE_DRIVER" value="sync"/>
24-
<env name="MAIL_DRIVER" value="array"/>
25-
<env name="TERMINAL_EMULATOR" value="JetBrains-JediTerm"/>
26-
</php>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache">
3+
<testsuites>
4+
<testsuite name="Tests">
5+
<directory>./tests</directory>
6+
</testsuite>
7+
</testsuites>
8+
<php>
9+
<env name="APP_ENV" value="testing"/>
10+
<env name="BCRYPT_ROUNDS" value="4"/>
11+
<env name="CACHE_DRIVER" value="array"/>
12+
<env name="SESSION_DRIVER" value="array"/>
13+
<env name="QUEUE_DRIVER" value="sync"/>
14+
<env name="MAIL_DRIVER" value="array"/>
15+
<env name="TERMINAL_EMULATOR" value="JetBrains-JediTerm"/>
16+
</php>
17+
<source>
18+
<include>
19+
<directory suffix=".php">./src</directory>
20+
</include>
21+
</source>
2722
</phpunit>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## Modular
2+
3+
- This a modular application. Each module is located in its own directory inside of `app-modules`.
4+
- IMPORTANT: Activate `modular` every time you're working with or creating a new module.

0 commit comments

Comments
 (0)