aldaflux/pgsql-doctrine-random-function
composer require aldaflux/pgsql-doctrine-random-function
config/packages/doctrine.yaml (Symfony 5+) or config/config.yml (older versions):
doctrine:
orm:
dql:
numeric_functions:
Random: Qbbr\PgsqlDoctrineRandomFunction\DQL\RandomFunction
$randomUser = $entityManager->createQueryBuilder()
->select('u')
->from('App\Entity\User', 'u')
->orderBy('RANDOM()')
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
Random Sorting:
Use RANDOM() in ORDER BY clauses to shuffle results:
$query = $entityManager->createQueryBuilder()
->select('p')
->from('App\Entity\Product', 'p')
->orderBy('RANDOM()')
->getQuery();
Random Sampling:
Combine with setMaxResults() for pagination-like randomness:
$randomProducts = $entityManager->createQueryBuilder()
->select('p')
->from('App\Entity\Product', 'p')
->orderBy('RANDOM()')
->setMaxResults(5)
->getQuery()
->getResult();
Random Seeding:
For reproducible randomness, use setRandomSeed() (PostgreSQL-specific):
$query = $entityManager->createQueryBuilder()
->select('u')
->from('App\Entity\User', 'u')
->orderBy('RANDOM(42)') // Seed = 42
->getQuery();
Dynamic Randomness in Repositories: Create a repository method for reusable random queries:
// src/Repository/UserRepository.php
public function findRandomUsers(int $limit = 10): array
{
return $this->createQueryBuilder('u')
->orderBy('RANDOM()')
->setMaxResults($limit)
->getQuery()
->getResult();
}
Random Joins: Apply randomness to joined tables:
$query = $entityManager->createQueryBuilder()
->select('u, p')
->from('App\Entity\User', 'u')
->leftJoin('u.products', 'p')
->orderBy('RANDOM()')
->getQuery();
$query = $entityManager->createNativeQuery(
'SELECT * FROM users ORDER BY RANDOM() LIMIT 10'
);
$cacheKey = 'random_products_' . md5(filemtime(__FILE__));
$randomProducts = $entityManager->getCache()->fetch($cacheKey, function() use ($entityManager) {
return $entityManager->createQueryBuilder()
->select('p')
->from('App\Entity\Product', 'p')
->orderBy('RANDOM()')
->setMaxResults(10)
->getQuery()
->getResult();
});
// src/Controller/RandomController.php
public function randomFeature(EntityManagerInterface $em): JsonResponse
{
$feature = $em->getRepository(Feature::class)
->findRandomFeatures(1)
->first();
return new JsonResponse(['feature' => $feature]);
}
Performance:
ORDER BY RANDOM() can be slow on large tables. Use indexes on filtered columns first:
// Bad: Full table scan
$query->orderBy('RANDOM()');
// Better: Filter first, then randomize
$query->where('u.active = :active')
->setParameter('active', true)
->orderBy('RANDOM()');
Doctrine Version:
ClassNotFoundException.PostgreSQL-Specific:
RANDOM() function is PostgreSQL-only. This package will not work with MySQL, SQLite, or other databases. Ensure your DATABASE_URL points to PostgreSQL.QueryBuilder Limitation:
RANDOM() function cannot be used in HAVING or subqueries directly. Workaround: Use native SQL or fetch results first:
// Workaround for complex cases
$results = $query->getResult();
$filtered = array_filter($results, fn($item) => $item->getPrice() > 100);
Seeding Behavior:
RANDOM() returns a different value on every query. For reproducibility, always use RANDOM(seed):
// Unpredictable
$query->orderBy('RANDOM()');
// Reproducible
$query->orderBy('RANDOM(12345)');
Check Configuration:
numeric_functions key in doctrine.yaml is correctly set. A misconfiguration will silently fail (no error, but RANDOM() won’t work).Enable SQL Logging:
RANDOM() function is being generated:
$query->getQueryPart('orderBy'); // Should show "RANDOM()"
config/packages/dev/doctrine.yaml:
doctrine:
dbal:
logging: true
profiling: true
Fallback for Non-PostgreSQL:
try {
$query->orderBy('RANDOM()');
} catch (\Doctrine\ORM\Query\QueryException $e) {
// Fallback logic (e.g., use a different random method)
}
Custom Random Functions:
// src/DQL/WeightedRandomFunction.php
namespace App\DQL;
use Qbbr\PgsqlDoctrineRandomFunction\DQL\RandomFunction as BaseRandomFunction;
class WeightedRandomFunction extends BaseRandomFunction
{
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$weights = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$this->weightExpression = $weights;
return $this;
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'RANDOM() * (' . $this->weightExpression . ')';
}
}
doctrine.yaml:
doctrine:
orm:
dql:
numeric_functions:
WeightedRandom: App\DQL\WeightedRandomFunction
Database-Specific Logic:
TABLESAMPLE:
public function getSql(SqlWalker $sqlWalker)
{
return 'TABLESAMPLE SYSTEM(' . $sqlWalker->walkArithmeticPrimary($this->expr) . ')';
}
Integration with QueryBuilder Methods:
QueryBuilder method for reusable random queries:
// src/Doctrine/QueryBuilderExtensions.php
namespace App\Doctrine;
use Doctrine\ORM\QueryBuilder;
class QueryBuilderExtensions
{
public function random(QueryBuilder $qb, string $alias, int $limit = 10)
{
return $qb->orderBy("RANDOM()")
->setMaxResults($limit);
}
}
// src/Repository/BaseRepository.php
trait RandomQueryTrait
{
public function findRandom(int $limit = 10)
{
return $this->createQueryBuilder('e')
->
How can I help you explore Laravel packages today?