Skip to content

Laravel Framework

170 companion flashcards · AI-assisted study content · Open the deck →

This deck is designed to help you build a solid understanding of the Laravel PHP framework, focusing on the features you'll use most often when building web applications. The cards cover core topics like Eloquent ORM for database interactions, Blade for templating, routing, controllers, and middleware. Whether you're just starting out with Laravel or need a refresher on specific concepts, this deck offers a structured way to test and reinforce what you know.

The questions range from foundational ideas, like what Eloquent or Blade actually are, to more hands-on details such as defining one-to-many relationships, passing data to views, or setting up route model binding. You'll also find prompts about practical techniques like eager loading, using accessors and mutators, and working with resource controllers. This makes the deck a good fit for developers preparing for interviews, students studying web development, or anyone working through Laravel certification material.

To get the most out of this deck, try spacing your study sessions over several days rather than cramming everything at once. Laravel concepts often build on each other, so revisiting related cards helps strengthen the connections in your memory. When you get a card wrong, take a moment to write out a small example or read the relevant section of the Laravel docs before moving on. This active approach turns the flashcard review into genuine learning rather than simple recognition.

The HTTP Layer: Routing and Middleware

Laravel routes are defined in routes/web.php for browser traffic or routes/api.php for stateless APIs. A route can return a view directly via a closure or point to a controller action such as Route::get('/users', [UserController::class, 'index']). Laravel maps each HTTP verb to a router method: get for reads, post for creates, put or patch for updates, and delete for deletes, with any matching everything and match accepting a subset. For CRUD-heavy resources, Route::resource() registers seven standard routes (index, create, store, show, edit, update, destroy) automatically; for APIs, Route::apiResource() excludes the form-only create and edit verbs. Routes can capture URI segments as parameters, marked required or optional, and constrained with where('id', '[0-9]+') for regex validation.

Named routes let you reference URLs symbolically so the actual paths can change without breaking callers. Naming a route with ->name('dashboard') lets you generate URLs via route('dashboard') and redirect via return redirect()->route('dashboard'). Route groups share common attributes such as prefix, middleware, name, domain, and where, which keeps related route declarations DRY. Laravel also supports signed URLs (with a hash Laravel validates to prevent tampering), rate limiting via the throttle middleware, and named limiters configured through RateLimiter::for(...) in a service provider. Additional routes can be added alongside a resource controller by placing them before the resource registration, since more specific routes win during matching.

Route model binding eliminates boilerplate by injecting model instances directly into controllers. With implicit binding, a type-hinted parameter like Post \(post is resolved by primary key automatically; if no record matches, Laravel throws a 404. Explicit binding (Route::bind('post', fn(\)v) => Post::where('slug', $v)->firstOrFail())) is useful for slug-based lookups, and overriding resolveRouteBinding() on the model itself provides per-model customization. In Laravel 11+, scoped bindings via Route::scopeBindings() or resolveRouteBindingQueryUsing() constrain child lookups to the parent resource, which is critical for multi-tenant applications where posts must belong to the requested user.

Middleware filters HTTP requests before or after they reach your application, handling cross-cutting concerns such as authentication, CORS, and logging. Create custom middleware with php artisan make:middleware CheckAge and implement logic in handle(); register it in bootstrap/app.php on Laravel 11+ or in app/Http/Kernel.php on older versions, either globally (runs on every request) or on specific routes via ->middleware('auth'). Middleware parameters are passed after a colon ('role:editor,admin') and received as variadic arguments in handle(). The optional terminate() method runs after the response is sent and is useful for logging, but should never hold critical security logic since the response is already on the wire.

Eloquent ORM: Models, Relationships, and Querying

Eloquent is Laravel's Active Record ORM: every database table maps to a Model class that provides an expressive, chainable query interface. Generate a model and its migration together with php artisan make:model Post -m; the -f flag also creates a factory and -s a seeder. To insert, update, or delete records, use create(), update(), and delete(), all gated by the \(fillable whitelist or \)guarded blacklist to prevent mass-assignment vulnerabilities. Lookup helpers include find() (returns null if missing), findOrFail() and firstOrFail() (throw a ModelNotFoundException that Laravel converts to a 404), the shorthand firstWhere(), plus whereIn() and whereBetween() for batch filters. Convenience helpers include latest(), oldest(), inRandomOrder(), pluck(), and select() with addSelect() for subqueries.

