Laravel/PHP backend expert. PROACTIVELY use when working with Laravel, PHP APIs, Eloquent ORM. Triggers: laravel, php, eloquent, artisan
/plugin marketplace add nguyenthienthanh/aura-frog/plugin install aura-frog@aurafrogThis skill is limited to using the following tools:
Expert-level Laravel patterns for PHP 8.2+, Eloquent ORM, and API development.
This skill activates when:
laravel/framework in composer.json*.php files in Laravel structure// ❌ BAD - N+1 queries
$users = User::all();
foreach ($users as $user) {
echo $user->posts->count(); // 1 query per user!
}
// ✅ GOOD - Eager loading
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->posts->count(); // No extra query
}
// ✅ GOOD - Count without loading
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count;
}
// ✅ GOOD - Select only needed columns
$users = User::select(['id', 'name', 'email'])->get();
// ✅ GOOD - Use exists() not count()
if (User::where('email', $email)->exists()) {
// ...
}
// ✅ GOOD - Chunking large datasets
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// Process
}
});
// ✅ GOOD - Cursor for memory efficiency
foreach (User::cursor() as $user) {
// Processes one at a time
}
// ✅ GOOD - updateOrCreate for upserts
User::updateOrCreate(
['email' => $email],
['name' => $name, 'role' => $role]
);
// ✅ GOOD - Atomic increment/decrement
$post->increment('views');
$user->decrement('credits', 10);
// ✅ GOOD - Bulk operations
User::insert([
['name' => 'John', 'email' => 'john@example.com'],
['name' => 'Jane', 'email' => 'jane@example.com'],
]);
// ✅ GOOD - whereIn over multiple OR
User::whereIn('status', ['active', 'pending', 'review'])->get();
// ✅ GOOD - Conditional queries
User::query()
->when($request->status, fn($q, $status) => $q->where('status', $status))
->when($request->search, fn($q, $search) => $q->where('name', 'like', "%{$search}%"))
->get();
// ✅ GOOD - Subqueries
$users = User::addSelect([
'last_login' => Login::select('created_at')
->whereColumn('user_id', 'users.id')
->latest()
->limit(1)
])->get();
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\CreateUserRequest;
use App\Http\Resources\UserResource;
use App\Services\UserService;
use Illuminate\Http\JsonResponse;
class UserController extends Controller
{
public function __construct(
private UserService $userService
) {}
public function index(): JsonResponse
{
$users = $this->userService->getAllUsers();
return response()->json([
'data' => UserResource::collection($users),
]);
}
public function store(CreateUserRequest $request): JsonResponse
{
$user = $this->userService->createUser($request->validated());
return response()->json([
'data' => new UserResource($user),
'message' => 'User created successfully',
], 201);
}
public function show(User $user): JsonResponse
{
return response()->json([
'data' => new UserResource($user->load('posts')),
]);
}
}
<?php
namespace App\Services;
use App\Models\User;
use App\DTOs\CreateUserDTO;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class UserService
{
public function __construct(
private readonly NotificationService $notificationService,
) {}
public function createUser(CreateUserDTO $dto): User
{
return DB::transaction(function () use ($dto) {
$user = User::create([
'name' => $dto->name,
'email' => $dto->email,
'password' => Hash::make($dto->password),
]);
$this->notificationService->sendWelcomeEmail($user);
return $user;
});
}
}
<?php
readonly class CreateUserDTO
{
public function __construct(
public string $name,
public string $email,
public string $password,
) {}
public static function fromRequest(CreateUserRequest $request): self
{
return new self(
name: $request->validated('name'),
email: $request->validated('email'),
password: $request->validated('password'),
);
}
}
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
class CreateUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:2', 'max:100'],
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'confirmed', Password::defaults()],
'role' => ['sometimes', 'string', 'in:user,admin,moderator'],
];
}
public function messages(): array
{
return [
'email.unique' => 'This email is already registered.',
];
}
protected function prepareForValidation(): void
{
$this->merge([
'email' => strtolower($this->email),
'name' => trim($this->name),
]);
}
}
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'avatar_url' => $this->avatar_url,
'created_at' => $this->created_at->toISOString(),
// Conditional relationships
'posts' => PostResource::collection($this->whenLoaded('posts')),
'posts_count' => $this->when(isset($this->posts_count), $this->posts_count),
// Auth-based fields
'is_admin' => $this->when($request->user()?->isAdmin(), $this->is_admin),
];
}
}
<?php
namespace App\Exceptions;
use Exception;
class BusinessException extends Exception
{
public function __construct(
string $message,
public readonly string $code = 'BUSINESS_ERROR',
public readonly int $statusCode = 400,
) {
parent::__construct($message);
}
public function render($request)
{
return response()->json([
'message' => $this->getMessage(),
'code' => $this->code,
], $this->statusCode);
}
}
// app/Exceptions/Handler.php
public function render($request, Throwable $e)
{
if ($request->expectsJson()) {
if ($e instanceof ValidationException) {
return response()->json([
'message' => 'Validation failed',
'errors' => $e->errors(),
], 422);
}
if ($e instanceof NotFoundHttpException) {
return response()->json([
'message' => 'Resource not found',
], 404);
}
}
return parent::render($request, $e);
}
<?php
namespace App\Jobs;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendWelcomeEmail implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $timeout = 30;
public function __construct(
public readonly User $user,
) {}
public function handle(MailService $mailService): void
{
$mailService->sendWelcomeEmail($this->user);
}
public function failed(Throwable $exception): void
{
Log::error('Failed to send welcome email', [
'user_id' => $this->user->id,
'error' => $exception->getMessage(),
]);
}
public function backoff(): array
{
return [60, 120, 300]; // 1min, 2min, 5min
}
}
// Dispatch
SendWelcomeEmail::dispatch($user);
SendWelcomeEmail::dispatch($user)->onQueue('emails');
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));
// ✅ GOOD - Cache expensive queries
$users = Cache::remember('users.active', 3600, function () {
return User::where('status', 'active')->get();
});
// ✅ GOOD - Cache tags for invalidation
$posts = Cache::tags(['posts', 'user.'.$userId])->remember(
"user.{$userId}.posts",
3600,
fn() => Post::where('user_id', $userId)->get()
);
// Invalidate
Cache::tags(['user.'.$userId])->flush();
// ✅ GOOD - Model cache invalidation
class User extends Model
{
protected static function booted(): void
{
static::saved(fn($user) => Cache::forget("user.{$user->id}"));
static::deleted(fn($user) => Cache::forget("user.{$user->id}"));
}
}
<?php
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->isAdmin();
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->isAdmin();
}
}
// In controller
public function update(UpdatePostRequest $request, Post $post)
{
$this->authorize('update', $post);
// ...
}
$user->createToken('api-token', ['posts:read', 'posts:write']);
<?php
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserApiTest extends TestCase
{
use RefreshDatabase;
public function test_can_create_user(): void
{
$response = $this->postJson('/api/users', [
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => 'password123',
'password_confirmation' => 'password123',
]);
$response->assertStatus(201)
->assertJson([
'data' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
]);
$this->assertDatabaseHas('users', [
'email' => 'john@example.com',
]);
}
}
<?php
use App\Models\User;
it('creates a user', function () {
$response = $this->postJson('/api/users', [
'name' => 'John',
'email' => 'john@example.com',
'password' => 'password123',
'password_confirmation' => 'password123',
]);
$response->assertStatus(201);
expect(User::where('email', 'john@example.com')->exists())->toBeTrue();
});
dataset('invalid_emails', ['invalid', '', 'missing@', '@domain.com']);
it('rejects invalid emails', function (string $email) {
$response = $this->postJson('/api/users', [
'name' => 'John',
'email' => $email,
'password' => 'password123',
]);
$response->assertStatus(422);
})->with('invalid_emails');
checklist[12]{pattern,best_practice}:
N+1,with() or withCount() eager loading
Queries,whereIn over OR conditions
Atomic,increment/decrement/updateOrCreate
Bulk,insert() over create() loops
Validate,FormRequest classes
Resources,JsonResource with whenLoaded
Services,Business logic in service layer
DTOs,readonly classes PHP 8.2+
Jobs,ShouldQueue + backoff + failed
Cache,Cache::remember with tags
Auth,Policies for authorization
Tests,RefreshDatabase + assertJson
Version: 1.3.0
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.