-
Notifications
You must be signed in to change notification settings - Fork 98
Description
Bug description
When using the Eloquent driver for both taxonomies and terms, API requests to /api/taxonomies/{handle}/terms fail with:
Call to a member function sites() on null
The error occurs in src/Taxonomies/Term.php:152 in the defaultLocale() method.
How to reproduce
-
Configure Statamic to use Eloquent driver for:
taxonomiestermssites
-
Seed taxonomy terms to the database using
dataForLocale('default', [...]) -
Make an API request to
/api/taxonomies/organization_types/terms -
The request fails with the error above
Root cause analysis
The issue is in Term::fromModel() in the eloquent-driver (vendor/statamic/eloquent-driver/src/Taxonomies/Term.php:26):
$term = (new static)
->slug($model->slug)
->taxonomy($model->taxonomy) // Sets taxonomy handle as string
->model($model)
->blueprint($model->data['blueprint'] ?? null);
collect($data['localizations'] ?? [])
->except($term->defaultLocale()) // <-- Calls taxonomy()->sites()->first()When defaultLocale() is called, it invokes $this->taxonomy() (the getter), which does:
->getter(function ($taxonomy) {
return $taxonomy ? Blink::once("taxonomy-{$taxonomy}", function () use ($taxonomy) {
return Taxonomy::findByHandle($taxonomy);
}) : null;
})The problem: Due to timing issues in the request lifecycle, Blink::once() can cache null even when the taxonomy exists in the database. Once null is cached, subsequent calls return null, causing ->sites() to fail.
Evidence from debugging
- Warming the Blink cache with
Blink::put("taxonomy-{$handle}", $taxonomy)in middleware before the controller runs does NOT fix the issue - The cache shows
has('taxonomy-organization_types') = trueboth before AND after the controller fails with 500 Taxonomy::all()andTaxonomy::findByHandle()work correctly in isolation (e.g., in tinker)- The issue only manifests during web API requests with the Eloquent driver
Workaround
We implemented a middleware that intercepts /api/taxonomies/{handle}/terms requests and queries the database directly, bypassing Term::fromModel() entirely:
$terms = DB::table('taxonomy_terms')
->where('taxonomy', $taxonomyHandle)
->where('site', 'default')
->get();Versions
| Software | Version |
|---|---|
| Statamic | 5.x (latest) |
| Laravel | 11.x |
| Eloquent Driver | latest |
| PHP | 8.3 |