Eloquent models support rich attribute handling through casts, accessors, mutators, and observer events. The $casts property converts attributes to booleans, arrays, datetimes, decimals, or PHP enums via AsEnum::class. Accessors transform values when reading and mutators transform them when writing, both declared with the modern Attribute::make(get: ..., set: ...) helper. Lifecycle events (including retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, and restored) can be subscribed to via observers or registered directly inside the model's booted() method. The saving event fires before both inserts and updates, creating fires only before inserts, and saved is the safest hook for side effects because it fires on both operations after the row is persisted. To skip events temporarily, use $model->saveQuietly(), Model::withoutEvents(), or Model::unsetEventDispatcher(). Inspecting change state is easy with isDirty('email') (pending changes), isClean(), wasChanged() (after save), and getOriginal('email'). fresh() returns a new instance loaded from the database while leaving the original untouched; refresh() overwrites the same instance. Use replicate(['views_count']) to clone an unsaved instance with a fresh primary key.

Relationships are declared as methods returning hasOne, belongsTo, hasMany, belongsToMany, hasManyThrough, hasOneThrough, morphMany, morphTo, morphToMany, or morphedByMany. Each method follows Laravel conventions for foreign keys (typically {parent}_id) and pivot tables (alphabetical singular, e.g., role_user); pass extra arguments to override. With many-to-many relationships, use attach() to add, detach() to remove, and sync() to make the pivot exactly match a given ID list. Access extra pivot columns via withPivot() and ->pivot->column; add withTimestamps() for automatic timestamp handling. Polymorphic relationships use morphs('commentable') columns on the child table so multiple parents can share the relation. The SoftDeletes trait plus a \(table->softDeletes() migration column enables non-destructive deletion via \)post->delete() and restoration with $post->restore(). Idempotent helpers firstOrCreate(['email' => $x], ['name' => 'A']) insert only when no match exists, while updateOrCreate() updates the matching record or creates one with both attribute sets merged.

Preventing N+1 queries is critical for performance. Eager loading via with('author') fetches relations in batched queries; without it, accessing a relation in a loop fires one query per iteration. Use load() for lazy eager loading when you already have a collection, and withCount('comments') to add a {relation}_count column without loading the relation. Call Model::preventLazyLoading(!app()->isProduction()) to surface lazy-loading violations during development. Filter parent queries by relation existence with whereHas('posts', $cb) (with constraints) or has('posts', '>', 5) (with count operators). Scopes encapsulate reusable query constraints: local scopes are invoked explicitly as User::active(), while global scopes apply automatically to every query on a model and can be removed per query with withoutGlobalScopes(). For very large result sets, use chunk(), the safer chunkById() (stable across mid-iteration modifications), lazy() / lazyById() returning a LazyCollection, or cursor() which runs a single unbuffered query via generators to keep memory flat. For safe raw SQL, use DB::raw() for select fragments and always bind user input with whereRaw('email = ?', [$email]). Collections returned by Eloquent wrap arrays with a chainable API of 100+ methods including map, filter, pluck, groupBy, sum, reduce, sortBy, chunk, unique, first, and last. To auto-generate UUIDs for primary keys, override newUniqueId() in the model, set \(incrementing = false and \)keyType = 'string', and use $table->uuid('id')->primary() in the migration.

Database Migrations, Schema, and Data Seeding

Migrations are Laravel's version control for database schemas. Generate one with php artisan make:migration create_posts_table, define schema changes in up(), and put the inverse in down(). Run with php artisan migrate, roll back with migrate:rollback, refresh with migrate:refresh (rollback plus re-run, keeping tables), or use migrate:fresh to drop all tables and re-run from scratch, which is preferred when the schema is broken. Add a column to an existing table with make:migration add_email_to_users_table --table=users, then $table->string('email')->after('name'). Renaming columns worked via the doctrine/dbal package on older Laravel but is native from Laravel 10 onward.

