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
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:
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
Installation
Fresh statamic/statamic site via CLI
Additional details
No response