Refactor code structure for improved readability and maintainability

This commit is contained in:
Adrian Mejias committed 2026-05-14 08:59:57 -05:00
1 parent 191239d6fc
commit 561e746cab
21 files changed
+779 -695

No files matched your search

@@ -94,7 +94,7 @@ Check sibling files, related controllers, models, or tests for established patte
### 9. Queue & Job Patterns → `rules/queue-jobs.md` ### 9. Queue & Job Patterns → `rules/queue-jobs.md`
- `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]` - `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]`
- `ShouldBeUnique` to prevent duplicates; `WithoutOverlapping::untilProcessing()` for concurrency - `ShouldBeUnique` to prevent duplicates; `ShouldBeUniqueUntilProcessing` for early lock release
- Always implement `failed()`; with `retryUntil()`, set `$tries = 0` - Always implement `failed()`; with `retryUntil()`, set `$tries = 0`
- `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs - `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs
- Horizon for complex multi-queue scenarios - Horizon for complex multi-queue scenarios
@@ -82,7 +82,7 @@ $this->app->bind(PaymentGateway::class, StripeGateway::class);
## Default Sort by Descending ## Default Sort by Descending
When no explicit order is specified, sort by `id` or `created_at` descending. Explicit ordering prevents cross-database inconsistencies between MySQL and Postgres. When no explicit order is specified, sort by `id` or `created_at` descending. Without an explicit `ORDER BY`, row order is undefined.
Incorrect: Incorrect:
```php ```php
@@ -2,7 +2,7 @@
## Use `Cache::remember()` Instead of Manual Get/Put ## Use `Cache::remember()` Instead of Manual Get/Put
Atomic pattern prevents race conditions and removes boilerplate. Cleaner cache-aside pattern that removes boilerplate. use `Cache::lock()` for race conditions.
Incorrect: Incorrect:
```php ```php
@@ -2,7 +2,7 @@
## `env()` Only in Config Files ## `env()` Only in Config Files
Direct `env()` calls return `null` when config is cached. Direct `env()` calls may return `null` when config is cached.
Incorrect: Incorrect:
```php ```php
@@ -29,7 +29,11 @@ class InvoicePaid extends Notification implements ShouldQueue
## Use `afterCommit()` on Notifications in Transactions ## Use `afterCommit()` on Notifications in Transactions
Same race condition as events — the queued notification job may run before the transaction commits. Same race condition as events — call `afterCommit()` to delay dispatch until the transaction commits.
```php
$user->notify((new InvoicePaid($invoice))->afterCommit());
```
## Route Notification Channels to Dedicated Queues ## Route Notification Channels to Dedicated Queues
@@ -52,7 +52,7 @@ $response = Http::retry([100, 500, 1000])
Only retry on specific errors: Only retry on specific errors:
```php ```php
$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) { $response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) {
return $exception instanceof ConnectionException return $exception instanceof ConnectionException
|| ($exception instanceof RequestException && $exception->response->serverError()); || ($exception instanceof RequestException && $exception->response->serverError());
})->post('https://api.example.com/data'); })->post('https://api.example.com/data');
@@ -10,7 +10,7 @@ A queued mailable dispatched inside a transaction may process before the commit.
## Use `assertQueued()` Not `assertSent()` for Queued Mailables ## Use `assertQueued()` Not `assertSent()` for Queued Mailables
`Mail::assertSent()` only catches synchronous mail. Queued mailables silently pass `assertSent`, giving false confidence. `Mail::assertSent()` only catches synchronous mail. Queued mailables fail `assertSent` with a "Did you mean to use assertQueued()?" hint.
Incorrect: `Mail::assertSent(OrderShipped::class);` when mailable implements `ShouldQueue`. Incorrect: `Mail::assertSent(OrderShipped::class);` when mailable implements `ShouldQueue`.
@@ -106,25 +106,23 @@ When using time-based retry limits, set `$tries = 0` to avoid premature failure.
```php ```php
public $tries = 0; public $tries = 0;
public function retryUntil(): DateTime public function retryUntil(): \DateTimeInterface
{ {
return now()->addHours(4); return now()->addHours(4);
} }
``` ```
## Use `WithoutOverlapping::untilProcessing()` ## Use `ShouldBeUniqueUntilProcessing` for Early Lock Release
Prevents concurrent execution while allowing new instances to queue. `ShouldBeUnique` holds the lock until the job completes. `ShouldBeUniqueUntilProcessing` releases it when processing starts, allowing new instances to queue.
```php ```php
public function middleware(): array class UpdateSearchIndex implements ShouldQueue, ShouldBeUniqueUntilProcessing
{ {
return [new WithoutOverlapping($this->product->id)->untilProcessing()]; // Lock releases when processing begins, not when it finishes
} }
``` ```
Without `untilProcessing()`, the lock extends through queue wait time. With it, the lock releases when processing starts.
## Use Horizon for Complex Queue Scenarios ## Use Horizon for Complex Queue Scenarios
Use Laravel Horizon when you need monitoring, auto-scaling, failure tracking, or multiple queues with different priorities. Use Laravel Horizon when you need monitoring, auto-scaling, failure tracking, or multiple queues with different priorities.
@@ -36,7 +36,8 @@ Use `Route::resource()` or `apiResource()` for RESTful endpoints.
```php ```php
Route::resource('posts', PostController::class); Route::resource('posts', PostController::class);
Route::apiResource('api/posts', Api\PostController::class); // In routes/api.php — the /api prefix is applied automatically
Route::apiResource('posts', Api\PostController::class);
``` ```
## Keep Controllers Thin ## Keep Controllers Thin
@@ -32,7 +32,7 @@ Use policies or gates in controllers. Never skip authorization.
Incorrect: Incorrect:
```php ```php
public function update(Request $request, Post $post) public function update(UpdatePostRequest $request, Post $post)
{ {
$post->update($request->validated()); $post->update($request->validated());
} }
@@ -90,7 +90,7 @@ Correct:
## CSRF Protection ## CSRF Protection
Include `@csrf` in all POST/PUT/DELETE Blade forms. Not needed in Inertia. Include `@csrf` in all POST/PUT/DELETE Blade forms. In Inertia apps, the `@csrf` directive is automatically applied.
Incorrect: Incorrect:
```blade ```blade
@@ -121,7 +121,7 @@ Route::post('/login', LoginController::class)->middleware('throttle:login');
## Validate File Uploads ## Validate File Uploads
Validate MIME type, extension, and size. Never trust client-provided filenames. Validate extension, MIME type, and size. The `mimes` rule checks extensions; use `mimetypes` for actual MIME type validation. Never trust client-provided filenames.
```php ```php
public function rules(): array public function rules(): array
Binary file not shown.
@@ -2,7 +2,7 @@
## Use `LazilyRefreshDatabase` Over `RefreshDatabase` ## Use `LazilyRefreshDatabase` Over `RefreshDatabase`
`RefreshDatabase` runs all migrations every test run even when the schema hasn't changed. `LazilyRefreshDatabase` only migrates when needed, significantly speeding up large suites. `RefreshDatabase` migrates once per process and wraps each test in a rolled-back transaction. `LazilyRefreshDatabase` skips even that first migration if the schema is already up to date.
## Use Model Assertions Over Raw Database Assertions ## Use Model Assertions Over Raw Database Assertions
+1 -1
View File
@@ -24,7 +24,7 @@ search-docs(['mcp tools', 'mcp resources', 'mcp validation'])
### Artisan Commands ### Artisan Commands
Create MCP Primitives" Create MCP Primitives
```bash ```bash
php artisan make:mcp-tool ToolName php artisan make:mcp-tool ToolName
php artisan make:mcp-resource ResourceName php artisan make:mcp-resource ResourceName
+11 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: pest-testing name: pest-testing
description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code." description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code."
license: MIT license: MIT
metadata: metadata:
author: laravel author: laravel
@@ -18,6 +18,12 @@ Use `search-docs` for detailed Pest 4 patterns and documentation.
All tests must be written using Pest. Use `php artisan make:test --pest {name}`. All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
The `{name}` argument should include only the path and test name, but should not include the test suite.
- Incorrect: `php artisan make:test --pest Feature/SomeFeatureTest` will generate `tests/Feature/Feature/SomeFeatureTest.php`
- Correct: `php artisan make:test --pest SomeControllerTest` will generate `tests/Feature/SomeControllerTest.php`
- Incorrect: `php artisan make:test --pest --unit Unit/SomeServiceTest` will generate `tests/Unit/Unit/SomeServiceTest.php`
- Correct: `php artisan make:test --pest --unit SomeServiceTest` will generate `tests/Unit/SomeServiceTest.php`
### Test Organization ### Test Organization
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories. - Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
@@ -26,6 +32,8 @@ All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
### Basic Test Structure ### Basic Test Structure
Pest supports both `test()` and `it()` functions. Before writing new tests, check existing test files in the same directory to match the project's convention. Use `test()` if existing tests use `test()`, or `it()` if they use `it()`.
<!-- Basic Pest Test Example --> <!-- Basic Pest Test Example -->
```php ```php
it('is true', function () { it('is true', function () {
@@ -154,4 +162,5 @@ arch('controllers')
- Using `assertStatus(200)` instead of `assertSuccessful()` - Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests - Forgetting datasets for repetitive validation tests
- Deleting tests without approval - Deleting tests without approval
- Forgetting `assertNoJavaScriptErrors()` in browser tests - Forgetting `assertNoJavaScriptErrors()` in browser tests
- Prefixing `Feature/` or `Unit/` in `{name}` when using `make:test`
+3
View File
@@ -0,0 +1,3 @@
ignore-scripts=true
audit=false
fund=false
+8 -6
View File
@@ -23,12 +23,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
## Skills Activation ## Skills Activation
This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck. This project has domain-specific skills available in `**/skills/**`. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
- `laravel-best-practices` — Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns.
- `mcp-development` — Use this skill for Laravel MCP development only. Trigger when creating or editing MCP tools, resources, prompts, or servers in Laravel projects. Covers: artisan make:mcp-* generators, mcp:inspector, routes/ai.php, Tool/Resource/Prompt classes, schema validation, shouldRegister(), OAuth setup, URI templates, read-only attributes, and MCP debugging. Do not use for non-Laravel MCP projects or generic AI features without MCP.
- `pest-testing` — Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code.
- `tailwindcss-development` — Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS.
## Conventions ## Conventions
@@ -107,6 +102,12 @@ This project has domain-specific skills available. You MUST activate the relevan
- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic. - Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
- Use array shape type definitions in PHPDoc blocks. - Use array shape type definitions in PHPDoc blocks.
=== deployments rules ===
# Deployment
- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.
=== tests rules === === tests rules ===
# Test Enforcement # Test Enforcement
@@ -181,6 +182,7 @@ This project has domain-specific skills available. You MUST activate the relevan
## Pest ## Pest
- This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`. - This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`.
- The `{name}` argument should not include the test suite directory. Use `php artisan make:test --pest SomeFeatureTest` instead of `php artisan make:test --pest Feature/SomeFeatureTest`.
- Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`. - Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`.
- Do NOT delete tests without approval. - Do NOT delete tests without approval.
+8 -6
View File
@@ -23,12 +23,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
## Skills Activation ## Skills Activation
This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck. This project has domain-specific skills available in `**/skills/**`. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
- `laravel-best-practices` — Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns.
- `mcp-development` — Use this skill for Laravel MCP development only. Trigger when creating or editing MCP tools, resources, prompts, or servers in Laravel projects. Covers: artisan make:mcp-* generators, mcp:inspector, routes/ai.php, Tool/Resource/Prompt classes, schema validation, shouldRegister(), OAuth setup, URI templates, read-only attributes, and MCP debugging. Do not use for non-Laravel MCP projects or generic AI features without MCP.
- `pest-testing` — Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code.
- `tailwindcss-development` — Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS.
## Conventions ## Conventions
@@ -107,6 +102,12 @@ This project has domain-specific skills available. You MUST activate the relevan
- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic. - Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
- Use array shape type definitions in PHPDoc blocks. - Use array shape type definitions in PHPDoc blocks.
=== deployments rules ===
# Deployment
- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.
=== tests rules === === tests rules ===
# Test Enforcement # Test Enforcement
@@ -181,6 +182,7 @@ This project has domain-specific skills available. You MUST activate the relevan
## Pest ## Pest
- This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`. - This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`.
- The `{name}` argument should not include the test suite directory. Use `php artisan make:test --pest SomeFeatureTest` instead of `php artisan make:test --pest Feature/SomeFeatureTest`.
- Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`. - Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`.
- Do NOT delete tests without approval. - Do NOT delete tests without approval.
+5 -1
View File
@@ -5,7 +5,7 @@
/** /**
* A helper file for Laravel, to provide autocomplete information to your IDE * A helper file for Laravel, to provide autocomplete information to your IDE
* Generated for Laravel 12.56.0. * Generated for Laravel 12.58.0.
* *
* This file should not be included in your code, only analyzed by your IDE! * This file should not be included in your code, only analyzed by your IDE!
* *
@@ -20592,6 +20592,7 @@ namespace Illuminate\Support\Facades {
* @param array $headers * @param array $headers
* @param string|null $disposition * @param string|null $disposition
* @return \Symfony\Component\HttpFoundation\StreamedResponse * @return \Symfony\Component\HttpFoundation\StreamedResponse
* @throws UnableToRetrieveMetadata
* @static * @static
*/ */
public static function response($path, $name = null, $headers = [], $disposition = 'inline') public static function response($path, $name = null, $headers = [], $disposition = 'inline')
@@ -20609,6 +20610,7 @@ namespace Illuminate\Support\Facades {
* @param string|null $name * @param string|null $name
* @param array $headers * @param array $headers
* @return \Symfony\Component\HttpFoundation\StreamedResponse * @return \Symfony\Component\HttpFoundation\StreamedResponse
* @throws UnableToRetrieveMetadata
* @static * @static
*/ */
public static function serve($request, $path, $name = null, $headers = []) public static function serve($request, $path, $name = null, $headers = [])
@@ -20625,6 +20627,7 @@ namespace Illuminate\Support\Facades {
* @param string|null $name * @param string|null $name
* @param array $headers * @param array $headers
* @return \Symfony\Component\HttpFoundation\StreamedResponse * @return \Symfony\Component\HttpFoundation\StreamedResponse
* @throws UnableToRetrieveMetadata
* @static * @static
*/ */
public static function download($path, $name = null, $headers = []) public static function download($path, $name = null, $headers = [])
@@ -20823,6 +20826,7 @@ namespace Illuminate\Support\Facades {
* *
* @param string $path * @param string $path
* @return string|false * @return string|false
* @throws UnableToRetrieveMetadata
* @static * @static
*/ */
public static function mimeType($path) public static function mimeType($path)
+1
View File
@@ -3,6 +3,7 @@
"claude_code", "claude_code",
"gemini" "gemini"
], ],
"cloud": false,
"guidelines": true, "guidelines": true,
"herd_mcp": false, "herd_mcp": false,
"mcp": true, "mcp": true,
Generated
+408 -348
View File
File diff suppressed because it is too large. Load diff
+312 -312
View File
File diff suppressed because it is too large. Load diff