The schema builder exposes column types including string, integer, json, timestamp, softDeletes, uuid, and morphs, plus modifiers such as nullable, default, unique, index, and after. Foreign keys are concise with foreignId('user_id')->constrained()->cascadeOnDelete(), and constrained('users') overrides the inferred table name. Add nullOnDelete(), restrictOnDelete(), or cascadeOnUpdate() for finer control. Composite indexes ($table->unique(['user_id', 'slot'])) accelerate queries that filter by their leftmost columns. Drop columns with dropColumn('is_admin') or rename with renameColumn('old', 'new'). For database features the builder does not expose, fall back to DB::statement() for raw SQL inside migrations, and write the inverse in down().

Seeders populate the database with sample or fixture data, and factories generate model blueprints using Faker. Create a seeder with make:seeder and call other seeders from DatabaseSeeder::run() via $this->call([UserSeeder::class, PostSeeder::class]); run them with php artisan db:seed or combine with migrate --seed for a one-step rebuild. A factory's definition() returns the attribute defaults, and returning another factory as a value (e.g., 'user_id' => User::factory()) automatically creates and links the parent. Define states like suspended() with return $this->state(['suspended_at' => now()]), then chain User::factory()->admin()->suspended()->create(). Use truncate() to remove all rows and reset auto-increment, but note that it cannot be rolled back inside a MySQL transaction.

Laravel's database layer also handles transactions, multiple connections, and query inspection. Wrap a closure in DB::transaction($cb, $attempts) to roll back automatically on any exception and optionally retry on serialization failures (deadlocks). For more granular control, use DB::beginTransaction(), commit(), and rollBack() manually, and defer side effects until commit with DB::afterCommit(fn() => ...). Configure additional connections in config/database.php and access them via DB::connection('pgsql') or set \(connection on a model. Read/write splits use nested arrays under the connection key. To debug query traffic, register DB::listen(fn (\)q) => logger()->info($q->sql, $q->bindings)) in a service provider during development. The DB facade also supports raw table queries (DB::table('users')->where(...)->get()) returning stdClass, plus UNIONs via DB::table('b')->union($q)->get() and subqueries through addSelect(['post_count' => DB::table(...)->selectRaw('count(*)')->whereColumn(...)]).

Blade Templates, Views, and Pagination

Blade is Laravel's lightweight templating engine that augments HTML with shortcuts for PHP control flow, output escaping, and template inheritance. The double-brace syntax {{ $var }} applies htmlspecialchars to escape output and prevent XSS, while {!! $var !!} renders raw HTML, used only when you trust the source. Control directives like @if, @foreach, @extends, @section, and @yield make layouts composable: child templates @extends a parent, fill @section blocks, and the parent yields them in named slots. Pass data with return view('profile', ['user' => $user]), ->with('user', $user), or the compact() helper. Share a variable with every view by calling View::share('key', $value) in a service provider's boot().

Components package reusable markup and logic. Generate a class component with php artisan make:component Alert, which creates both a class in app/View/Components/ and a Blade view in resources/views/components/; render it as <x-alert type="error" :message="$msg" />. Anonymous components skip the class entirely: drop a Blade file into resources/views/components/ and use it as <x-alert />. {{ $slot }} renders child content; named slots work with <x-slot:name>...</x-slot:name>. Push scripts or styles from children into a parent placeholder with @stack('scripts') and @push('scripts'), or use @prepend to add to the front. For conditional or ordered partials, use @includeWhen, @includeIf, @includeFirst, and @each('partials.item', \(items, 'item', 'partials.empty').

Extend Blade with custom directives and built-ins. Register Blade::directive('datetime', fn (\)exp) => "<?php echo ($exp)->format('Y-m-d'); ?>") in a service provider, or use Blade::if('admin', fn () => auth()->check() && auth()->user()->isAdmin()) for conditional shortcuts. Render markdown with @markdown(\(post->body) and embed JSON in a <script> tag with @json(\)data). For dynamic templates such as emails, call Blade::render('Hello, {{ $name }}', ['name' => 'A']). CSRF protection is built in: @csrf outputs the _token hidden input that Laravel's VerifyCsrfToken middleware validates on non-GET requests. For SPAs posting to the same Laravel app, configure Sanctum to read the XSRF-TOKEN cookie and send it as the X-XSRF-TOKEN header (Axios does this automatically when withCredentials is true).

