PHPackages                             quellabs/recommender - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. quellabs/recommender

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

quellabs/recommender
====================

Collaborative filtering recommendation engine (item-based CF and Slope One) using CakePHP 5 database layer

1.0.0(1mo ago)00MITPHPPHP &gt;=8.2

Since Jun 23Pushed 1mo agoCompare

[ Source](https://github.com/quellabs/recommender)[ Packagist](https://packagist.org/packages/quellabs/recommender)[ RSS](/packages/quellabs-recommender/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

quellabs/recommender
====================

[](#quellabsrecommender)

Collaborative filtering recommendation engine for PHP 8.2+, built on the CakePHP 5 database layer. Implements two complementary algorithms:

- **Item-based collaborative filtering** — "people who liked A also liked B"
- **Slope One** — lightweight weighted rating prediction for unrated items

Based on the [Vogoo PHP recommendation engine](http://www.vogoo.net/) (2007–2008) by Stéphane Droux, modernised for PHP 8.2+ and the Quellabs ecosystem.

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- `cakephp/database` ^5.0
- MySQL (the incremental link update queries use MySQL-specific `UPDATE ... JOIN` and `INSERT ... ON DUPLICATE KEY UPDATE` syntax)

Installation
------------

[](#installation)

```
composer require quellabs/recommender
```

Setup
-----

[](#setup)

### 1. Publish the configuration file

[](#1-publish-the-configuration-file)

```
sculpt recommender:init
```

This copies `config/recommender.php` to your project root. Edit it to tune the engine constants. Database credentials are read from `config/database.php`, which is shared with other Canvas packages.

### 2. Create the database tables

[](#2-create-the-database-tables)

```
sculpt recommender:init-db
```

Creates `vogoo_ratings` and `vogoo_links`. Use `--force` to drop and recreate existing tables.

### 3. Populate the link table

[](#3-populate-the-link-table)

If you have existing ratings, rebuild the link table from scratch:

```
sculpt recommender:rebuild-links
```

To rebuild a single category only:

```
sculpt recommender:rebuild-links --category=2
```

Database schema
---------------

[](#database-schema)

### `vogoo_ratings`

[](#vogoo_ratings)

Stores member/product ratings.

ColumnTypeDescription`member_id`INT UNSIGNEDYour application's user ID`product_id`INT UNSIGNEDYour application's product/item ID`category`INT UNSIGNEDCategory grouping (default: 1)`rating`FLOAT0.0–1.0, or -1.0 for "not interested"`ts`DATETIMELast updated timestamp### `vogoo_links`

[](#vogoo_links)

Stores pre-computed item co-occurrence counts and Slope One diff values. Populated by the rebuild command or maintained incrementally.

ColumnTypeDescription`item_id1`INT UNSIGNEDFirst item in the pair`item_id2`INT UNSIGNEDSecond item in the pair`category`INT UNSIGNEDCategory grouping`cnt`INTCo-occurrence count`diff_slope`FLOATAccumulated Slope One rating differentialConfiguration
-------------

[](#configuration)

All options with their defaults:

```
// config/recommender.php
return [
    // Default category used when no category is passed to engine methods
    'category' => 1,

    // Minimum number of common ratings before similarity is considered reliable
    'threshold_nr_common_ratings' => 30,

    // Multiplier used in the similarity confidence calculation
    'threshold_mult' => 2,

    // Minimum rating for an item to count as "liked" in link calculations
    'threshold_rating' => 0.66,

    // Cost factor used in the member similarity spread calculation
    'cost' => 5.0,

    // Sentinel value stored to mark "not interested" (must be negative)
    'not_interested' => -1.0,

    // Maintain vogoo_links incrementally on every rating change.
    // When false, run "sculpt recommender:rebuild-links" after bulk imports.
    'direct_links' => false,
    'direct_slope' => true,
];
```

`direct_links` and `direct_slope` are independent. You can enable either or both:

`direct_links``direct_slope`Powers`getLinkedItems()`, `memberGetRecommendedItems()``getSlopeItems()`, `memberPredict()`, `memberPredictAll()`CountsCo-occurrence (liked pairs only)All rated pairsWhen both are `false`, `vogoo_links` is read-only at runtime and must be rebuilt manually.

Usage
-----

[](#usage)

### With Canvas (autowired)

[](#with-canvas-autowired)

`RecommendationEngine` and `ItemRecommender` are resolved automatically by the Canvas DI container — their constructors depend only on `Connection` (provided by `quellabs/canvas-database`) and `RecommendationConfig`(provided by this package), so no manual wiring is required:

```
use Quellabs\Recommender\ItemRecommender;
use Quellabs\Recommender\RecommendationEngine;

class ProductController
{
    public function __construct(
        private RecommendationEngine $engine,
        private ItemRecommender $recommender,
    ) {}
}
```

### Standalone

[](#standalone)

```
use Cake\Database\Connection;
use Cake\Database\Driver\Mysql;
use Quellabs\Recommender\Config\RecommendationConfig;
use Quellabs\Recommender\RecommendationEngine;
use Quellabs\Recommender\ItemRecommender;

$connection = new Connection([
    'driver'   => Mysql::class,
    'host'     => 'localhost',
    'username' => 'root',
    'password' => '',
    'database' => 'mydb',
]);

$config    = new RecommendationConfig();
$engine    = new RecommendationEngine($connection, $config);
$recommender = new ItemRecommender($connection, $config);
```

---

API reference
-------------

[](#api-reference)

### `RecommendationEngine`

[](#recommendationengine)

Handles rating CRUD. All write methods trigger incremental link/slope updates when `direct_links` or `direct_slope` is enabled.

```
// Record ratings
$engine->setRating($memberId, $productId, 0.8);
$engine->automaticRating($memberId, $productId, purchase: true);  // 1.0
$engine->automaticRating($memberId, $productId, purchase: false); // 0.7, or +0.01
$engine->setNotInterested($memberId, $productId);

// Read ratings
$engine->getRating($memberId, $productId);           // ['rating' => 0.8, 'ts' => '...']
$engine->memberRatings($memberId);                   // [['product_id', 'rating', 'ts'], ...]
$engine->memberNumRatings($memberId);
$engine->memberAverageRating($memberId);
$engine->productRatings($productId);
$engine->productNumRatings($productId);
$engine->productAverageRating($productId);

// Delete
$engine->deleteRating($memberId, $productId);
$engine->deleteMember($memberId);
$engine->deleteProduct($productId);
```

### `ItemRecommender`

[](#itemrecommender)

Item-based CF and Slope One recommendations.

```
// Item-based CF (requires direct_links or rebuild)
$recommender->getLinkedItems($productId);                          // [productId, ...]
$recommender->memberGetRecommendedItems($memberId);                // [productId, ...]
$recommender->memberGetReasons($memberId, $productId);             // [productId, ...]

// Slope One (requires direct_slope or rebuild)
$recommender->memberPredict($memberId, $productId);                // float|null
$recommender->memberPredictAll($memberId);                         // [['product_id', 'rating'], ...]
$recommender->getSlopeItems($productId);                           // [['product_id', 'diff'], ...]

// Anonymous visitors (pass a VisitorContext instead of a member ID)
$recommender->visitorGetRecommendedItems($visitor);
$recommender->visitorPredict($visitor, $productId);
$recommender->visitorPredictAll($visitor);
```

All methods accept an optional `$filter` (array of allowed product IDs), `$limit`, and `$category` parameter.

### `UserSimilarity`

[](#usersimilarity)

User-based CF. Similarity scores range from 0 (no overlap) to 100 (identical taste).

```
$similarity = $userSimilarity->memberSimilarity($memberId1, $memberId2); // int 0–100
$neighbours = $userSimilarity->getNeighbours($memberId, minSimilarity: 10, limit: 20);
$items      = $userSimilarity->memberGetRecommendedItems($memberId);
```

> **Note:** `getNeighbours()` computes similarity against every candidate neighbour in PHP. For large member sets, pre-compute and cache neighbour lists.

### `VisitorContext`

[](#visitorcontext)

Holds in-memory ratings for anonymous visitors. Persist and restore it across requests via session serialization.

```
$visitor = new VisitorContext($config);
$visitor->setRating($productId, 0.9);
$visitor->setNotInterested($productId);
$visitor->removeRating($productId);
$visitor->getRatings();         // [['product_id', 'rating', 'category'], ...]
$visitor->getRatedProductIds(); // [productId, ...]
```

### `Statistics`

[](#statistics)

```
$stats->numMembers();
$stats->numProducts();
$stats->numRatings();
$stats->numLinks();
$stats->mostRatedProducts(limit: 10);  // [['product_id', 'num_ratings'], ...]
$stats->topRatedProducts(limit: 10, minRatings: 5); // [['product_id', 'avg_rating'], ...]
```

Sculpt CLI commands
-------------------

[](#sculpt-cli-commands)

CommandDescription`sculpt recommender:init`Publish `config/recommender.php``sculpt recommender:init-db`Create `vogoo_ratings` and `vogoo_links` tables`sculpt recommender:init-db --force`Drop and recreate tables`sculpt recommender:rebuild-links`Rebuild `vogoo_links` from all ratings`sculpt recommender:rebuild-links --category=N`Rebuild a single categoryMulti-category support
----------------------

[](#multi-category-support)

Every method accepts an optional `?int $category` parameter. When omitted it falls back to the `category` value in `RecommendationConfig` (default: 1). To work with multiple catalogues, either pass the category explicitly or instantiate separate `RecommendationConfig` objects per category:

```
$booksConfig    = new RecommendationConfig(category: 1);
$moviesConfig   = new RecommendationConfig(category: 2);

$booksEngine  = new RecommendationEngine($connection, $booksConfig);
$moviesEngine = new RecommendationEngine($connection, $moviesConfig);
```

License
-------

[](#license)

MIT

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

45d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/57e4ab872b3e37536367f2d26b192df3d3bb6a6a1cebec9a104d14a6d2ffe157?d=identicon)[noescom](/maintainers/noescom)

### Embed Badge

![Health badge](/badges/quellabs-recommender/health.svg)

```
[![Health](https://phpackages.com/badges/quellabs-recommender/health.svg)](https://phpackages.com/packages/quellabs-recommender)
```

###  Alternatives

[robmorgan/phinx

Phinx makes it ridiculously easy to manage the database migrations for your PHP app.

4.5k49.7M483](/packages/robmorgan-phinx)[cakephp/migrations

Database Migration plugin for CakePHP

13413.2M314](/packages/cakephp-migrations)[cakephp/orm

CakePHP ORM - Provides a flexible and powerful ORM implementing a data-mapper pattern.

152255.5k86](/packages/cakephp-orm)[zubzet/framework

 A lightweight PHP framework focused on rapid development, ease of use, and great portability.

1370.8k1](/packages/zubzet-framework)[jord-jd/array_undot

array\_undot (the opposite of the array\_dot helper function) expands a dot notation array into a full multi-dimensional array.

241.3k3](/packages/jord-jd-array-undot)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
