1. Go to this page and download the library: Download andydefer/laravel-cluster library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
namespace App\Services;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Registry\SqlFunctionRegistry;
class UserFilterService
{
public function __construct(
private readonly ClusterService $clusterService,
private readonly ClusterQuery $clusterQuery,
private readonly SqlFunctionRegistry $sqlRegistry
) {}
public function filter(array $criteria)
{
// Utilisation de $this->clusterService...
}
}
use AndyDefer\LaravelCluster\Lexer;
use AndyDefer\LaravelCluster\Parser;
use AndyDefer\LaravelCluster\ClusterQuery;
// 1. Vous écrivez une requête
$query = 'status=active & age>25 & COUNT(addresses)>2';
// 2. Le Lexer tokenise l'expression
$lexer = new Lexer();
$tokens = $lexer->tokenize($query);
// 3. Le Parser construit l'AST
$parser = new Parser();
$ast = $parser->parse($query);
// 4. Le ClusterQuery exécute l'AST
$clusterQuery = new ClusterQuery();
// Évaluation en mémoire
$result = $clusterQuery->filter($collection, $query);
// Génération SQL
$sql = $clusterQuery->toSql('clusters', $query, DatabaseDriver::MYSQL);
// Application Eloquent
$clusterQuery->applyToEloquent($queryBuilder, 'clusters', $query, DatabaseDriver::MYSQL);
use AndyDefer\LaravelCluster\Lexer;
use AndyDefer\LaravelCluster\Parser;
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Registry\SqlFunctionRegistry;
use AndyDefer\LaravelCluster\Registry\AggregateFunctionRegistry;
// 1. Lexer - Tokenise une expression
$tokens = (new Lexer())->tokenize('status=active');
// 2. Parser - Construit l'AST
$ast = (new Parser())->parse('status=active');
// 3. ClusterQuery - Moteur central
$engine = new ClusterQuery();
$filtered = $engine->filter($clusters, 'status=active');
// 4. ClusterService - Façade
$service = new ClusterService($engine);
$filtered = $service->filter($clusters, 'status=active');
// 5. Registres - Gestion des fonctions
$sqlRegistry = new SqlFunctionRegistry();
$aggRegistry = new AggregateFunctionRegistry();
use AndyDefer\LaravelCluster\ClusterQuery;
// Création simple
$engine = new ClusterQuery();
// Via le conteneur Laravel
$engine = app(ClusterQuery::class);
use AndyDefer\LaravelCluster\ClusterQuery;
$engine = new ClusterQuery();
// Parse une requête simple
$ast = $engine->parse('status=active');
// Parse une requête complexe
$ast = $engine->parse('status=active & (role=admin | role=doctor)');
// Parse une requête avec fonction SQL
$ast = $engine->parse('COUNT(addresses) > 2');
// Parse une sous-condition
$ast = $engine->parse('addresses[city=Kinshasa]');
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
$engine = new ClusterQuery();
// Créer une collection
$clusters = new ClusterVOCollection();
$clusters->add(new ClusterVO(['status' => 'active', 'age' => 25]));
$clusters->add(new ClusterVO(['status' => 'inactive', 'age' => 30]));
$clusters->add(new ClusterVO(['status' => 'active', 'age' => 18]));
// Filtrer
$filtered = $engine->filter($clusters, 'status=active & age>=20');
// Résultat : 1 cluster (age=25)
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
$engine = new ClusterQuery();
$cluster = new ClusterVO([
'status' => 'active',
'role' => 'admin',
'age' => 30,
]);
// Test simple
$matches = $engine->matches($cluster, 'status=active'); // true
// Test avec AND
$matches = $engine->matches($cluster, 'status=active & role=admin'); // true
// Test avec OR
$matches = $engine->matches($cluster, 'status=active | status=pending'); // true
// Test avec fonction
$matches = $engine->matches($cluster, 'age>25'); // true
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
$engine = new ClusterQuery();
// SQL pour MySQL
$sql = $engine->toSql('clusters', 'status=active', DatabaseDriver::MYSQL);
// JSON_EXTRACT(clusters, '$."status"') = 'active'
// SQL pour SQLite
$sql = $engine->toSql('clusters', 'status=active', DatabaseDriver::SQLITE);
// json_extract(clusters, '$.status') = 'active'
// SQL pour PostgreSQL
$sql = $engine->toSql('clusters', 'status=active', DatabaseDriver::PGSQL);
// clusters->>'status' = 'active'
// SQL avec fonction
$sql = $engine->toSql('clusters', 'COUNT(addresses) > 2', DatabaseDriver::SQLITE);
// json_array_length(clusters, '$.addresses') > 2
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use App\Models\User;
$engine = new ClusterQuery();
$query = User::query();
// Condition simple
$engine->applyToEloquent($query, 'clusters', 'status=active', DatabaseDriver::MYSQL);
// Conditions multiples
$engine->applyToEloquent($query, 'clusters', 'status=active & role=admin', DatabaseDriver::MYSQL);
// Sous-condition
$engine->applyToEloquent($query, 'clusters', 'addresses[city=Kinshasa]', DatabaseDriver::MYSQL);
// Fonction SQL
$engine->applyToEloquent($query, 'clusters', 'COUNT(addresses) > 2', DatabaseDriver::MYSQL);
$users = $query->get();
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\ClusterQuery;
// Création manuelle
$service = new ClusterService(new ClusterQuery());
// Via le conteneur
$service = app(ClusterService::class);
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
$service = app(ClusterService::class);
// 1. Parser
$ast = $service->parse('status=active');
// 2. Filtrer une collection
$clusters = new ClusterVOCollection();
$filtered = $service->filter($clusters, 'status=active');
// 3. Tester un cluster
$cluster = new ClusterVO(['status' => 'active']);
$matches = $service->matches($cluster, 'status=active'); // true
// 4. Générer du SQL
$sql = $service->toSql('clusters', 'status=active', DatabaseDriver::MYSQL);
// 5. Appliquer à Eloquent
$query = User::query();
$service->applyToEloquent($query, 'clusters', 'status=active', DatabaseDriver::MYSQL);
$users = $query->get();
namespace App\Services;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use App\Models\Product;
class ProductFilterService
{
public function __construct(
private readonly ClusterService $clusterService
) {}
public function filterProducts(array $filters): array
{
$query = Product::query();
// Construction de la requête
$conditions = [];
foreach ($filters as $key => $value) {
if ($key === 'min_price') {
$conditions[] = "price>=$value";
} elseif ($key === 'max_price') {
$conditions[] = "price<=$value";
} elseif ($key === 'category') {
$conditions[] = "category=$value";
}
}
$queryString = implode(' & ', $conditions);
if (!empty($queryString)) {
$this->clusterService->applyToEloquent(
$query,
'attributes',
$queryString,
DatabaseDriver::MYSQL
);
}
return $query->get()->toArray();
}
}
namespace App\Models;
use AndyDefer\LaravelCluster\Casts\ClusterCast;
use Illuminate\Database\Eloquent\Model;
final class User extends Model
{
protected $casts = [
'metadata' => ClusterCast::class,
];
}
// Création avec un tableau
$user = User::create([
'name' => 'John Doe',
'metadata' => [
'status' => 'active',
'role' => 'admin',
'preferences' => [
'theme' => 'dark',
'notifications' => 'yes',
],
],
]);
// Lecture - automatiquement converti en ClusterVO
$cluster = $user->metadata;
// Accès comme un tableau (ArrayAccess)
$status = $cluster['status']; // 'active'
$theme = $cluster['preferences.theme']; // 'dark'
// Accès via get()
$role = $cluster->get('role'); // 'admin'
// Mise à jour
$user->metadata = [
'status' => 'inactive',
'role' => 'doctor',
];
$user->save();
// Filtrage Eloquent avec whereCluster
$activeAdmins = User::whereCluster('metadata', 'status=active & role=admin')->get();
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
// Création vide
$collection = new ClusterVOCollection();
// Ajout d'éléments
$collection->add(new ClusterVO(['name' => 'John', 'status' => 'active']));
$collection->add(new ClusterVO(['name' => 'Jane', 'status' => 'inactive']));
$collection->add(new ClusterVO(['name' => 'Bob', 'status' => 'active']));
// where - Égalité
$active = $collection->where('status', 'active');
// John, Bob
// whereNot - Différent
$notActive = $collection->whereNot('status', 'active');
// Jane
// whereYes - Égal à 'yes'
$verified = $collection->whereYes('verified');
// whereNo - Égal à 'no'
$unverified = $collection->whereNo('verified');
// whereIn - Dans une liste
$admins = $collection->whereIn('role', ['admin', 'super_admin']);
// whereNotIn - Hors liste
$nonAdmins = $collection->whereNotIn('role', ['admin', 'super_admin']);
// whereHas - La clé existe
$hasEmail = $collection->whereHas('email');
// whereMissing - La clé n'existe pas
$noEmail = $collection->whereMissing('email');
// whereNull - La valeur est null
$nullAge = $collection->whereNull('age');
// whereNotNull - La valeur n'est pas null
$hasAge = $collection->whereNotNull('age');
// whereContains - Contient une sous-chaîne
$containsJohn = $collection->whereContains('name', 'John');
// whereStartsWith - Commence par
$startsJ = $collection->whereStartsWith('name', 'J');
// whereEndsWith - Se termine par
$endsDoe = $collection->whereEndsWith('name', 'Doe');
// whereLike - Alias de whereContains
$likeJohn = $collection->whereLike('name', 'John');
// whereLikePattern - Motif LIKE SQL
$pattern = $collection->whereLikePattern('name', '%john%'); // Contient
$pattern = $collection->whereLikePattern('name', 'john%'); // Commence par
$pattern = $collection->whereLikePattern('name', '%john'); // Se termine par
// whereArrayContains - Le tableau contient une valeur
$hasPhp = $collection->whereArrayContains('tags', 'php');
// whereArrayNotContains - Le tableau ne contient pas une valeur
$noPhp = $collection->whereArrayNotContains('tags', 'php');
// whereArrayContainsAny - Le tableau contient au moins une valeur
$hasPhpOrJs = $collection->whereArrayContainsAny('tags', ['php', 'js']);
// whereArrayContainsAll - Le tableau contient toutes les valeurs
$hasPhpAndJs = $collection->whereArrayContainsAll('tags', ['php', 'js']);
// whereArraySize - Taille exacte
$exactSize = $collection->whereArraySize('tags', 3);
// whereArraySizeGreaterThan - Taille supérieure
$moreThan2 = $collection->whereArraySizeGreaterThan('tags', 2);
// whereArraySizeLessThan - Taille inférieure
$lessThan2 = $collection->whereArraySizeLessThan('tags', 2);
// whereArrayEmpty - Tableau vide
$emptyTags = $collection->whereArrayEmpty('tags');
// whereArrayNotEmpty - Tableau non vide
$hasTags = $collection->whereArrayNotEmpty('tags');
// AND - via chaînage
$activeAdmins = $collection
->where('status', 'active')
->where('role', 'admin');
// OR - via orWhere
$adminOrDoctor = $collection
->where('role', 'admin')
->orWhere('role', 'doctor');
// OR sur condition simple
$activeOrPending = $collection
->where('status', 'active')
->orWhere('status', 'pending');
// Sous-condition simple
$sql = $engine->toSql('clusters', 'addresses[city=Kinshasa]', DatabaseDriver::SQLITE);
// EXISTS (SELECT 1 FROM json_each(clusters, '$.addresses') WHERE LOWER(json_extract(value, '$.city')) = LOWER('Kinshasa'))
// Sous-condition avec AND
$sql = $engine->toSql('clusters', 'addresses[city=Kinshasa & country=RDC]', DatabaseDriver::SQLITE);
// EXISTS (SELECT 1 FROM json_each(clusters, '$.addresses') WHERE LOWER(json_extract(value, '$.city')) = LOWER('Kinshasa') AND LOWER(json_extract(value, '$.country')) = LOWER('RDC'))
// Ces fonctions fonctionnent automatiquement en SQLite
$users = User::whereRaw('JSON_LENGTH(clusters, \'$.addresses\') > 2')->get();
// Ou via whereCluster
$users = User::whereCluster('clusters', 'COUNT(addresses) > 2')->get();
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use App\Models\User;
$engine = new ClusterQuery();
$query = User::query();
$engine->applyToEloquent($query, 'clusters', 'status=active', DatabaseDriver::MYSQL);
$users = $query->get();
// SELECT * FROM users WHERE JSON_EXTRACT(clusters, '$."status"') = 'active'
$query = User::query();
// Utilisateurs avec une adresse à Kinshasa
$engine->applyToEloquent($query, 'clusters', 'addresses[city=Kinshasa]', DatabaseDriver::MYSQL);
// Utilisateurs avec une adresse à Kinshasa ET actifs
$engine->applyToEloquent($query, 'clusters', 'status=active & addresses[city=Kinshasa]', DatabaseDriver::MYSQL);
$users = $query->get();
$query = User::query();
// Utilisateurs avec plus de 2 adresses
$engine->applyToEloquent($query, 'clusters', 'COUNT(addresses) > 2', DatabaseDriver::MYSQL);
// Utilisateurs avec une moyenne de scores >= 85
$engine->applyToEloquent($query, 'clusters', 'AVG(scores) >= 85', DatabaseDriver::MYSQL);
// Combinaison
$engine->applyToEloquent($query, 'clusters', 'status=active & COUNT(addresses) > 1', DatabaseDriver::MYSQL);
$users = $query->get();
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
$collection = new ClusterVOCollection();
$collection->add(new ClusterVO([
'name' => 'John',
'addresses' => [
['city' => 'Kinshasa', 'country' => 'RDC'],
['city' => 'Paris', 'country' => 'France'],
],
]));
$collection->add(new ClusterVO([
'name' => 'Jane',
'addresses' => [
['city' => 'Paris', 'country' => 'France'],
],
]));
$collection->add(new ClusterVO([
'name' => 'Bob',
'addresses' => [
['city' => 'Kinshasa', 'country' => 'RDC'],
['city' => 'London', 'country' => 'UK'],
['city' => 'Paris', 'country' => 'France'],
],
]));
// Condition simple
$result = $collection->whereQuery('addresses[city=Kinshasa]');
// John, Bob
// Condition avec AND
$result = $collection->whereQuery('addresses[city=Kinshasa & country=RDC]');
// John, Bob
// Condition avec OR
$result = $collection->whereQuery('addresses[city=Kinshasa | city=Paris]');
// John, Jane, Bob
// Condition avec LIKE
$result = $collection->whereQuery('addresses[city=~kin%]');
// John, Bob
// Condition avec NOT_LIKE
$result = $collection->whereQuery('addresses[city!~kin%]');
// Jane, Bob (Bob a Paris et Londres)
// EXISTS - Tableau non vide
$result = $collection->whereQuery('addresses[]');
// John, Jane, Bob
// NOT_EXISTS - Clé absente
$result = $collection->whereQuery('addresses[#city]');
// Personne (tous ont city)
use App\Models\User;
// Utilisateurs avec une adresse à Kinshasa
$users = User::whereCluster('clusters', 'addresses[city=Kinshasa]')->get();
// Utilisateurs avec une adresse à Kinshasa ET actifs
$users = User::whereCluster('clusters', 'status=active & addresses[city=Kinshasa]')->get();
// Utilisateurs avec une adresse à Kinshasa ou Paris
$users = User::whereCluster('clusters', 'addresses[city=Kinshasa | city=Paris]')->get();
// Utilisateurs avec au moins une adresse
$users = User::whereCluster('clusters', 'addresses[]')->get();
// Utilisateurs sans adresse
$users = User::whereCluster('clusters', 'addresses[#city]')->get();
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
$collection = new ClusterVOCollection();
$collection->add(new ClusterVO(['name' => 'John', 'email' => '[email protected]']));
$collection->add(new ClusterVO(['name' => 'Jane']));
$collection->add(new ClusterVO(['name' => 'Bob', 'email' => '[email protected]']));
// EXISTS
$result = $collection->whereQuery('*email');
// John, Bob
// EXISTS avec condition
$result = $collection->whereQuery('*email & name=John');
// John
// NOT_EXISTS
$result = $collection->whereQuery('#email');
// Jane
// NOT_EXISTS avec condition
$result = $collection->whereQuery('#email & name=Jane');
// Jane
use App\Models\User;
// Utilisateurs avec un email
$users = User::whereCluster('clusters', '*email')->get();
// Utilisateurs sans email
$users = User::whereCluster('clusters', '#email')->get();
// Utilisateurs avec un email et actifs
$users = User::whereCluster('clusters', '*email & status=active')->get();
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
$collection = new ClusterVOCollection();
$collection->add(new ClusterVO(['name' => 'John Doe']));
$collection->add(new ClusterVO(['name' => 'Jane Smith']));
$collection->add(new ClusterVO(['name' => 'Bob Johnson']));
// Contient "John"
$result = $collection->whereQuery('name=~John');
// John Doe, Bob Johnson (contient John)
// Commence par "J"
$result = $collection->whereQuery('name=~J%');
// John Doe, Jane Smith
// Se termine par "n"
$result = $collection->whereQuery('name=~%n');
// Bob Johnson
// Avec motif
$result = $collection->whereQuery('name=~%John%');
// John Doe, Bob Johnson
// Ne contient pas "John"
$result = $collection->whereQuery('name!~John');
// Jane Smith
// Ne commence pas par "J"
$result = $collection->whereQuery('name!~J%');
// Bob Johnson
// Ne se termine pas par "n"
$result = $collection->whereQuery('name!~%n');
// John Doe, Jane Smith
use App\Models\User;
// Noms commençant par "J"
$users = User::whereCluster('clusters', 'name=~J%')->get();
// Noms ne commençant pas par "J"
$users = User::whereCluster('clusters', 'name!~J%')->get();
// Noms contenant "John"
$users = User::whereCluster('clusters', 'name=~%John%')->get();
namespace App\Cluster\Functions;
use AndyDefer\LaravelCluster\Functions\AbstractAggregateFunction;
class DoubleCountFunction extends AbstractAggregateFunction
{
public function execute(array $data, array $args): int
{
$path = $args[0] ?? null;
$value = $this->resolveArg($data, $path);
if (is_array($value)) {
return count($value) * 2;
}
if (is_string($value)) {
return strlen($value) * 2;
}
return 0;
}
public function getName(): string
{
return 'DOUBLE_COUNT';
}
public function getDefaultValue(): mixed
{
return 0;
}
public function getReturnType(): string
{
return 'int';
}
public function returnsBoolean(): bool
{
return false;
}
public function getMinArgs(): int
{
return 1;
}
public function getMaxArgs(): int
{
return 1;
}
public function validateArgs(array $args): bool
{
return count($args) === 1;
}
}
use AndyDefer\LaravelCluster\Registry\AggregateFunctionRegistry;
use App\Cluster\Functions\DoubleCountFunction;
$registry = app(AggregateFunctionRegistry::class);
$registry->register(new DoubleCountFunction());
// Utilisation
$result = $collection->whereAggregate('{DOUBLE_COUNT(addresses) > 4}');
namespace App\Cluster\SqlFunctions;
use AndyDefer\LaravelCluster\SqlFunctions\AbstractSqlFunction;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
class CustomFunction extends AbstractSqlFunction
{
public function getName(): string
{
return 'CUSTOM';
}
public function toSql(string $column, string $path, DatabaseDriver $driver): string
{
return match ($driver) {
DatabaseDriver::SQLITE => sprintf(
"CUSTOM_FN(json_extract(%s, '$.%s'))",
$column,
$path
),
DatabaseDriver::MYSQL => sprintf(
"CUSTOM_FN(JSON_EXTRACT(%s, '$.%s'))",
$column,
$path
),
DatabaseDriver::PGSQL => sprintf(
"CUSTOM_FN(%s->>'%s')",
$column,
$path
),
};
}
public function getReturnType(): string
{
return 'int';
}
public function execute(mixed $value): mixed
{
// Logique en mémoire
return is_array($value) ? count($value) * 2 : 0;
}
}
use AndyDefer\LaravelCluster\Registry\SqlFunctionRegistry;
use App\Cluster\SqlFunctions\CustomFunction;
$registry = app(SqlFunctionRegistry::class);
$registry->register(new CustomFunction());
// Utilisation
$result = $collection->whereAggregate('{CUSTOM(addresses) > 4}');
// ou
$query = User::whereCluster('clusters', 'CUSTOM(addresses) > 4');
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Nodes\ConditionNode;
use AndyDefer\LaravelCluster\Nodes\GroupNode;
use AndyDefer\LaravelCluster\Nodes\FunctionNode;
use AndyDefer\LaravelCluster\Nodes\SubConditionNode;
$engine = new ClusterQuery();
// ConditionNode - Condition simple
$ast = $engine->parse('status=active');
var_dump($ast instanceof ConditionNode); // true
// GroupNode - Groupe logique (AND/OR)
$ast = $engine->parse('status=active & role=admin');
var_dump($ast instanceof GroupNode); // true
// FunctionNode - Fonction SQL
$ast = $engine->parse('COUNT(addresses) > 2');
var_dump($ast instanceof FunctionNode); // true
// SubConditionNode - Sous-condition
$ast = $engine->parse('addresses[city=Kinshasa]');
var_dump($ast instanceof SubConditionNode); // true
$ast1 = $engine->parse('status=active');
$ast2 = $engine->parse('status=active');
// $ast1 et $ast2 sont la même instance
$ast3 = $engine->parse('status=inactive');
// $ast3 est une nouvelle instance
// Priorité des opérateurs
$query = '(status=active | status=pending) & role=admin';
// (status=active OU status=pending) ET role=admin
// where - Égalité simple
$collection->where(string $key, mixed $value): self
// whereNot - Différent
$collection->whereNot(string $key, mixed $value): self
// whereYes - Égal à 'yes'
$collection->whereYes(string $key): self
// whereNo - Égal à 'no'
$collection->whereNo(string $key): self
// orWhere - OR logique
$collection->orWhere(string $key, mixed $value): self
// whereIn - Dans une liste
$collection->whereIn(string $key, array $values): self
// whereNotIn - Hors liste
$collection->whereNotIn(string $key, array $values): self
// whereHas - La clé existe
$collection->whereHas(string $key): self
// whereMissing - La clé n'existe pas
$collection->whereMissing(string $key): self
// whereNull - La valeur est null
$collection->whereNull(string $key): self
// whereNotNull - La valeur n'est pas null
$collection->whereNotNull(string $key): self
// get - Tous les éléments
$collection->get(): array
// firstWhere - Premier élément correspondant
$collection->firstWhere(string $key, mixed $value): ?ClusterVO
namespace App\Services;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use App\Models\Customer;
class CustomerFilterService
{
public function __construct(
private readonly ClusterService $clusterService
) {}
public function findCustomers(array $criteria): array
{
$conditions = [];
if (isset($criteria['min_revenue'])) {
$conditions[] = "revenue >= " . $criteria['min_revenue'];
}
if (isset($criteria['industry'])) {
$conditions[] = "industry=" . $criteria['industry'];
}
if (isset($criteria['country'])) {
$conditions[] = "country=" . $criteria['country'];
}
if (isset($criteria['is_active'])) {
$conditions[] = "active=" . ($criteria['is_active'] ? 'yes' : 'no');
}
if (isset($criteria['has_contract'])) {
$conditions[] = "*contract_signed";
}
$queryString = implode(' & ', $conditions);
$query = Customer::query();
if (!empty($queryString)) {
$this->clusterService->applyToEloquent(
$query,
'company_data',
$queryString,
DatabaseDriver::MYSQL
);
}
return $query->get()->toArray();
}
}
namespace App\Services;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use App\Models\Product;
class ProductSearchService
{
public function __construct(
private readonly ClusterService $clusterService
) {}
public function searchProducts(array $filters): array
{
$query = Product::query();
$conditions = [];
// Catégories - avec OR
if (!empty($filters['categories'])) {
$categoryConditions = [];
foreach ($filters['categories'] as $category) {
$categoryConditions[] = "categories_{$category}=yes";
}
$conditions[] = '(' . implode(' OR ', $categoryConditions) . ')';
}
// Tags - avec AND
if (!empty($filters['tags'])) {
$tagConditions = [];
foreach ($filters['tags'] as $tag) {
$tagConditions[] = "tags_{$tag}=yes";
}
$conditions[] = '(' . implode(' AND ', $tagConditions) . ')';
}
// Prix
if (isset($filters['min_price'])) {
$conditions[] = "price >= " . $filters['min_price'];
}
if (isset($filters['max_price'])) {
$conditions[] = "price <= " . $filters['max_price'];
}
// Disponibilité
if (isset($filters['in_stock'])) {
$conditions[] = "in_stock=" . ($filters['in_stock'] ? 'yes' : 'no');
}
// Promotion
if (isset($filters['on_promotion'])) {
$conditions[] = "promotion=" . ($filters['on_promotion'] ? 'yes' : 'no');
}
if (!empty($conditions)) {
$queryString = implode(' & ', $conditions);
$this->clusterService->applyToEloquent(
$query,
'product_attributes',
$queryString,
DatabaseDriver::MYSQL
);
}
return $query->get()->toArray();
}
}
namespace App\Services;
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
use AndyDefer\LaravelCluster\Services\ClusterService;
class DeveloperFilterService
{
public function __construct(
private readonly ClusterService $clusterService
) {}
public function findDevelopers(array $candidates, array $criteria): array
{
$collection = new ClusterVOCollection();
foreach ($candidates as $candidate) {
$collection->add(new ClusterVO($candidate));
}
// Filtrer par compétences
if (!empty($criteria['",
$criteria['optional_skills']
)) . ')'
);
}
// Années d'expérience
if (isset($criteria['min_experience'])) {
$collection = $collection->whereGreaterThanOrEqual('experience', $criteria['min_experience']);
}
// Localisation
if (isset($criteria['city'])) {
$collection = $collection->where('city', $criteria['city']);
}
// Disponibilité
if (isset($criteria['available'])) {
$collection = $collection->where('available', $criteria['available'] ? 'yes' : 'no');
}
return $collection->get();
}
}
namespace App\Http\Controllers\Api;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
use App\Models\Resource;
class ResourceController extends Controller
{
public function __construct(
private readonly ClusterService $clusterService
) {}
public function index(Request $request)
{
$query = Resource::query();
$filter = $request->get('filter');
$search = $request->get('search');
$sort = $request->get('sort', 'created_at');
$order = $request->get('order', 'desc');
// Filtrage avancé
if ($filter) {
$this->clusterService->applyToEloquent(
$query,
'metadata',
$filter,
DatabaseDriver::MYSQL
);
}
// Recherche textuelle
if ($search) {
$query->where('name', 'LIKE', "%{$search}%");
}
$query->orderBy($sort, $order);
return $query->paginate(20);
}
}
namespace App\Services;
use AndyDefer\LaravelCluster\Services\ClusterService;
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
use AndyDefer\LaravelCluster\ValueObjects\ClusterVO;
class DataExportService
{
public function __construct(
private readonly ClusterService $clusterService
) {}
public function exportFilteredData(array $sourceData, string $filter, array $columns = []): array
{
// Conversion en ClusterVOCollection
$collection = new ClusterVOCollection();
foreach ($sourceData as $item) {
$collection->add(new ClusterVO($item));
}
// Filtrage
$filtered = $this->clusterService->filter($collection, $filter);
// Extraction des colonnes spécifiques
if (empty($columns)) {
return $filtered->toArray();
}
$result = [];
foreach ($filtered as $cluster) {
$row = [];
foreach ($columns as $column) {
$row[$column] = $cluster->get($column, null);
}
$result[] = $row;
}
return $result;
}
}
use AndyDefer\LaravelCluster\ClusterQuery;
$engine = new ClusterQuery();
try {
$ast = $engine->parse('status=active & role=admin');
// Requête valide
} catch (\RuntimeException $e) {
echo "Erreur de syntaxe: " . $e->getMessage();
}
use AndyDefer\LaravelCluster\Collections\ClusterVOCollection;
$collection = new ClusterVOCollection();
$valid = $collection->validateAggregate('{COUNT(addresses) > 2}');
// true
$valid = $collection->validateAggregate('{INVALID(addresses) > 2}');
// false
if (!$valid) {
// L'expression est invalide
}
use AndyDefer\LaravelCluster\ClusterQuery;
use AndyDefer\LaravelCluster\Enums\DatabaseDriver;
$engine = new ClusterQuery();
// Afficher le SQL généré pour débogage
$sql = $engine->toSql('clusters', 'status=active & role=admin', DatabaseDriver::MYSQL);
dd($sql); // Voir le SQL exact
// Avec une sous-condition
$sql = $engine->toSql('clusters', 'addresses[city=Kinshasa]', DatabaseDriver::SQLITE);
dd($sql);
// Avec une fonction SQL
$sql = $engine->toSql('clusters', 'COUNT(addresses) > 2', DatabaseDriver::SQLITE);
dd($sql);
// ❌ À éviter - Filtrer plusieurs fois
$filtered = $collection->where('status', 'active');
$filtered = $filtered->where('role', 'admin');
$filtered = $filtered->where('age', '>', '25');
// ✅ Recommandé - Chaînage direct
$filtered = $collection
->where('status', 'active')
->where('role', 'admin')
->whereGreaterThan('age', 25);
// ✅ Recommandé - Une seule requête
$filtered = $collection->whereQuery('status=active & role=admin & age>25');
// ❌ À éviter - Utiliser les fonctions SQL sur des colonnes non indexées
$users = User::whereCluster('clusters', 'COUNT(addresses) > 2')->get();
// ✅ Recommandé - Indexer les colonnes JSON utilisées
// Dans la migration :
DB::statement('ALTER TABLE users ADD INDEX idx_clusters_status ((JSON_EXTRACT(clusters, "$.status")))');
// ✅ Recommandé - Utiliser des conditions simples sur les colonnes indexées
$users = User::whereCluster('clusters', 'status=active')->get();
// ✅ Recommandé - Limiter les résultats avant de filtrer
$users = User::take(100)
->whereCluster('clusters', 'status=active')
->get();
// ✅ Recommandé - Utiliser select pour ne récupérer que les colonnes nécessaires
$users = User::select('id', 'name', 'clusters')
->whereCluster('clusters', 'status=active')
->get();
// ✅ Recommandé - Utiliser pagination
$users = User::whereCluster('clusters', 'status=active')
->paginate(20);
// 1. Utiliser les alias pour améliorer la lisibilité
$service = app(ClusterService::class);
$service->applyToEloquent($query, 'metadata', 'status=active', DatabaseDriver::MYSQL);
// 2. Valider les expressions avant de les utiliser
$collection = new ClusterVOCollection();
if ($collection->validateAggregate($expression)) {
$result = $collection->whereAggregate($expression);
}
// 3. Utiliser les macros Laravel pour plus de clarté
$users = User::whereCluster('clusters', 'status=active')->get();
// 4. Préférer whereQuery pour les requêtes complexes
$result = $collection->whereQuery('status=active & COUNT(addresses) > 2');
// 5. Utiliser les fonctions d'agrégation pour les calculs complexes
$result = $collection->whereAggregate('{AVG(scores) >= 85}');
// ❌ À éviter - Travailler sur de très grandes collections en mémoire
$allUsers = User::all();
$filtered = $allUsers->whereCluster('clusters', 'status=active');
// ✅ Recommandé - Utiliser le filtrage en base de données
$filtered = User::whereCluster('clusters', 'status=active')->get();
// ✅ Recommandé - Utiliser le streaming si nécessaire
User::whereCluster('clusters', 'status=active')->chunk(100, function ($users) {
foreach ($users as $user) {
// Traitement par lots
}
});