Appearance
🚀 Quick Reference Cheat Sheet
Essential patterns and templates for daily Laravel development. Bookmark this page for quick access!
📋 File Structure Quick Reference
app/
├── Actions/ # Single-purpose operations
├── Http/
│ ├── Controllers/ # Thin controllers
│ ├── Requests/ # Form validation
│ └── Resources/ # API responses
├── Models/ # Eloquent models
├── Services/ # Business logic
├── Repositories/ # Data access
│ └── Interfaces/ # Repository contracts
├── Observers/ # Model event handlers
└── Policies/ # Authorization logic🎮 Controller Template
php
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Http\Resources\UserResource;
use App\Services\UserService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
class UserController extends Controller
{
public function __construct(
private UserService $userService
) {}
public function index(): AnonymousResourceCollection
{
$users = $this->userService->getAllUsers();
return UserResource::collection($users);
}
public function store(StoreUserRequest $request): JsonResponse
{
$user = $this->userService->createUser($request->validated());
return response()->json([
'data' => new UserResource($user),
'message' => 'User created successfully',
], 201);
}
public function show(int $id): JsonResponse
{
$user = $this->userService->getUserById($id);
return response()->json(['data' => new UserResource($user)]);
}
public function update(UpdateUserRequest $request, int $id): JsonResponse
{
$user = $this->userService->updateUser($id, $request->validated());
return response()->json([
'data' => new UserResource($user),
'message' => 'User updated successfully',
]);
}
public function destroy(int $id): JsonResponse
{
$this->userService->deleteUser($id);
return response()->json(null, 204);
}
}🔧 Service Template
php
<?php
declare(strict_types=1);
namespace App\Services;
use App\Events\UserCreated;
use App\Exceptions\UserNotFoundException;
use App\Models\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
class UserService
{
public function __construct(
private UserRepositoryInterface $userRepository
) {}
public function getAllUsers(): Collection
{
return $this->userRepository->getAllActive();
}
public function getUserById(int $id): User
{
$user = $this->userRepository->find($id);
if (!$user) {
throw new UserNotFoundException("User with ID {$id} not found");
}
return $user;
}
public function createUser(array $data): User
{
try {
DB::beginTransaction();
$data['password'] = Hash::make(value: $data['password']);
$data['email'] = strtolower(string: trim(string: $data['email']));
$user = $this->userRepository->create(data: $data);
event(event: new UserCreated($user));
DB::commit();
Log::info(message: 'User created successfully', context: ['user_id' => $user->id]);
return $user;
} catch (\Exception $e) {
DB::rollBack();
Log::error(message: 'Failed to create user', context: ['error' => $e->getMessage()]);
throw $e;
}
}
public function updateUser(int $id, array $data): User
{
$user = $this->getUserById(id: $id);
if (isset($data['password'])) {
$data['password'] = Hash::make(value: $data['password']);
}
$this->userRepository->update(id: $id, data: $data);
return $user->refresh();
}
public function deleteUser(int $id): bool
{
$user = $this->getUserById(id: $id);
return $this->userRepository->delete(id: $id);
}
}📦 Repository Template
php
<?php
declare(strict_types=1);
namespace App\Repositories;
use App\Models\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
class UserRepository implements UserRepositoryInterface
{
public function __construct(
private User $model
) {}
public function find(int $id): ?User
{
return $this->model->find($id);
}
public function findByEmail(string $email): ?User
{
return $this->model->where(column: 'email', operator: '=', value: $email)->first();
}
public function create(array $data): User
{
return $this->model->create(attributes: $data);
}
public function update(int $id, array $data): bool
{
return $this->model->where(column: 'id', operator: '=', value: $id)->update(values: $data);
}
public function delete(int $id): bool
{
return $this->model->destroy(ids: $id) > 0;
}
public function getAllActive(): Collection
{
return $this->model
->where(column: 'is_active', operator: '=', value: true)
->with(relations: ['role'])
->orderBy(column: 'created_at', direction: 'desc')
->get();
}
public function paginate(int $perPage = 15): LengthAwarePaginator
{
return $this->model
->with(relations: ['role'])
->orderBy(column: 'created_at', direction: 'desc')
->paginate(perPage: $perPage);
}
}📝 Form Request Template
php
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', User::class);
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', 'unique:users,email'],
'password' => [
'required',
'string',
'min:12',
'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/',
],
'role' => ['required', 'string', 'in:user,admin,moderator'],
];
}
public function messages(): array
{
return [
'password.regex' => 'Password must contain uppercase, lowercase, number, and special character.',
'email.unique' => 'This email is already registered.',
];
}
protected function prepareForValidation(): void
{
$this->merge([
'email' => strtolower(trim($this->email ?? '')),
'name' => trim($this->name ?? ''),
]);
}
}📨 API Resource Template
Response Standards
For complete response format standards with macros and collections, see Response Macro Service Provider.
php
<?php
declare(strict_types=1);
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'role' => $this->role,
'is_active' => $this->is_active,
'email_verified_at' => $this->email_verified_at?->toIso8601String(),
'created_at' => $this->created_at->toIso8601String(),
'updated_at' => $this->updated_at->toIso8601String(),
// Conditional relationships
'profile' => ProfileResource::make($this->whenLoaded('profile')),
'orders' => OrderResource::collection($this->whenLoaded('orders')),
// Conditional fields
'permissions' => $this->when(
$request->user()?->isAdmin(),
fn () => $this->permissions->pluck('name')
),
];
}
}⚡ Action Class Template
php
<?php
declare(strict_types=1);
namespace App\Actions\Users;
use App\Events\UserCreated;
use App\Models\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class CreateUserAction
{
public function __construct(
private UserRepositoryInterface $userRepository
) {}
public function execute(array $data): User
{
return DB::transaction(function () use ($data) {
$data['password'] = Hash::make(value: $data['password']);
$data['email'] = strtolower(string: trim(string: $data['email']));
$user = $this->userRepository->create(data: $data);
event(event: new UserCreated($user));
return $user;
});
}
}🗄️ Model Template
php
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'title',
'content',
'user_id',
'category_id',
'is_published',
];
protected $hidden = [
'deleted_at',
];
protected $casts = [
'is_published' => 'boolean',
'published_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
public function scopePublished($query)
{
return $query->where(column: 'is_published', operator: '=', value: true);
}
public function scopeRecent($query)
{
return $query->orderBy(column: 'created_at', direction: 'desc');
}
}🔄 Migration Template
php
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('content');
$table->boolean('is_published')->default(false);
$table->timestamp('published_at')->nullable();
$table->timestamps();
$table->softDeletes();
// Indexes
$table->index(['user_id', 'is_published']);
$table->index('category_id');
$table->index('created_at');
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};🧪 Test Templates
Unit Test
php
<?php
declare(strict_types=1);
namespace Tests\Unit\Services;
use App\Models\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use App\Services\UserService;
use Mockery;
use Tests\TestCase;
class UserServiceTest extends TestCase
{
private UserService $userService;
private UserRepositoryInterface $userRepository;
protected function setUp(): void
{
parent::setUp();
$this->userRepository = Mockery::mock(UserRepositoryInterface::class);
$this->userService = new UserService($this->userRepository);
}
public function test_create_user_successfully(): void
{
// Arrange
$userData = [
'name' => 'John Doe',
'email' => '[email protected]',
'password' => 'password123',
];
$expectedUser = new User($userData);
$this->userRepository
->shouldReceive('create')
->once()
->andReturn($expectedUser);
// Act
$result = $this->userService->createUser($userData);
// Assert
$this->assertInstanceOf(User::class, $result);
$this->assertEquals('John Doe', $result->name);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
}Feature Test
php
<?php
declare(strict_types=1);
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class UserManagementTest extends TestCase
{
use RefreshDatabase, WithFaker;
public function test_user_can_be_created(): void
{
// Arrange
$userData = [
'name' => $this->faker->name(),
'email' => $this->faker->safeEmail(),
'password' => 'SecurePass123!',
'password_confirmation' => 'SecurePass123!',
];
// Act
$response = $this->postJson('/api/users', $userData);
// Assert
$response->assertStatus(201)
->assertJsonStructure([
'data' => ['id', 'name', 'email'],
]);
$this->assertDatabaseHas('users', [
'name' => $userData['name'],
'email' => $userData['email'],
]);
}
public function test_authenticated_user_can_view_profile(): void
{
$user = User::factory()->create();
$response = $this->actingAs(user: $user)->getJson(uri: '/api/profile');
$response->assertStatus(200)
->assertJson([
'data' => [
'id' => $user->id,
'name' => $user->name,
],
]);
}
}🔐 Policy Template
php
<?php
declare(strict_types=1);
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function viewAny(User $user): bool
{
return true;
}
public function view(User $user, Post $post): bool
{
return $post->is_published || $post->user_id === $user->id;
}
public function create(User $user): bool
{
return $user->hasPermission('posts.create');
}
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id
|| $user->hasPermission('posts.update.any');
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id
|| $user->hasPermission('posts.delete.any');
}
}👁️ Observer Template
php
<?php
declare(strict_types=1);
namespace App\Observers;
use App\Models\User;
use Illuminate\Support\Facades\Log;
class UserObserver
{
public function created(User $user): void
{
Log::info(message: 'User created', context: ['user_id' => $user->id]);
}
public function updated(User $user): void
{
if ($user->wasChanged(attributes: 'email')) {
Log::info(message: 'User email changed', context: [
'user_id' => $user->id,
'old_email' => $user->getOriginal(key: 'email'),
'new_email' => $user->email,
]);
}
}
public function deleted(User $user): void
{
Log::info(message: 'User deleted', context: ['user_id' => $user->id]);
}
}🎯 Common Artisan Commands
bash
# Generate files
php artisan make:controller UserController --api --requests
php artisan make:model Post -mfs # Migration, Factory, Seeder
php artisan make:request StoreUserRequest
php artisan make:resource UserResource
php artisan make:policy PostPolicy --model=Post
php artisan make:observer UserObserver --model=User
php artisan make:service UserService # Custom command
php artisan make:repository UserRepository # Custom command
php artisan make:action CreateUserAction # Custom command
# Database
php artisan migrate
php artisan migrate:rollback
php artisan migrate:fresh --seed
php artisan db:seed
# Testing
php artisan test
php artisan test --coverage
php artisan test --filter UserTest
# Code quality
./vendor/bin/pint
./vendor/bin/phpstan analyse
php artisan optimize
php artisan optimize:clear
# Cache
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear📊 Common Validation Rules
php
// String validation
'name' => 'required|string|max:255|min:3'
'email' => 'required|email|unique:users,email|max:255'
'url' => 'required|url|active_url'
'slug' => 'required|alpha_dash|unique:posts,slug'
// Numeric validation
'age' => 'required|integer|min:18|max:120'
'price' => 'required|numeric|min:0|max:999999.99'
'quantity' => 'required|integer|between:1,100'
// Date validation
'birth_date' => 'required|date|before:today'
'start_date' => 'required|date|after_or_equal:today'
'end_date' => 'required|date|after:start_date'
// File validation
'avatar' => 'required|image|mimes:jpg,png|max:2048'
'document' => 'required|file|mimes:pdf,doc,docx|max:10240'
// Array validation
'tags' => 'required|array|min:1|max:5'
'tags.*' => 'required|string|distinct|exists:tags,name'
// Boolean validation
'is_active' => 'required|boolean'
'accept_terms' => 'required|accepted'
// Relation validation
'user_id' => 'required|integer|exists:users,id'
'category_id' => 'required|integer|exists:categories,id'
// Password validation
'password' => [
'required',
'string',
'min:12',
'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/',
'confirmed'
]🔄 Query Optimization Patterns
php
// N+1 Prevention
$users = User::with(['posts', 'profile'])->get();
$users = User::with(['posts' => fn($q) => $q->published()])->get();
// Select specific columns
$users = User::select(['id', 'name', 'email'])->get();
$users = User::with(['posts:id,user_id,title'])->get();
// Pagination
$users = User::paginate(15);
$users = User::simplePaginate(15);
$users = User::cursorPaginate(15);
// Chunking
User::chunk(100, function ($users) {
foreach ($users as $user) {
// Process user
}
});
// Lazy loading
User::lazy()->each(function ($user) {
// Process user
});🎨 Response Format Standards
php
// Success response
return response()->json([
'data' => $resource,
'message' => 'Operation successful',
], 200);
// Created response
return response()->json([
'data' => $resource,
'message' => 'Resource created successfully',
], 201);
// Error response
return response()->json([
'error' => [
'message' => 'Resource not found',
'code' => 'RESOURCE_NOT_FOUND',
],
], 404);
// Validation error response
return response()->json([
'error' => [
'message' => 'Validation failed',
'errors' => $validator->errors(),
],
], 422);💡 Pro Tip: Keep this page bookmarked for quick access during development. Use Ctrl+F to quickly find specific templates!