Laravel's paginator integrates directly with Eloquent and renders links via {{ $items->links() }}. paginate() runs a COUNT(*) query so you can show "X of Y" UIs, while simplePaginate() only knows whether a next page exists, making it cheaper for huge tables. For datasets where inserts shift pages, use cursorPaginate(15), which encodes cursors instead of page numbers and is stable across new rows. Customize the rendered view globally with Paginator::defaultSimpleView(...) or per call with $items->links('partials.pager'), and rename the page query parameter via Paginator::currentPageResolver(fn () => request('p', 1)).

Authentication, Authorization, and Sessions

Laravel provides two complementary authorization layers: gates (closures) and policies (classes). Define gates in AuthServiceProvider with Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; }), then check with Gate::allows('update-post', $post) or @can('update-post', $post) in Blade. Policies organize authorization logic around a specific model: generate with make:policy PostPolicy --model=Post, and methods like update(User $user, Post $post) map directly to abilities. Inside controllers, $this->authorize('update', $post) throws an AuthorizationException (403) when denied, or you can use $user->can('update', $post) for a boolean check. Gates are best for simple, general checks; policies win for any non-trivial model.

Reading the current user is straightforward: auth()->user() or Auth::user(). Log a user in programmatically with Auth::login($user), Auth::loginUsingId(5), or Auth::attempt(['email' => $email, 'password' => $pw], $remember = true); the last one verifies credentials against the configured guard and starts a session. Passing true as the second argument issues a long-lived remember-me cookie that authenticates the user on subsequent visits even after the session expires. Check Auth::viaRemember() to detect this case in your application logic. For API token issuance with cookies for SPAs and bearer tokens for mobile apps, use Sanctum: $user->createToken('app')->plainTextToken returns a token clients send as a bearer credential, while SPAs authenticate via the shared laravel_session cookie once configured in config/sanctum.php.

Sessions back all of this state. Store values with session(['key' => 'value']), retrieve with session('key', 'default'), and use flash() for data that should survive exactly one redirect, such as status messages: $request->session()->flash('status', 'Saved!'). now() is the request-scoped variant that does not persist across the redirect; keep() re-flashes for an additional request. After login or privilege escalation, call $request->session()->regenerate() to write a new session ID and migrate data, preventing session fixation attacks; pair with ->invalidate() on logout to drop all session data. Session drivers (file, cookie, database, redis, array) are configured in config/session.php.

Queues, Events, Notifications, and Broadcasting

Artisan is Laravel's command-line interface, and a set of common commands covers most daily tasks: serve for the dev server, migrate, make:model, tinker (interactive REPL powered by PsySH), route:list, and cache:clear. Create your own commands with make:command SendEmails, set the $signature property, and put logic in handle(). In Laravel 11+, you can also define single-file closure commands in routes/console.php: Artisan::command('mail:send {user}', fn ($user) => ...)->hourly(). Schedule tasks the same way: Schedule::command('emails:send')->everyMinute()->withoutOverlapping(), then add a cron entry that calls php artisan schedule:run every minute. withoutOverlapping() uses a mutex file to prevent the same task from running concurrently with itself, useful for long-running jobs. Chain ->timezone('Europe/Zagreb'), ->when(fn () => ...), or ->skip() to control when the task fires.

Queues defer slow work (emails, exports, API calls) to a background worker. Generate a job with make:job ProcessPodcast, implement handle(), and dispatch it with ProcessPodcast::dispatch($podcast). Run the worker with php artisan queue:work: it boots the framework once and reuses it across jobs, which is efficient for production. Use queue:listen during development so code changes take effect, since it restarts the framework per job (but is slower). Tune retries with --tries=3, --timeout=60, and --backoff=10; failed jobs land in the failed_jobs table and can be retried with queue:retry all. Configure the driver (Redis, database, SQS) in config/queue.php. For large result sets, combine chunkById with Bus::batch to dispatch many jobs without exhausting memory.

