Overview: Relations as Pure PHP Code Eloquent relations often feel like magic, but they are essentially a sophisticated wrapper around a query builder and custom PHP logic. By source-diving into the Laravel framework, we can see that relations are objects that follow a specific lifecycle. Understanding this lifecycle allows us to break free from standard `belongsTo` or `hasMany` patterns and implement unconventional data connections, such as comma-separated ID strings, geospatial proximity, or even AI-generated predictions. When you realize that the database side is optional, the flexibility of Eloquent expands significantly. Prerequisites To get the most out of this tutorial, you should be comfortable with the following: * **PHP 8.x**: Proficiency with classes, anonymous functions, and array manipulation. * **Laravel Eloquent**: Basic knowledge of standard relationship types and model structures. * **Collections**: Familiarity with Laravel's `collect`, `map`, and `flatmap` methods. Key Libraries & Tools * **Eloquent**: The core database abstraction layer in Laravel. * **Faker**: A PHP library used to generate fake data for testing or prototyping. * **Prism**: A package used for integrating AI/LLM responses into PHP applications. * **MySQL Spatial Functions**: Database-level functions used for calculating distances between coordinates. Code Walkthrough: The Relationship Lifecycle Eager loading in Laravel happens in three distinct phases: adding constraints, fetching results, and matching results to parents. To create a custom relation, you must implement the `addEagerConstraint` and `match` methods. 1. Handling Comma-Separated IDs Suppose you have a legacy database where IDs are stored as a string: `"1,2,3"`. You can create a `HasSpeakers` class that extends `Relation`. In the `addEagerConstraint` method, we extract these IDs into a clean array to query the database efficiently. ```python public function addEagerConstraints(array $models) { $ids = collect($models) ->map(fn ($model) => explode(',', $model->speaker_ids)) ->flatten() ->unique(); $this->query->whereIn('id', $ids); } ``` 2. The Matching Phase After the database returns the related models, the `match` method associates them back to the original parent models. This is where we manually populate the relationship attribute on each model. ```python public function match(array $models, Collection $results, $relation) { $resultsById = $results->keyBy('id'); foreach ($models as $model) { $ids = explode(',', $model->speaker_ids); $matches = collect($ids)->map(fn ($id) => $resultsById->get($id))->filter(); $model->setRelation($relation, $matches); } return $models; } ``` Syntax Notes: Custom Instantiation You don't have to use Laravel's built-in methods to define a relation. Instead of calling `$this->hasMany()`, you can return your custom class directly within your model. This gives you full control over the constructor and any extra parameters, like distance thresholds for geospatial queries or custom prompts for AI logic. Practical Examples * **Geospatial Proximity**: Using the `ST_Distance_Sphere` function in MySQL to find all "Ice Cream Shops" within 500 meters of an "Event" coordinates. * **AI Predictions**: Using Prism to predict future events based on historical data, instantiating transient models on the fly. * **Fake Data Generation**: Using Faker to simulate relationships for a UI prototype when the backend table doesn't exist yet. Tips & Gotchas * **Stick to Conventions**: 99% of the time, standard relations are better for maintenance and team clarity. Only use custom relations when data is shaped irregularly. * **Cloning Models**: When matching results, consider cloning models if the same related record belongs to multiple parents to avoid state issues. * **Performance**: Heavy PHP processing inside the `match` loop can slow down large collections. Keep logic efficient.
Chris Morrell
People
Aug 2025 • 1 videos
High activity month for Chris Morrell. Laravel among the most active voices, with 1 videos across 1 sources.
Aug 2025
- Aug 8, 2025