CMS Framework - v2.2.2
Includable Relationships
The HasIncludableRelationships trait provides on-demand relationship loading via the ?include= query parameter. Introduced in v1.1.0, it allows API consumers to control which relationships are eager-loaded in a response while maintaining backwards compatibility through default includes.
How It Works
Controllers using this trait define two properties:
$includableRelationships-- An allowlist of relationship names that the API consumer is permitted to request.$defaultIncludes-- An array of relationships to load when theincludequery parameter is absent, preserving backwards-compatible behavior.
The trait provides a single method, getRequestedIncludes(), that the controller calls when building a query to determine which relationships to eager-load.
Behavior
| Request | Result |
|---|---|
No include parameter |
Loads $defaultIncludes (backwards compatible) |
?include=roles |
Loads only roles (if it is in the allowlist) |
?include=roles,permissions |
Loads roles and permissions (both must be in the allowlist) |
?include= (empty string) |
Loads no relationships |
?include=roles,invalid |
Loads only roles; invalid is silently filtered out |
Invalid relationship names are silently ignored -- they never produce an error. This prevents leaking information about internal model structure and provides a stable API surface.
Currently Used On
The trait is currently applied to these controllers:
| Controller | Includable Relationships | Default Includes |
|---|---|---|
UserController |
roles |
roles |
RoleController |
permissions |
permissions |
PermissionController |
roles |
roles |
PostController |
varies | varies |
PageController |
varies | varies |
PostCategoryController |
varies | varies |
PageCategoryController |
varies | varies |
Example API Calls
Load users with their roles (default behavior -- no parameter needed):
curl -H "Authorization: Bearer TOKEN" \
https://your-app.test/api/v1/users
Explicitly request roles:
curl -H "Authorization: Bearer TOKEN" \
"https://your-app.test/api/v1/users?include=roles"
Load users with no relationships (override the default):
curl -H "Authorization: Bearer TOKEN" \
"https://your-app.test/api/v1/users?include="
Request multiple relationships (on a controller that supports them):
curl -H "Authorization: Bearer TOKEN" \
"https://your-app.test/api/v1/roles?include=permissions"
Adding to Your Own Controllers
- Import and use the trait in your controller.
- Define
$includableRelationshipswith the allowed relationship names. - Define
$defaultIncludeswith the relationships to load by default. - Call
$this->getRequestedIncludes( $request )when building your query.
<?php
namespace App\Http\Controllers;
use ArtisanPackUI\CMSFramework\Http\Controllers\Concerns\HasIncludableRelationships;
use App\Models\Product;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ProductController extends Controller
{
use HasIncludableRelationships;
/**
* Relationships the API consumer may request.
*
* @var array<int, string>
*/
protected array $includableRelationships = ['category', 'tags', 'reviews'];
/**
* Relationships loaded when no include parameter is provided.
*
* @var array<int, string>
*/
protected array $defaultIncludes = ['category'];
public function index( Request $request ): JsonResponse
{
$includes = $this->getRequestedIncludes( $request );
$products = Product::query()
->with( $includes )
->paginate();
return response()->json( $products );
}
}
With this setup:
GET /productsloads products withcategory(the default).GET /products?include=tags,reviewsloads products withtagsandreviewsonly.GET /products?include=loads products with no relationships.GET /products?include=category,nonexistentloads products withcategoryonly;nonexistentis silently dropped.