Events decouple triggers from reactions. Generate an event with make:event OrderShipped and a listener with make:listener SendShipmentNotification --event=OrderShipped, then register them in EventServiceProvider (auto-discovery handles most cases). Dispatch with event(new OrderShipped($order)). Event subscribers are classes that subscribe to multiple events from a single subscribe(Dispatcher $events) method, registered in EventServiceProvider::$subscribe. Notifications (which can use the same dispatcher) let a single class deliver to multiple channels such as mail, database, SMS, and Slack. Generate with make:notification InvoicePaid, define channels in via(), and send with \(user->notify(new InvoicePaid(\)invoice)) or Notification::send($users, new InvoicePaid($invoice)). Add 'database' to via() and implement toDatabase() to persist notifications; retrieve with \(user->unreadNotifications and mark with \)notification->markAsRead().

Broadcasting pushes real-time events to WebSocket clients. Implement ShouldBroadcast on an event, define broadcastOn() to return a Channel, PrivateChannel, or PresenceChannel, then trigger the event normally. Front-ends subscribe via Laravel Echo. Authorize private channels in routes/channels.php: Broadcast::channel('orders.{id}', fn ($user, $id) => $user->id === (int) $id). Presence channels additionally return an array of presence data (e.g., user info) used to populate who's online.

Service Container, Facades, and Cross-Cutting Concerns

Laravel's service container is the backbone of dependency injection. It resolves classes and their dependencies automatically, including all of Laravel's own components. Bind implementations with $this->app->bind(Interface::class, Implementation::class); use singleton() instead of bind() when the same instance should be returned on every resolution. Service providers are the central place to register bindings in register() and configure services in boot(), which runs after all providers register. Facades provide a static-like syntax (Cache::get('key')) on top of container-resolved classes, concise yet still testable. Real-time facades extend this pattern: prefix any class with Facades\ in a use statement and Laravel dynamically generates a facade that resolves the class from the container, letting you swap implementations in tests without code changes.

Validation guards incoming data. Inline rules run via $request->validate(['title' => 'required|max:255']); for reusable logic, generate a Form Request with make:request StorePostRequest, define rules in rules(), and type-hint it in the controller so Laravel resolves and validates it automatically. Common rules include required, string|max:255, email, unique:users,email, confirmed (matches a field_confirmation value), exists:posts,id, and nullable|image|mimes:jpg,png. Custom rules are first-class: make:rule Uppercase generates a class whose validate() method calls $fail(...) on invalid input; apply with 'name' => [new Uppercase]. For custom 404 and 500 pages, drop Blade files at resources/views/errors/{code}.blade.php; for JSON responses, throw abort(response()->json(...), 422) or hook withExceptions() in bootstrap/app.php.

Testing is integrated end-to-end. Feature tests generated with make:test UserTest use HTTP testing helpers like $this->get('/')->assertStatus(200). The RefreshDatabase trait wraps each test in a transaction that rolls back at the end, keeping tests isolated without re-running migrations. Authenticate routes with \(this->actingAs(\)user), or for Sanctum tokens: \(this->actingAs(\)user, 'sanctum'). Mock collaborators with \(this->mock(Service::class, fn (\)m) => $m->shouldReceive(...)); use Bus::fake(), Event::fake(), and Notification::fake() to assert framework interactions without executing them. For HTTP client tests, Http::fake([...]) and Http::assertSent(...) intercept requests; pair with Http::preventStrayRequests() to catch unwired external calls. Format code with Laravel Pint (vendor/bin/pint, with --test to report without fixing) or PHP_CodeSniffer for stricter PSR-12 enforcement.

Cross-cutting concerns cover caching, mail, storage, logging, and configuration. The Cache facade offers Cache::remember($key, $ttl, $cb) for memoized lookups, rememberForever() for indefinite storage, atomic Cache::lock('key', 10)->block(5, $cb) for race-free operations, and Cache::tags(['users'])->flush() for tag-based eviction on supported stores. Mail supports raw messages via Mail::raw or full Mailable classes generated with make:mail that implement envelope(), content(), and attachments(); add ShouldQueue to send asynchronously. Markdown mail renders pre-styled HTML; publish templates with vendor:publish --tag=laravel-mail to customize themes. Storage uses disks (local, public, s3): php artisan storage:link makes the public disk accessible at /storage. File uploads go through $request->file('avatar')->store('avatars', 'public') after validating with image|max:2048; helper methods include getClientOriginalName(), extension(), size(), and isValid(). Laravel's HTTP client (Http::get(...)) supports timeouts, retries, bearer tokens, and throw() for 4xx/5xx handling. Logging routes through configured channels (single, daily, slack, stack) via the Log facade, with contextual arrays such as Log::info('User signed in', ['id' => $u->id]). Always read config via config('app.timezone') and never env() outside config files, because php artisan config:cache causes env() calls elsewhere to return null in production.

Frequently asked questions

What is Laravel Eloquent ORM?

Eloquent is Laravel's built-in Active Record ORM. Each database table has a corresponding Model class used to interact with that table. It provides an expressive syntax for queries:
$users = User::where('active', true)->get();

What are migrations in Laravel?

Migrations are version control for your database. They let you define and modify database schema using PHP code:
php artisan make:migration create_posts_table
Run with php artisan migrate. Roll back with php artisan migrate:rollback.

How do you store notifications in the database?

Run:
php artisan notifications:table && php artisan migrate
Add 'database' to the via() method and implement toDatabase() or toArray(). Retrieve with:
$user->unreadNotifications;
Mark as read:
$notification->markAsRead();

How do you define a many-to-many relationship in Eloquent?

Return belongsToMany from both models pointing at the pivot table:
public function roles() { return $this->belongsToMany(Role::class); }
public function users() { return $this->belongsToMany(User::class); }
Laravel assumes the pivot is role_user (alphabetical, singular). Pass a second argument to override.

How do you order and limit query results in Eloquent?

User::orderBy('created_at', 'desc')->get();
User::latest()->get(); // order by created_at desc
User::oldest()->get();
User::inRandomOrder()->first();
User::skip(10)->take(5)->get();
latest / oldest accept an optional column name.

How do you replicate an Eloquent model (create a copy not yet saved)?

Call $new = $model->replicate(); to get a new unsaved instance with attributes copied. Optionally exclude attributes:
$new = $model->replicate(['views_count']);
Then modify and $new->save();. The new record gets a fresh primary key and timestamps.

How do you register a custom Blade directive?

In a service provider:
Blade::directive('datetime', fn ($exp) => "format('Y-m-d'); ?>");
Use it: @datetime($post->created_at). Return PHP to execute. Use Blade::if('admin', fn() => auth()->check() && auth()->user()->isAdmin()) for conditional directives.

How do you configure multiple database connections in Laravel?

In config/database.php define entries under 'connections'. Use them via:
DB::connection('pgsql')->table(...)->get();
Or set a model's $connection property. Use a read/write split: 'mysql' => ['read' => [...], 'write' => [...]].

What is the difference between Cache::remember and Cache::rememberForever?

remember($key, $ttl, $cb) caches the closure result for $ttl seconds. rememberForever($key, $cb) caches until you call Cache::forget($key). Use rememberForever for data that rarely changes; forget it explicitly when you mutate the source.

How do you write data to the session in Laravel?

session(['key' => 'value']);
session()->put('key', 'value');
request()->session()->flash('status', 'Saved');
$value = session('key', 'default');
Drivers: file, cookie, database, redis, array. Configured in config/session.php.

Drill this topic

170 flashcards on Laravel Framework — free, no signup needed to start.

Study Laravel Framework flashcards

LearnWiki pages are generated with AI assistance from LearnCoachAssist's reviewed study catalog and may contain errors — verify anything critical against your course materials.