Skip to content

Impossible to order scope by score #11681

@jimblue

Description

@jimblue

Bug description

Hi gentlemen!
I've been working on a custom scope that queries entries from a collection having any terms from any taxonomies. It works great for filtering entries that share taxonomy terms, but I'm looking to improve the ordering by introducing a “match score.” In other words, I want entries that share two or more common terms to be ranked above entries that only share one common term.
Does anyone know how I could create a score based on the number of matching taxonomy terms for ordering the scope result? Any insights, best practices, or pointers to similar implementations would be greatly appreciated!
Thanks!

Here is the PHP Scope:

<?php
 
namespace App\Scopes;
 
use Statamic\Query\Builder;
use Statamic\Query\Scopes\Scope;
use Statamic\Taxonomies\LocalizedTerm;
 
class RelatedTerms extends Scope
{
    /**
     * Apply the scope.
     *
     * Expects $values to contain:
     * - 'id': current entry ID (to exclude)
     * - 'terms': an associative array where keys are taxonomy field names
     *            and values are single terms or arrays of terms.
     *
     * @param Builder $query
     * @param array   $values
     */
    public function apply($query, $values)
    {
        $id = $values['id'] ?? null;
        $terms = $values['query_terms'] ?? [];
 
        if (!$id || empty($terms)) {
            return;
        }
 
        $query->where('id', '!=', $id)->whereTaxonomyIn($this->termQueries($terms));
    }
 
    /**
     * Normalize a term or array of terms into an array of term query strings.
     *
     * Converts a LocalizedTerm or string into a string in the format: "taxonomy::slug".
     *
     * @param mixed $terms
     */
    protected function termQueries($terms): array
    {
        if ($terms instanceof LocalizedTerm) {
            return ["{$terms->taxonomy}::{$terms->slug}"];
        }
 
        if (\is_string($terms)) {
            return [$terms];
        }
 
        if (\is_array($terms)) {
            return array_map(static function ($term) {
                return $term instanceof LocalizedTerm ? "{$term->taxonomy}::{$term->slug}" : (string) $term;
            }, $terms);
        }
 
        return [];
    }
}

How to reproduce

For a stories collection linked to 2 taxonomies locations and topics:

{ collection:stories as="related_stories" query_scope="with_same_terms" :id="id" :query_terms="[locations, topics]" }}
  {{ title }}
{{ /collection:stories }}

You'll see that the query scope is working, but story sharing same locations and topics aren't place first.

Logs

Environment

Environment
Application Name: website
Laravel Version: 12.8.1
PHP Version: 8.4.5
Composer Version: 2.8.8
Environment: development
Debug Mode: ENABLED
URL: website.test
Maintenance Mode: OFF
Timezone: Europe/Paris
Locale: en

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

Drivers
Broadcasting: null
Cache: file
Database: sqlite
Logs: stack / single
Mail: smtp
Queue: redis
Session: file

Storage
public/storage: NOT LINKED

Statamic
Addons: 3
Sites: 1
Stache Watcher: Disabled (auto)
Static Caching: Disabled
Version: 5.52.0 PRO

Statamic Addons
rias/statamic-redirect: 3.10.5
spatie/statamic-responsive-images: 5.2.2
statamic/seo-pro: 6.6.3

Installation

Fresh statamic/statamic site via CLI

Additional details

No response

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