Overview of the Autonomous Coding Loop Codex CLI has introduced a powerful experimental feature called `/goal`, which implements an autonomous reasoning loop similar to the ReAct pattern. This feature allows the coding agent to pursue complex objectives independently by cycling through thought, action, and observation phases. By defining clear success criteria, developers can step away from the terminal while the agent handles multi-phase refactoring or project bootstrapping. This technique matters because it shifts the developer's role from micro-managing every line of code to defining high-level outcomes and auditing the agent's self-verification steps. Prerequisites and Configuration To use this feature, you should be comfortable with command-line interfaces and basic Git workflows. Since `/goal` is currently experimental, you must manually enable it within your project's `config.toml` file. ```toml [features] goals = true ``` Without this specific flag, the `/goal` command will not be recognized by the CLI. It is also helpful to have a monitoring plan for your usage limits, especially if you are on a standard tier like the $20/month plan, as autonomous tasks consume tokens significantly faster than standard prompts. Key Libraries and Tools * Codex CLI: The primary command-line tool for interacting with OpenAI models locally. * GPT-4.5-high: The high-reasoning model used for complex tasks in these experiments. * Filament: A content management framework for Laravel used in the design implementation test. * Tailwind CSS: The styling utility used for front-end verification. Testing the Autonomous Workflow When you initiate a goal, the syntax requires a clear objective and a definition of done. For example, implementing a new design might look like this: ```bash /goal Implement Filament design in the chat project. Success criteria: Automated tests must pass and the dashboard text must be visible in the sidebar. ``` During execution, you can monitor progress using `/goal status`. This returns real-time data on time elapsed and tokens consumed without interrupting the agent's work. In a multi-phase test consisting of eight distinct architectural stages, the agent successfully navigated from phase to phase, committing to Git after each successful verification. Syntax Notes and System Behavior A notable feature of Codex CLI is its handling of context saturation. When the context window reaches 100% capacity (defaulting to 258k tokens), the system performs an automatic "compaction." It clears the current context and restarts from 0%, re-analyzing the project state to stay lean. While this risks losing some historical nuance, it prevents the agent from stalling mid-task. Practical Examples and Usage Limits In real-world applications, `/goal` proves more thorough than standard prompts. For instance, in a layout implementation task, the goal-oriented agent generated more precise PHPUnit assertions—specifically checking if a dashboard link existed *inside* a sidebar—whereas a standard prompt merely checked if the text existed anywhere on the page. Tips and Gotchas Beware the "command approval wall." When you hit your 5-hour or weekly usage limits, Codex CLI may continue to generate code but will fail when attempting to run Model Context Protocol (MCP) commands like `search_docs` or database seeds. These automatic approvals require an LLM call that is blocked when the quota is zero. Always check your dashboard before starting long-running autonomous tasks to ensure you have enough headroom for the final audit phase.
PHPUnit
Products
- May 2, 2026
- Apr 20, 2026
- Apr 9, 2026
- Jan 24, 2026
- Aug 27, 2025
Overview Laravel 12.4 introduces three distinct utility methods designed to streamline how we interact with data models, verify code execution in tests, and handle array constraints. These additions focus on developer ergonomics, providing cleaner alternatives to existing patterns. By integrating `Model::except()`, `assertThrowsNothing`, and `Arr::sole()`, developers can write more expressive code with less boilerplate. Prerequisites To get the most out of this tutorial, you should have a solid grasp of PHP and the Laravel ecosystem. Familiarity with Eloquent ORM, the `Arr` helper class, and the PHPUnit testing suite is essential. Key Libraries & Tools * **Laravel 12.4 Framework**: The core environment providing these new features. * **Eloquent ORM**: The database abstraction layer where the new `except()` method lives. * **Laravel Testing**: The integrated suite containing the updated exception assertions. Code Walkthrough Model Visibility with except() Previously, we used `makeHidden()` to hide attributes on a model instance. The new `except()` method offers a more intuitive syntax for returning all attributes except a specific few. ```python $user = User::first(); // Returns all attributes EXCEPT email and name return $user->except(['email', 'name']); ``` This method mirrors the behavior of the `only()` method but works as its logical inverse, making dynamic attribute filtering much cleaner during API development. Verifying Code Safety with assertThrowsNothing Testing that code fails correctly is easy with `assertThrows()`, but ensuring code *never* fails often resulted in implicit tests. Laravel 12.4 adds `assertThrowsNothing` to make this explicit. ```python $this->assertThrowsNothing(function () { (new ImportUsersAction)->handle(); }); ``` If any exception bubbles up during execution, the test fails immediately and provides a clear error message identifying the unexpected exception type. Strict Array Checks with Arr::sole() While the Eloquent builder has long supported `sole()`, we now have a dedicated helper for standard arrays. This ensures an array contains exactly one element. ```python use Illuminate\Support\Arr; $data = ['name' => 'Christoph']; $result = Arr::sole($data); // This will throw a MultipleRecordsFoundException if more than one item exists ``` Syntax Notes Notice the consistency in Laravel's naming conventions. The `except()` method on models now aligns with the `except()` method found in the Request and Collection classes. Similarly, `Arr::sole()` brings parity between database queries and raw data manipulation. Practical Examples * **Privacy Filters**: Use `except(['ssn', 'password'])` when logging model state to ensure sensitive data stays out of logs. * **Refactoring Tests**: Replace empty tests that rely on "no news is good news" with `assertThrowsNothing` to document intent clearly. Tips & Gotchas Avoid confusing `Model::except()` with database-level `select()`. The `except()` method works on a model instance that has already been retrieved; it does not filter the SQL query itself. For large datasets, always filter at the query level to save memory.
Apr 17, 2025Overview Laravel 12.2 continues the framework's tradition of refining developer experience by smoothing out common friction points. This update introduces more granular control over collection manipulation, a surgical approach to debugging test responses, and powerful extensions to Eloquent relationships. These features matter because they reduce the boilerplate code required for common tasks like data importing and complex relationship querying. Prerequisites To get the most out of this tutorial, you should have a solid grasp of: - **PHP 8.2+** syntax and features. - Core **Laravel** concepts like Eloquent relationships and Collections. - Basic automated testing using Pest or PHPUnit. Key Libraries & Tools - **Laravel Framework (v12.2)**: The primary PHP framework being updated. - **Eloquent ORM**: Laravel's database mapper used for the new relationship methods. - **Artisan**: The command-line interface for running imports and tests. Code Walkthrough Debugging with ddBody When testing, dumping a full response object often overwhelms the console. The new `ddBody()` method targets exactly what you need to see. ```python // Traditional way (too much noise) $response->dd(); // New surgical approach $response->ddBody(); // Targeted JSON debugging $response->ddBody('users'); ``` Passing a key to `ddBody` allows you to dive straight into nested JSON data without manually filtering the array. Contextual Increments Laravel's Context service now supports arithmetic operations, which is perfect for tracking progress in background jobs or Artisan commands. ```python Context::increment('users_imported_count', $chunk->count()); ``` This automatically tracks the value throughout the request cycle and attaches it to your logs, providing a clear audit trail of batch processes. One of Many Relationships You can now use `latestOfMany()` and `oldestOfMany()` on `HasOneThrough` relationships. This bridges the gap between complex three-table joins and clean Eloquent syntax. ```python public function latestComment() { return $this->hasOneThrough(Comment::class, Post::class) ->latestOfMany(); } ``` This replaces manual `orderBy` and `limit` calls with a semantic, readable method. Syntax Notes - **Chunking**: The `chunk($size, $preserveKeys = true)` method now allows passing `false` as the second argument to reset keys. - **Fluent Relationships**: The `one()` method converts a `HasManyThrough` into a `HasOneThrough` instance dynamically. Practical Examples Use `Context::increment` inside an Artisan command that parses CSV files. By incrementing a 'processed_rows' key, your log files will show exactly how many records were handled in that specific execution without you manually formatting the log message. Tips & Gotchas - **JSON Keys**: Remember that `ddBody('key')` only works if the response is valid JSON. If the response is HTML, it will return the full body string. - **Database Performance**: `latestOfMany()` is highly optimized, but ensure your foreign keys and timestamp columns are indexed to maintain speed on large datasets.
Mar 19, 2025Overview: The Philosophy of Efficient Code Generation Programming efficiency isn't just about typing faster; it's about reducing the cognitive load required to translate a mental architecture into working code. Blueprint represents a significant evolution in the Laravel ecosystem, moving beyond basic file stubbing into true application automation. While the framework provides robust tools like `php artisan make:model`, these commands often leave developers with a "facade" of code—empty classes that still require manual configuration of migrations, fillable attributes, and controller logic. Blueprint bridges this gap by leveraging Laravel conventions to infer intent. By providing a simple YAML definition, a developer can generate migrations with correct data types, models with defined relationships, and controllers with functional, tested logic. This matters because it eliminates the tedious, repetitive boilerplate that consumes the first several hours of any new feature or project. It turns a dozen manual steps into a single build command, ensuring that best practices—like form request validation and comprehensive testing—are baked into the codebase from the first second. Prerequisites and Environment Setup Before utilizing Blueprint, you should have a baseline understanding of the PHP programming language and the Laravel framework. Specifically, familiarity with Eloquent ORM relationships, database migrations, and RESTful controller patterns is essential. From a tooling perspective, you need a local Laravel installation (version 10 or 11 is recommended) and Composer for package management. To get started, install Blueprint as a development dependency: ```bash composer require --dev laravel-shift/blueprint ``` If you want to use Blueprint's specialized testing assertions, such as checking if a controller used a specific form request or dispatched a particular job, you should also include the testing helpers: ```bash composer require --dev jasonmccreary/laravel-test-assertions ``` Key Libraries & Tools * **Blueprint**: The core code generation tool that parses YAML files to create Laravel components. * **Laravel Shift**: The organization behind Blueprint, primarily known for automated framework upgrades. * **YAML**: A human-readable data serialization language used to define application drafts. * **Pest**: A modern PHP testing framework supported by Blueprint as an alternative to PHPUnit. * **Eloquent ORM**: Laravel's active record implementation which Blueprint automates. Code Walkthrough: From Draft to Implementation The workflow begins with a `draft.yaml` file. This file acts as the architect's sketch of the application. Let's break down a logical section involving a conference management system. 1. Defining the Model You don't need to specify IDs or timestamps; Blueprint assumes these by default. Focus on the unique attributes and relationships. ```yaml models: Conference: name: string:400 starts_at: datetime venue_id: id:venue relationships: hasMany: Talk, Attendee ``` In this snippet, `string:400` tells the generator to create a column with a specific length. The `venue_id: id:venue` syntax is a shorthand that creates a foreign key and establishes a `belongsTo` relationship in the Eloquent model. 2. Crafting the Controller Blueprint uses "controller statements" to define logic. These are keywords like `query`, `render`, `redirect`, and `store`. ```yaml controllers: Conference: index: query: all render: conference.index with: conferences store: validate: name, starts_at, venue_id save: conference flash: conference.id redirect: conference.index ``` When you run `php artisan blueprint:build`, this results in a `ConferenceController` where the `store` method automatically uses a generated `ConferenceStoreRequest` for validation. It also creates the `conference.index` blade view and a migration file for the `conferences` table. 3. Automatic Testing Generation One of the most powerful features is the testing output. Blueprint doesn't just create a test file; it writes functional tests that use Model Factories to populate data and assert that responses are correct. If you define a `mail` or `dispatch` statement in your controller, Blueprint will automatically add `Mail::fake()` and `Bus::fake()` to the test, ensuring the code is fully covered. Syntax Notes: Shorthands and Conventions Blueprint is built on the idea of "typing less to get more." Several syntax patterns facilitate this: * **The `resource` keyword**: Instead of defining every action, you can type `resource: web` or `resource: api` under a controller name. This expands into the full suite of resourceful methods (index, create, store, etc.). If you choose `api`, it swaps blade redirects for Eloquent API resources. * **Column Typing**: Blueprint uses the exact same names as Laravel migration methods (e.g., `nullable`, `string`, `text`, `unsignedInteger`). * **Relationship Inferences**: If you name a column `user_id`, Blueprint automatically adds a `belongsTo(User::class)` method to your model. * **Passivity**: Blueprint is a passive generator. It creates new files but generally avoids destructive edits to your existing logic, though it will append routes to your `web.php` or `api.php` files. Practical Examples: Trace and Existing Apps A common misconception is that Blueprint is only for "Greenfield" projects. However, the `blueprint:trace` command allows it to work with existing codebases. Imagine you have an existing `User` model but need to build an admin interface for it. By running `php artisan blueprint:trace`, Blueprint analyzes your existing models and migrations. You can then reference those existing models in a new `draft.yaml` to generate new controllers or tests that are fully aware of your existing database schema. This is a massive time-saver for expanding mature applications. Tips & Gotchas * **The "Nah" Shortcut**: When prototyping, you may generate code you don't like. A common community alias is `alias nah='git clean -df && git checkout .'`, which quickly wipes uncommitted changes so you can tweak your `draft.yaml` and try again. * **Configuring for Pest**: If you prefer Pest over PHPUnit, publish the config via `php artisan vendor:publish --provider="Blueprint\BlueprintServiceProvider"` and swap the test generator class. * **Stub Customization**: You can publish Blueprint's "stubs" (template files). If your team has a specific way of writing controllers or models that differs from the Laravel default, you can modify the stubs so that Blueprint always generates code in your specific style. * **Formatting YAML**: YAML is sensitive to indentation. Ensure your models and controllers are correctly nested, or the parser will fail to associate attributes with the correct parent component.
Oct 29, 2024Overview: Why Long-Running PHP Matters Most developers view PHP through the lens of PHP-FPM. This traditional model follows a "shared-nothing" architecture: a request arrives, the entire framework boots from scratch, the request is served, and the process dies. While this ensures a clean state and prevents memory leaks from accumulating, it introduces significant overhead. As applications scale, the milliseconds spent booting service providers and loading configuration files add up. Laravel Octane flips this script. It serves your application using high-performance runtimes that boot the framework once and keep it in memory to handle subsequent requests. This transition from short-lived scripts to long-running processes allows for "supersonic" speeds by eliminating the boot cycle. Understanding Octane isn't just about knowing how to install the package; it requires a mental shift regarding concurrency, I/O blocking, and state management. Prerequisites: Fundamentals of PHP Execution Before exploring Octane, you should have a solid grasp of how PHP interacts with web servers like Nginx. You should understand the difference between synchronous execution (tasks happening one after another) and parallel execution (multiple tasks happening at once on different CPU cores). Familiarity with Laravel service providers and the request/response lifecycle is essential, as Octane fundamentally alters how these components persist in memory. Key Libraries & Tools * **Swoole**: A high-performance networking framework for PHP written in C and C++. It provides event loops and coroutines. * **FrankenPHP**: A modern PHP app server written in Go. It integrates with the Caddy web server and supports features like early hints. * **RoadRunner**: An open-source, high-performance PHP application server and load balancer written in Go. * **Laravel Octane**: The abstraction layer that allows Laravel applications to interface with the runtimes above without changing core application logic. Code Walkthrough: How Octane Manages State Octane serves as an adapter between the runtime and Laravel. When a request hits a worker, Octane must ensure the application feels "fresh" even though it is actually a long-lived instance. It achieves this by cloning the application instance into a sandbox for every request. ```php // Conceptual representation of Octane's request handling $app = $worker->getApplication(); // The warm, booted instance $worker->onRequest(function ($request) use ($app) { // Clone the app to prevent state pollution across requests $sandbox = clone $app; // Convert the runtime-specific request to a Laravel request $laravelRequest = Request::createFromBase($request); // Handle the request through the sandbox $response = $sandbox->handle($laravelRequest); // Send response back to the runtime client return $response; }); ``` In this walkthrough, notice that the `$app` instance is booted only once when the worker starts. The `clone` operation is significantly faster than a full framework boot. Octane also listens for worker start events to prepare this state. In Swoole, this looks like a typical event-driven registration: ```php $server->on('workerStart', function ($server, $workerId) { // Octane boots the framework here and stores it in worker state $this->bootWorker($workerId); }); ``` Leveraging Concurrency with Task Workers One of Octane's most powerful features is the ability to execute tasks concurrently. In standard PHP, if you need to fetch data from three different APIs, you wait for each one sequentially. With Octane's concurrency support—specifically through Swoole—you can resolve multiple callbacks simultaneously. ```php [$users, $orders, $stats] = Octane::concurrently([ fn () => ExternalApi::getUsers(), fn () => ExternalApi::getOrders(), fn () => ExternalApi::getStats(), ]); ``` Behind the scenes, Octane offloads these closures to "task workers." These are separate processes that execute the code and return the results to the main request worker. The total time for the operation becomes the duration of the slowest task rather than the sum of all tasks. This is a game-changer for dashboards or data-heavy endpoints. Syntax Notes & Architectural Patterns * **Closures**: Octane relies heavily on closures to wrap logic that should execute per-request versus logic that executes at boot time. * **Dependency Injection**: You must be careful with injecting the `$request` object into long-lived singleton constructors. Because the singleton persists, it might hold onto the first request it ever saw, leading to stale data. * **Super Globals**: Octane abstracts away `$_GET`, `$_POST`, and `$_SERVER`. You should always use Laravel's request objects to ensure compatibility across different runtimes. Practical Examples: High-Traffic Optimization Octane shines in scenarios where response latency is critical. Consider a route that only serves data from Redis. In a standard environment, the PHP boot process might take 20ms, while the Redis query takes 1ms. You spend 95% of your time just starting the engine. With Octane, that 20ms boot time disappears after the first request, allowing the endpoint to respond in nearly real-time. Infrastructure cost reduction is another practical application. Because each worker spends less time waiting for I/O and no time on redundant boot cycles, a single server can handle significantly higher throughput, allowing you to scale down your horizontal footprint. Tips & Gotchas: Avoiding Memory Leaks and Stale State The biggest pitfall in Octane is "polluted state." If you store data in a static variable or a singleton during a request, that data remains there for the next user. Octane attempts to flush core Laravel state (like the authenticated user and session) automatically, but it cannot know about your custom static caches. **Best Practices:** 1. **Restart Workers**: Configure Octane to restart workers after a set number of requests (e.g., 500) to clear any minor memory leaks. 2. **Avoid Static Properties**: Don't use static properties to cache request-specific data. 3. **Test in Octane**: Always run your test suite against an Octane-like environment if you plan to deploy it, as state issues won't appear in standard PHPUnit runs.
Sep 9, 2024Overview Testing serves as the safety net for your application. It ensures that as you add features or refactor code, you don't accidentally break existing functionality. In the Laravel ecosystem, testing is a first-class citizen, providing developers with the tools to simulate user behavior, verify database states, and validate component rendering. Writing tests transforms your development process from "hoping it works" to "knowing it works." Prerequisites To follow along, you should have a baseline understanding of PHP and the Laravel framework. Familiarity with the command line is necessary for running Artisan commands. You should also understand the basics of Eloquent models and how routing works within a web application. Key Libraries & Tools * PEST: A functional testing framework for PHP focused on simplicity and readability. It offers a more expressive syntax compared to traditional class-based tests. * PHPUnit: The industry-standard testing framework for PHP. It uses a class-based approach where tests are defined as methods within a class. * Livewire%20Volt: An elegant, single-file component syntax for Livewire. It includes dedicated testing utilities for asserting component state. * Laravel%20Breeze: A minimal authentication scaffolding that comes pre-packaged with a comprehensive suite of tests, making it an excellent learning resource. Code Walkthrough: Your First Feature Test Let's break down the creation of a feature test for a To-Do manager. We want to ensure the page renders and that we can actually save data. Step 1: Generating the Test Run the following command to create a new test file: ```bash php artisan make:test ToDoTest ``` This creates a file in the `tests/Feature` directory. If you chose PEST during installation, it will use functional syntax; otherwise, it will use PHPUnit. Step 2: Testing Component Rendering We need to verify that a logged-in user can see our Livewire%20Volt component. ```python test('to do page is displayed', function () { $user = User::factory()->create(); $response = $this->actingAs($user) ->get('/dashboard'); $response->assertStatus(200); $response->assertSeeVolt('to-do-manager'); }); ``` Here, we use a factory to create a temporary user and `actingAs()` to simulate an authenticated session. The `assertSeeVolt` helper specifically checks if the Volt component is present on the page. Step 3: Testing Data Interaction Next, we test the logic of adding a task. We interact directly with the component state. ```python test('new to do can be added', function () { $user = User::factory()->create(); Volt::test('to-do-manager') ->set('title', 'My First Task') ->call('addToDo') ->assertHasNoErrors(); $this->assertDatabaseHas('to_dos', [ 'title' => 'My First Task', 'user_id' => $user->id, ]); }); ``` We use `Volt::test()` to mount the component, `set()` to fill the input field, and `call()` to execute the submission method. Finally, we check the database to ensure the record exists. Syntax Notes Notice the difference between **Feature** and **Unit** tests. Feature tests often use `$this->get()` or `$this->post()` to simulate HTTP requests. In PEST, we use the `test()` or `it()` functions, whereas PHPUnit requires `public function test_something()`. Always use the `refresh()` method on a model if you need to check its updated state after a database operation. Practical Examples * **Auth Gates:** Testing that only admins can access a specific dashboard. * **Form Validation:** Ensuring a user receives an error when they leave a required field blank. * **API Integrations:** Mocking a third-party payment gateway to verify your app handles successful and failed payments correctly. Tips & Gotchas Avoid the trap of testing implementation details. Focus on outcomes. If you change a variable name inside a method but the result remains the same, your test should still pass. A common mistake is forgetting to use the `RefreshDatabase` trait, which results in tests leaking data into each other. Always ensure your testing environment uses a dedicated database (like an in-memory SQLite instance) to keep runs fast and isolated.
Jul 30, 2024The Power of the Laravel Installer Setting up a new project often feels like a chore, but the Laravel Installer transforms this into a streamlined, interactive experience. While you can always rely on Composer to pull in the framework, the dedicated installer acts as a sophisticated wizard. It manages the boilerplate so you can focus on building features. If you use Laravel Herd, you already have this tool at your fingertips. Otherwise, a simple global installation via the command line gets you started. Prerequisites Before running your first command, ensure your environment meets these requirements: * **PHP 8.2+**: The latest Laravel versions require modern PHP features. * **Composer**: Essential for managing PHP dependencies. * **Database Driver**: Knowledge of SQLite, MySQL, or PostgreSQL. Interactive Project Scaffolding When you execute the `laravel new` command, the installer initiates a conversation. It doesn't just copy files; it configures your entire stack based on your preferences. ```bash Start a new project named 'nexus' laravel new nexus ``` You will choose between starter kits like Laravel Breeze for simple authentication or Laravel Jetstream for robust team management. You also decide on your frontend stack—Livewire for TALL stack enthusiasts or Vue.js with Inertia for those who prefer a single-page application feel. Choosing Your Testing Strategy Laravel prioritizes developer confidence. The installer asks whether you want to use PHPUnit or Pest. While PHPUnit is the industry standard, Pest provides a highly readable, functional syntax that many modern developers prefer for its expressive nature. Database and Migrations Modern development increasingly favors SQLite for its simplicity. The installer can automatically create your database file and run your initial migrations. This means that within seconds of finishing the prompt, you have a fully functional application with a working login system and database schema. Tips & Gotchas * **Latest Version Only**: The installer always pulls the latest stable release (e.g., Laravel 11). Use Composer directly if you need a specific legacy version. * **Git Initialization**: The installer offers to initialize a repository for you, saving another manual step in your workflow.
Jun 5, 2024Overview: Why Lazy Refreshing Matters Testing performance often hinges on how frequently you interact with the database. In Laravel, the standard `RefreshDatabase` trait ensures a clean state by running migrations for every test. While reliable, this becomes a bottleneck when your test suite contains methods that don't actually touch the database, such as unit tests for validation rules or domain logic. The LazilyRefreshDatabase trait solves this by deferring migrations until a database connection is actually requested. Prerequisites To follow this guide, you should be comfortable with PHP and the Laravel framework. Familiarity with PHPUnit or Pest testing structures is essential, as is a basic understanding of database migrations. Key Libraries & Tools * **Laravel Framework**: The primary PHP framework providing the testing traits. * **Ray**: A debug tool used here to monitor how many times migrations execute in real-time. * **MySQL**: Used to demonstrate behavior on persistent disk-based databases. * **In-Memory SQLite**: The common choice for fast, isolated test environments where lazy refreshing shines brightest. Code Walkthrough Consider a test class with mixed responsibilities. We have validation checks and database assertions in one file. ```php use Illuminate\Foundation\Testing\LazilyRefreshDatabase; class PodcastTest extends TestCase { use LazilyRefreshDatabase; /** @test */ public function validation_errors_are_correct() { // This test only checks array logic, no DB hit $this->post('/podcasts', [])->assertSessionHasErrors(['title']); } /** @test */ public function podcast_is_stored_in_database() { // This test hits the database Podcast::factory()->create(['title' => 'Laravel Gems']); $this->assertDatabaseHas('podcasts', ['title' => 'Laravel Gems']); } } ``` When using `RefreshDatabase`, the migrations run twice—once for each method. By switching to `LazilyRefreshDatabase`, the framework monitors the connection. It skips migrations for the validation test and only triggers them when the `Podcast::factory()` call occurs in the second test. Syntax Notes Laravel traits like LazilyRefreshDatabase utilize the `setUp` hook of the testing base class. The trait overrides the migration logic to wrap the connection in a closure that triggers the migration only upon the first "ping" to the database driver. Practical Examples This technique is a lifesaver for massive test suites using in-memory SQLite. In a file with 20 tests where only one requires a database, you reduce 20 migration cycles down to one. This significantly cuts down execution time in CI/CD pipelines. Tips & Gotchas If you use a real MySQL database, Laravel is already smart enough to skip migrations if the schema is cached. However, even with a real database, LazilyRefreshDatabase prevents any migration logic from running if the test never hits the wire. Avoid using this trait if your test relies on side effects of a migrated (but empty) database that isn't explicitly called via Eloquent or Query Builder.
Feb 9, 2024Overview Modern PHP testing requires more than just assertions; it demands a developer experience that is fluid, readable, and architecturally sound. Pest has evolved from a simple wrapper around PHPUnit into a powerhouse ecosystem that prioritizes simplicity without sacrificing depth. The latest enhancements focus on reducing the friction between writing code and verifying its integrity. By shifting from a class-based boilerplate to a functional, expectation-driven API, developers can focus on the intent of their tests rather than the structure of the testing framework itself. This guide explores the core enhancements that define the current state of the Pest ecosystem, including snapshot testing, architectural rules, and automated migration tools. Prerequisites To follow along with these patterns, you should have a baseline understanding of PHP 8.1+ and the Laravel framework. Familiarity with Composer for package management and a basic grasp of automated testing concepts—such as assertions and test suites—is necessary. You should have a local development environment where you can run terminal commands and execute PHP scripts. Key Libraries & Tools * **Pest**: An elegant PHP testing framework focused on simplicity and developer happiness. * **Laravel**: The web framework that provides the foundation for many of these testing patterns. * **Composer**: The dependency manager used to install Pest and its associated plugins. * **Drift Plugin**: A specialized tool designed to automate the conversion of PHPUnit tests into the Pest syntax. * **Architecture Plugin**: An extension for Pest that allows developers to define and enforce structural rules for their codebase. Code Walkthrough: From Boilerplate to Fluid Expectations Transitioning to Pest involves moving away from the verbose class-based structure of PHPUnit. In a traditional setup, you are burdened with namespaces, class declarations, and public function signatures. Pest replaces this with a clean, functional approach. The Functional API Instead of defining a class, you use the `it()` or `test()` functions. This drastically reduces the cognitive load when reading a test file. ```php // Before: PHPUnit public function test_it_has_a_welcome_page() { $response = $this->get('/'); $response->assertStatus(200); } // After: Pest it('has a welcome page', function () { $this->get('/')->assertStatus(200); }); ``` Chained Expectations Pest introduces an Expectation API that allows you to chain assertions on a single value, making the code read like a natural sentence. This avoids the repetitive passing of variables into multiple assertion methods. ```php // Using the Expectation API expect($value) ->toBeString() ->not->toBeInt() ->toContain('Laracon'); ``` In this snippet, `expect()` wraps the value, and the `not` modifier fluently negates the subsequent check. This is more than syntactic sugar; it prevents the common "needle vs. haystack" parameter confusion found in older assertion libraries. Advanced Features: Snapshots and Architecture Testing goes beyond simple values. Sometimes you need to verify that a large, complex output—like an entire HTML response—remains unchanged. Pest solves this with Snapshot testing. Snapshot Testing Instead of manually asserting against dozens of strings within a view, you can match the entire response against a stored "snapshot." ```php it('renders the about page correctly', function () { $response = $this->get('/about'); expect($response)->toMatchSnapshot(); }); ``` The first time this runs, Pest creates a reference file. Future runs compare the current output against that file. If you accidentally remove a CSS link or an SEO tag, the test fails immediately, even if the specific text you were looking for is still there. Architectural Testing One of the most powerful features in the Pest ecosystem is the ability to test the structure of the application itself. You can enforce that certain functions like `dd()` never make it to production, or that your models are only ever called within repository classes. ```php test('globals') ->expect(['dd', 'dump', 'ray']) ->not->toBeUsed(); test('architecture') ->expect('App\Models') ->toOnlyBeUsedIn('App\Repositories'); ``` This layer of testing prevents "architectural drift" where developers bypass established patterns, ensuring the codebase stays maintainable as the team grows. Syntax Notes Pest utilizes several modern PHP features to achieve its minimal syntax. High-order expectations allow you to perform assertions directly on properties or method returns of the expected value. The use of closures (anonymous functions) is the backbone of the framework, allowing for a localized scope for each test. Furthermore, Pest introduces the `describe` block pattern, common in JavaScript testing frameworks like Jest, to group related tests and apply localized hooks like `beforeEach()` to a specific subset of tests. Practical Examples Real-world applications of these tools are vast. Type coverage, for instance, is a critical metric for teams migrating legacy projects to modern, strictly-typed PHP. By running `vendor/bin/pest --type-coverage`, a team can identify exactly which methods lack return types or parameter hints. This can be integrated into a CI/CD pipeline with a minimum threshold, such as `--min=100`, to ensure no new untyped code is merged. Another example is using the Drift plugin to instantly modernize a Laravel Jetstream project, converting hundreds of standard PHPUnit tests into the more readable Pest format in seconds. Tips & Gotchas * **Snapshot Updates**: When you intentionally change a view or data structure, your snapshot tests will fail. Use the `--update-snapshots` flag to refresh the reference files. * **Parallel Testing**: Pest supports parallel execution out of the box. Use the `-p` flag to significantly speed up large test suites. * **Namespace Issues**: While Pest doesn't require namespaces for the test files themselves, ensure your `Pest.php` configuration file correctly maps your test folders to the appropriate base test classes (like Laravel's `TestCase`). * **The Drift Limit**: While the Drift plugin is remarkably accurate, always perform a manual code review after a large migration to ensure custom assertions or complex mocks were handled as expected.
Jul 26, 2023Overview: The Final Phase of Ergodnc In this session, we transition from foundational setup to the refined business logic required for a production-ready application. Building Ergodnc — a platform reminiscent of Airbnb for remote work offices — demands more than just basic CRUD operations. It requires a robust validation layer, automated notification systems, and secure data handling to ensure a seamless user experience. We focus on finalizing the reservation lifecycle, which includes creating, validating, and canceling bookings, while implementing best practices that make the code act as its own documentation. Refining an application at this stage involves tightening security and improving the developer experience through better testing. By leveraging Laravel's built-in features like **Encrypted Casts**, **Scheduled Commands**, and **JSON Resources**, we can solve complex problems with minimal boilerplate. This tutorial isn't just about making things work; it's about making them resilient and scalable. Prerequisites To follow along effectively, you should have a solid grasp of the following: * **PHP 8.x**: Familiarity with modern PHP syntax, including constructor promotion and arrow functions. * **Laravel Framework**: Understanding of Eloquent models, Artisan commands, and the Service Container. * **Automated Testing**: Basic knowledge of PHPUnit or Pest to interpret the test-driven approach used here. * **Database Fundamentals**: Familiarity with migrations and relational database concepts like foreign keys and indexes. Key Libraries & Tools * Laravel Framework: The primary PHP framework used to build the backend. * Artisan: Laravel's command-line interface for generating boilerplate and running tasks. * Eloquent ORM: The database mapper for managing office listings and reservations. * Laravel Forge: The tool planned for future production deployment to provision servers. * Laravel Vapor: A serverless deployment platform intended for the next phase of this series. Code Walkthrough: Validation and Reservations 1. Hardening the Reservation Validator A common mistake in Laravel is failing to use the validated data directly from the validator. We corrected this by ensuring the `create` method in the `UserReservationController` strictly uses data returned from the `validate()` method. This prevents the application from accidentally reading unvalidated input from the request helper. ```python Note: Using Python highlighting for PHP syntax visualization in Markdown $data = $validator->validate(); We now use $data['start_date'] instead of request('start_date') ``` Beyond basic syntax, we implemented business-level validation. For example, a user cannot book an office that is currently in a 'pending' or 'hidden' state. This logic resides in the controller to keep the flow transparent and easy to debug. 2. Scheduled Notifications for Active Bookings Rather than cluttering the queue with jobs scheduled months in advance, we utilized Laravel's **Console Kernel**. By creating a custom Artisan command, we can query the database daily for reservations starting that day and dispatch notifications efficiently. ```python Inside the handle method of our new Artisan command $reservations = Reservation::query() ->where('status', Reservation::STATUS_ACTIVE) ->where('start_date', now()->toDateString()) ->with('office.user') ->get(); foreach ($reservations as $reservation) { Notification::send($reservation->user, new UserReservationStarting($reservation)); Notification::send($reservation->office->user, new HostReservationStarting($reservation)); } ``` This approach keeps our queue clean and ensures that the system only processes what is relevant for the current 24-hour window. We eager-loaded the `office.user` relationship to avoid the notorious **N+1 query problem**, which would otherwise cripple performance as the number of daily reservations grows. 3. Secure Wi-Fi Credential Storage Privacy is paramount. When generating a Wi-Fi password for a reservation, we shouldn't store it as plain text. Laravel's **Encrypted Casts** provide a transparent way to handle this. By adding the cast to the `Reservation` model, the value is encrypted when saved to the database and decrypted when accessed via Eloquent. ```python In the Reservation Model protected $casts = [ 'wifi_password' => 'encrypted', ]; ``` In the migration, we used the `text` column type instead of `string` because the encrypted payload is significantly longer than the original plain-text password. This prevents data truncation issues. Syntax Notes: JSON Resources and Filtering Overriding API Paths When returning image data, the database usually only stores the relative file path. To make our API consumer-friendly, we use **JSON Resources** to transform this into a full URL. By using the `merge()` method within the resource, we can override the `path` attribute dynamically using the `Storage::url()` helper. Complex Tag Filtering Filtering offices by multiple tags requires ensuring an office matches *all* requested tags, not just *any*. We achieved this by using the `whereHas` method with a count condition. If a user filters by three tags, we query for offices where the count of matching tags is exactly three. This ensures the results are precise and relevant to the user's specific requirements. Practical Examples * **Booking Validation**: Preventing a user from reserving their own office or making a booking for the "same day," ensuring the host has time to prepare. * **Automated Communication**: Sending a "Your reservation starts today" email to the user and a "You have a guest arriving" email to the host at 12:01 AM. * **Cancellation Rules**: Restricting cancellations to active reservations that haven't started yet, protecting hosts from last-minute revenue loss. Tips & Gotchas * **Validator Safe Access**: Always use `$validator->validated()` to ensure you are only working with data that has passed your rules. This helps avoid "Mass Assignment" vulnerabilities. * **The N+1 Trap**: When looping through reservations to send notifications, always use `with()` to eager-load relationships. Running a separate query for every host in a loop of 100 reservations will significantly slow down your Artisan command. * **Queue Everything**: Any action that involves sending an email or interacting with an external API (like Mailgun or Postmark) should implement the `ShouldQueue` interface. This keeps your application's response times fast and provides built-in retry logic if an email provider is temporarily down. * **Encryption Limits**: Remember that Eloquent's `encrypted` cast is for storage security, not authentication. If you need to search the database by the Wi-Fi password, you cannot use this method, as the encrypted values will not match search queries.
Oct 11, 2021Overview: Refined Authentication and Filtering In this technical exploration, we tackle the architectural complexities of building a high-performance reservation system within the Laravel ecosystem. The primary objective involves streamlining the user experience for an office-rental platform—similar to Airbnb—where both tenants and hosts require granular control over their reservation data. We address critical authentication hurdles using Laravel Sanctum, optimize database interactions via lazy loading traits, and implement complex query grouping to ensure data integrity during date-range filtering. Mastering these patterns is essential for any developer building multi-tenant or marketplace applications. Authentication isn't just about logging in; it’s about ensuring that public endpoints can still identify users when a token is present. Similarly, filtering isn't just about `WHERE` clauses; it's about managing logical groupings in SQL to prevent data leakage between users. This guide breaks down the "why" behind these advanced patterns, moving beyond basic CRUD into professional-grade backend engineering. Prerequisites To follow this guide, you should have a solid grasp of the following: * **PHP 8.x**: Familiarity with closures and arrow functions. * **Laravel Framework**: Understanding of Controllers, Eloquent models, and the Service Container. * **RESTful API Design**: Knowledge of headers, query parameters, and JSON response structures. * **Testing Basics**: Experience with PHPUnit or Laravel's built-in testing suite. Key Libraries & Tools * **Laravel Sanctum**: Provides a featherweight authentication system for SPAs and mobile apps using API tokens. * **LazilyRefreshDatabase**: A newer Laravel trait that optimizes test performance by only running migrations when a database connection is actually requested. * **Composer**: The dependency manager for PHP, used here to ensure the framework is updated to leverage the latest `assertNotSoftDeleted` assertions. Solving the Sanctum Default Guard Dilemma A common friction point in Laravel APIs occurs when an endpoint is accessible to both guests and authenticated users. By default, Laravel uses the `web` (session) guard. If you are building a stateless API with Laravel Sanctum, your application will fail to identify the user even if a valid `Authorization` bearer token is sent, because it isn't looking for one. The Guard Switch To fix this globally, we update `config/auth.php` to set the default guard to `sanctum`. This ensures that even on routes not protected by the `auth:sanctum` middleware, the `auth()->user()` helper will correctly attempt to resolve the user from the token. However, this change has a ripple effect on your test suite. Standard testing helpers like `$this->actingAs($user)` default to the session guard, leading to `401 Unauthorized` errors in your tests because the application is now expecting a Sanctum token. Overriding actingAs in TestCase Instead of manually updating every test to use `Sanctum::actingAs()`, a cleaner approach involves overriding the method in your base `TestCase.php`. This maintains a clean API for your tests while ensuring the underlying logic matches your production authentication guard. ```python // Base TestCase.php public function actingAs(UserContract $user, $guard = null) { return Sanctum::actingAs($user, ['*']); } ``` This methodical override allows you to keep your test syntax succinct while bridging the gap between session-based testing and token-based production environments. Implementing Logical Query Grouping When filtering reservations by date ranges, developers often run into a logic bug where an `OR` condition breaks the security of the `user_id` constraint. If you write a query that looks for `user_id = 1` AND `start_date` is X OR `end_date` is Y, the `OR` might return records belonging to other users if they match the date condition. The Closure Fix To prevent this, we encapsulate the date logic inside a PHP closure. This forces Laravel to wrap those specific conditions in parentheses in the generated SQL. ```python $reservations = Reservation::query() ->where('user_id', auth()->id()) ->where(function ($query) use ($request) { $query->whereBetween('start_date', [$request->from_date, $request->to_date]) ->orWhereBetween('end_date', [$request->from_date, $request->to_date]); }) ->get(); ``` By grouping the `whereBetween` and `orWhereBetween` calls, we ensure the query remains restricted to the authenticated user's data regardless of how many date conditions we add. This is a non-negotiable best practice for data privacy. Advanced Filtering for Hosts While tenants filter by their own ID, hosts need to filter reservations across multiple offices they own. We use the `whereRelation` method for a clean, readable syntax that checks the owner of the office associated with a reservation. This avoids manual joins and keeps the code expressive. ```python $reservations = Reservation::query() ->whereRelation('office', 'user_id', auth()->id()) ->when($request->office_id, fn($q) => $q->where('office_id', $request->office_id)) ->when($request->status, fn($q) => $q->where('status', $request->status)) ->get(); ``` Syntax Notes: Modern Laravel Conventions Several modern conventions were utilized to keep the codebase lean: 1. **LazilyRefreshDatabase**: This trait is a performance booster. In a large test suite, skipping migrations for tests that only check basic logic (like validation) saves significant time. 2. **AssertNotSoftDeleted**: Instead of manually checking the database for a null `deleted_at` timestamp, this dedicated assertion provides a more semantic way to verify that a resource was not removed. 3. **HTTP Build Query**: When testing, using `http_build_query($params)` is a robust way to generate URL strings for GET requests, ensuring special characters are properly encoded. Practical Examples Consider a user searching for their past bookings to prepare for an expense report. They need to filter by a specific date range. Without the logical grouping discussed, the system might accidentally show them other people's bookings that occurred in the same month—a massive security breach. By implementing the closure-based grouping, the `user_id` check acts as a global filter that cannot be bypassed by the `OR` logic of the date search. Another example is the Host Dashboard. A host managing 10 different offices needs to see only the "Active" reservations for a specific "Downtown Loft." By using the `when()` helper in Eloquent, we build a dynamic query that only applies filters if the user provides them, keeping the API flexible and the controller clean. Tips & Gotchas * **The Validation Trap**: Don't skip validation just because a query might return empty results. Validating that a `to_date` is after a `from_date` prevents the SQL engine from processing nonsensical ranges and provides better feedback to the frontend. * **Query Performance**: If you are filtering by geographical location (e.g., nearest office), avoid using `SQRT` functions in your SQL if possible. Using squared distances for comparisons is significantly faster and achieves the same sorting result. * **Middleware Clarity**: Just because you set Sanctum as the default guard doesn't mean your routes are protected. You still need the `auth:sanctum` middleware for any endpoint that should be strictly private.
Sep 30, 2021