feat: tag-based product alternatives

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Doar Mihai
2026-10-17 16:06:27 +03:00
co-authored by Claude Sonnet 5.5
parent fbb4862454
commit c4e6173e46
4 changed files with 160 additions and 1 deletions
+26
View File
@@ -3,6 +3,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -173,6 +174,31 @@ class Product extends Model
return $this->belongsToMany(Tag::class);
}
/**
* Get approved products sharing tags with this one, best matches first.
* Single query; each result has a shared_tags_count attribute.
*/
public function alternatives(int $limit = 3): Collection
{
return static::query()
->approved()
->where('products.id', '!=', $this->id)
->join('product_tag', 'product_tag.product_id', '=', 'products.id')
->whereIn('product_tag.tag_id', function ($query) {
$query->select('tag_id')->from('product_tag')->where('product_id', $this->id);
})
->select('products.*')
->selectRaw('COUNT(product_tag.tag_id) as shared_tags_count')
->withCount('upvotes')
->groupBy('products.id')
->orderByDesc('shared_tags_count')
->orderByDesc('upvotes_count')
->orderByDesc('products.created_at')
->orderByDesc('products.id')
->limit($limit)
->get();
}
/**
* Scope a query to only include approved products.
*/