Skip to content

Query scopes used by relationship/entries fieldtype don't receive queryScopes in params #14204

@joshdaugherty

Description

@joshdaugherty

Bug description

What happened?

When an entries (or relationship) field has query_scopes configured (e.g., my_awesome_scope), the index request (e.g., opening the "Link Entry" picker in the CP) is built with applyIndexQueryScopes($query, $request->all()). Each scope's apply($query, $params) is called with that same $params (i.e., $request->all()).

Statamic\Fieldtypes\Entries::getIndexQuery() passes $request->all() as the second argument to applyIndexQueryScopes() (src/Fieldtypes/Entries.php line 257); Statamic\Fieldtypes\Relationship::applyIndexQueryScopes() then passes that $params unchanged to each scope's apply($query, $params) (src/Fieldtypes/Relationship.php lines 349–354).

$params is just the raw request (config, page, perPage, etc.). The scope handles live inside the decoded config (used to set up the fieldtype), but that decoded config is never merged into $params — so $params never has queryScopes.

Any scope that needs to know which scope handles are active (e.g., by reading $params['queryScopes']) never sees them and cannot apply the right filter. The index then shows entries that should have been filtered out.

What did you expect to happen?

Scopes that rely on $params['queryScopes'] (or equivalent) to know which handles are in effect should receive that list. For example, a scope like MyAwesomeScope (see below) only applies when its handle is in $values['queryScopes']; when it runs, it does $query->whereIn('id', []) so the index is empty. I expected the field's configured query_scopes to be passed into $params (e.g., as queryScopes) before calling each scope's apply().

Root cause

In Statamic\Fieldtypes\Relationship::applyIndexQueryScopes(), the second argument to each scope's apply() is always $request->all(). The field's query_scopes config is never merged into that array, so scopes never receive the list of active scope handles in $params.

Use case: one scope class, many handles

Scopes can use the optional aliases() method so that one scope class is registered under multiple handles. Core supports this in src/Extend/RegistersItself.php: when a scope registers, if it defines aliases(), each returned handle is bound to the same class. That allows a single scope (e.g. EntriesByType) to back many handles (e.g. entries_by_type_basic, entries_by_type_fancy). In that case the scope must know which handle(s) were requested so it can apply the right filter; the only way to pass that context is via $params['queryScopes']. Without it, aliased scopes cannot behave correctly.

How to reproduce

  1. Create a custom query scope that extends Statamic\Query\Scopes\Scope, is registered with handle my_awesome_scope, and implements apply() as follows (only applies when its handle is in $values['queryScopes']; when applied, returns no results):
<?php

namespace App\Scopes;

use Statamic\Query\Scopes\Scope;

class MyAwesomeScope extends Scope
{
    protected static $handle = 'my_awesome_scope';

    public function apply($query, $values): void
    {
        if (
            ! \in_array(
                static::$handle,
                (array) ($values['queryScopes'] ?? []),
                true
            )
        ) {
            return;
        }

        $query->whereIn('id', []);
    }
}
  1. Add an entries (or relationship) field to a blueprint with:
    • collections: e.g., ['pages']
    • query_scopes: e.g., ['my_awesome_scope']
  2. Edit an entry that uses that blueprint and click "Link Entry" (or equivalent) to open the relationship index.
  3. Observe: The index shows all entries, because $values['queryScopes'] is missing and the scope never applies, so the "empty result" constraint is never added.
  4. Expected: The index shows no entries when my_awesome_scope is in query_scopes (scope runs and applies whereIn('id', [])).

Logs

Environment

Environment
Application Name: UAMS Statamic
Laravel Version: 12.54.0
PHP Version: 8.4.16
Composer Version: 2.8.11
Environment: local
Debug Mode: ENABLED
URL: uams-statamic.test
Maintenance Mode: OFF
Timezone: UTC
Locale: en

Cache
Config: NOT CACHED
Events: CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: file
Database: sqlite
Logs: stack / single
Mail: smtp
Queue: sync
Session: file

Storage
public/storage: NOT LINKED

Statamic
Addons: 7
Sites: 255 (University of Arkansas for Medical Sciences, UAMS Health, AR-IMPACT, and 252 more)
Stache Watcher: Enabled (auto)
Static Caching: Disabled
Version: 6.5.0 PRO

Statamic Addons
el-schneider/statamic-admin-bar: 0.3.1
jacksleight/statamic-bard-mutator: 3.0.5
jacksleight/statamic-bard-texstyle: 4.0.2
statamic/eloquent-driver: 5.3.0
statamic/ssg: 4.1.0
studio1902/statamic-peak-seo: 11.0.2
studio1902/statamic-peak-tools: 9.0.2

Statamic Eloquent Driver
Addon Settings: file
Asset Containers: file
Assets: eloquent
Blueprints: file
Collection Trees: file
Collections: file
Entries: file
Fieldsets: file
Form Submissions: file
Forms: file
Global Sets: file
Global Variables: file
Navigation Trees: file
Navigations: file
Revisions: file
Sites: file
Taxonomies: file
Terms: file
Tokens: file

Installation

Fresh statamic/statamic site via CLI

Additional details

Merge the field's query_scopes into $params before calling each scope, so that $params includes the active scope handles. For example:

protected function applyIndexQueryScopes($query, $params)
{
    $params = [
        ...$params,
        'queryScopes' => Arr::wrap($this->config('query_scopes')),
    ];

    collect(Arr::wrap($this->config('query_scopes')))
        ->map(fn ($handle) => Scope::find($handle))
        ->filter()
        ->each(fn ($scope) => $scope->apply($query, $params));
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions