Quality

laravel-refactor

Refactors code for SOLID/DRY compliance

Overview

The laravel-refactor agent improves code quality by applying SOLID principles, eliminating duplication (DRY), extracting services, and following Laravel best practices. It uses Pint for styling and PHPStan for static analysis.

Responsibilities

  • SOLID Compliance - Single responsibility, dependency inversion, etc.
  • DRY Extraction - Extract duplicated code into reusable components
  • Service Extraction - Move business logic from controllers to services
  • Code Style - Apply Laravel Pint formatting
  • Static Analysis - Fix PHPStan issues
  • Simplification - Reduce complexity, improve readability

SOLID Principles Applied

Principle What Agent Does
S - Single Responsibility Extracts classes doing too much into focused services
O - Open/Closed Uses interfaces and strategy patterns
L - Liskov Substitution Ensures proper inheritance hierarchies
I - Interface Segregation Splits fat interfaces into focused ones
D - Dependency Inversion Injects dependencies via constructor

Refactoring Examples

// BEFORE: Fat controller with business logic
class OrderController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([...]);

        // Business logic in controller (bad!)
        $order = new Order($validated);
        foreach ($request->items as $item) {
            $product = Product::find($item['id']);
            if ($product->stock < $item['quantity']) {
                return back()->withErrors(['Insufficient stock']);
            }
            $product->decrement('stock', $item['quantity']);
            $order->items()->create([...]);
        }
        $order->save();

        // More business logic...
        Mail::send(new OrderConfirmation($order));

        return redirect()->route('orders.show', $order);
    }
}

// AFTER: Thin controller with service
class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService
    ) {}

    public function store(StoreOrderRequest $request)
    {
        try {
            $order = $this->orderService->create(
                auth()->user(),
                $request->validated()
            );

            return redirect()
                ->route('orders.show', $order)
                ->with('success', 'Order placed!');

        } catch (InsufficientStockException $e) {
            return back()->withErrors(['stock' => $e->getMessage()]);
        }
    }
}

DRY Extraction Example

// BEFORE: Duplicated query logic
class UserController
{
    public function index()
    {
        $users = User::where('active', true)
            ->where('role', 'admin')
            ->orderBy('name')
            ->get();
    }
}

class ReportController
{
    public function admins()
    {
        $users = User::where('active', true)
            ->where('role', 'admin')
            ->orderBy('name')
            ->get(); // Same query!
    }
}

// AFTER: Query scope in model
class User extends Model
{
    public function scopeActiveAdmins($query)
    {
        return $query->where('active', true)
            ->where('role', 'admin')
            ->orderBy('name');
    }
}

// Usage
$users = User::activeAdmins()->get();

Laravel Pint Configuration

{
    "preset": "laravel",
    "rules": {
        "simplified_null_return": true,
        "blank_line_before_statement": {
            "statements": ["return", "throw", "try"]
        },
        "concat_space": {
            "spacing": "one"
        },
        "method_argument_space": {
            "on_multiline": "ensure_fully_multiline"
        }
    }
}

PHPStan Configuration

# phpstan.neon
parameters:
    level: 8
    paths:
        - app
    ignoreErrors:
        - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder#'
    checkMissingIterableValueType: false

Common Refactoring Patterns

  • Extract Service - Move business logic from controllers
  • Extract Action - Create single-purpose classes
  • Extract Query Scope - Reusable query logic in models
  • Extract Trait - Share behavior across models
  • Extract Interface - Define contracts for swappable implementations
  • Extract Value Object - Encapsulate related data

Invoked By Commands

Guardrails

The refactor agent follows strict rules:

  • ALWAYS run tests before and after refactoring
  • ALWAYS extract after 2nd occurrence (Rule of Three)
  • ALWAYS keep methods under 20 lines
  • NEVER refactor without test coverage
  • NEVER change behavior while refactoring
  • NEVER create god classes or services

See Also