denisok94/doctrine-dql-operator
Install the package:
composer require denisok94/doctrine-dql-operator
Configure Doctrine in config/packages/doctrine.yaml:
doctrine:
dbal:
types:
timestampt: Denisok94\DoctrineDqlOperator\DBAL\Timestampt
timestamptz: Denisok94\DoctrineDqlOperator\DBAL\Timestamptz
money: Denisok94\DoctrineDqlOperator\DBAL\MoneyType
orm:
dql:
datetime_functions:
DATE: Denisok94\DoctrineDqlOperator\DQL\Date
DATE_TRUNC: Denisok94\DoctrineDqlOperator\DQL\DateTrunc
EXTRACT: Denisok94\DoctrineDqlOperator\DQL\Extract
DATE_PART: Denisok94\DoctrineDqlOperator\DQL\DatePart
numeric_functions:
COALESCE: Denisok94\DoctrineDqlOperator\DQL\CoalesceFunction
string_functions:
CAST: Denisok94\DoctrineDqlOperator\DQL\Cast
TO_CHAR: Denisok94\DoctrineDqlOperator\DQL\ToChar
jsonb_functions:
JSONB_AGG: Denisok94\DoctrineDqlOperator\DQL\JsonbAgg
JSONB_HSTORE: Denisok94\DoctrineDqlOperator\DQL\JsonbHstore
JSONB_EX: Denisok94\DoctrineDqlOperator\DQL\JsonbEx
JSONB_IN: Denisok94\DoctrineDqlOperator\DQL\JsonbIn
First Use Case:
Query a timestamp field with DATE_TRUNC:
$qb = $entityManager->createQueryBuilder();
$qb->select('u')
->from(User::class, 'u')
->where('DATE_TRUNC("u.createdAt", \'day\') = :date')
->setParameter('date', new \DateTime('2024-01-01'));
Date/Time Manipulation:
Use DATE_TRUNC for grouping by time periods (day/month/year):
$qb->select('DATE_TRUNC("o.orderDate", \'month\') as month, COUNT(o.id)')
->from(Order::class, 'o')
->groupBy('month');
JSONB Aggregations:
Aggregate JSON data with JSONB_AGG:
$qb->select('JSONB_AGG(JSONB_BUILD_OBJECT(\'id\', p.id, \'name\', p.name)) as products')
->from(Product::class, 'p')
->where('p.category = :category');
Type-Specific Queries:
Filter timestampt/timestamptz fields:
$qb->andWhere('u.lastLogin > :cutoff')
->setParameter('cutoff', new \DateTimeImmutable('2024-01-01'));
Coalesce for Null Handling:
Replace NULL values in queries:
$qb->select('COALESCE(u.discount, 0) as discount')
->from(User::class, 'u');
doctrine.orm.dql.*_functions in config.timestampt/timestamptz for PostgreSQL-specific timestamp types in @ORM\Column:
/**
* @ORM\Column(type="timestamptz")
*/
private $createdAt;
public function getMonthlySales(QueryBuilder $qb, string $entityAlias) {
return $qb->addSelect('DATE_TRUNC("' . $entityAlias . '.saleDate", \'month\') as month')
->addSelect('SUM("' . $entityAlias . '.amount) as total');
}
PostgreSQL-Specific:
timestampt/timestamptz and JSONB functions only work with PostgreSQL. Ensure your DBAL platform is configured correctly:
doctrine:
dbal:
platform_service_name: postgresql
Case Sensitivity:
DATE_TRUNC, not date_trunc).Parameter Binding:
// ❌ Avoid
$qb->where('DATE_TRUNC(:field, \'day\') = :date');
// ✅ Correct
$qb->where('DATE_TRUNC("user.createdAt", \'day\') = :date');
Money Type:
money type requires PostgreSQL’s money type. Migrate existing columns if needed:
ALTER TABLE orders ALTER COLUMN amount TYPE money USING amount::money;
Enable SQL Logging:
$entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Verify generated SQL matches expectations (e.g., DATE_TRUNC syntax).
Check Platform Compatibility:
If queries fail, confirm your DBAL\Platform is PostgreSQLPlatform:
$platform = $entityManager->getConnection()->getDatabasePlatform();
Add Custom Functions:
Extend the package by creating a new DQL function class (e.g., CustomFunction) and register it in doctrine.orm.dql.numeric_functions:
doctrine:
orm:
dql:
numeric_functions:
CUSTOM_FUNC: App\DQL\CustomFunction
Override Existing Functions:
Replace default implementations by redefining the config key (e.g., for COALESCE):
doctrine:
orm:
dql:
numeric_functions:
COALESCE: App\DQL\CustomCoalesce
Type Mapping:
Create custom DBAL types by extending Denisok94\DoctrineDqlOperator\DBAL\AbstractType for unsupported PostgreSQL types.
DATE_TRUNC/EXTRACT are indexed for large datasets:
CREATE INDEX idx_orders_truncated_date ON orders (DATE_TRUNC(created_at, 'day'));
JSONB_AGG for aggregating large JSON datasets in a single query instead of client-side processing.How can I help you explore Laravel packages today?