camelot/doctrine-postgres-bundle
Installation:
composer require camelot/doctrine-postgres-bundle
Add to config/bundles.php:
Camelot\DoctrinePostgres\CamelotDoctrinePostgresBundle::class => ['all' => true],
First Use Case:
Define a Doctrine entity with PostgreSQL-specific fields (e.g., jsonb or array types):
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Product
{
#[ORM\Column(type: Types::JSON, nullable: true)]
private ?array $metadata = [];
#[ORM\Column(type: 'array', nullable: true)]
private ?array $tags = [];
}
Run migrations to create the table with PostgreSQL-compatible columns.
Querying JSONB/Arrays: Use DQL to leverage PostgreSQL functions:
$products = $entityManager->createQueryBuilder()
->select('p')
->from(Product::class, 'p')
->where('JSON_CONTAINS(p.metadata, :search)')
->setParameter('search', '{"key": "value"}')
->getQuery()
->getResult();
JSONB Operations:
jsonb type for nested data. Example:
$product->setMetadata(['price' => 100, 'features' => ['fast', 'light']]);
// Check if JSON contains a key
$qb->andWhere('JSON_CONTAINS(p.metadata, :key)')
->setParameter('key', '"features"');
// Extract JSON value
$qb->andWhere('p.metadata->>\'price\' > :minPrice')
->setParameter('minPrice', '50');
Array Operations:
$qb->select('UNNEST(p.tags) as tag')
->from(Product::class, 'p');
$qb->andWhere('p.tags @> :searchTags')
->setParameter('searchTags', ['fast', 'light']);
Text Search:
tsvector/tsquery:
$qb->andWhere('p.description @@ PLAINTO_TSQUERY(:search)')
->setParameter('search', 'fast & light');
DataTransformer or custom types.json_encode works seamlessly).jsonb or array types:
# src/Migrations/VersionYYYYMMDDHHMMSS.php
public function up(SchemaManager $sm, Adapter $connection)
{
$this->addSql('ALTER TABLE product ALTER metadata TYPE jsonb USING metadata::jsonb');
}
Type Mismatches:
json (Doctrine’s generic JSON) and jsonb (PostgreSQL’s binary JSON). Stick to jsonb for performance.Types::JSONB (if available) or cast in migrations:
ALTER TABLE table_name ALTER column_name TYPE jsonb USING column_name::jsonb;
Array Indexing:
p.tags[1] return the second element.ARRAY_REMOVE or UNNEST carefully to avoid off-by-one errors.Case Sensitivity:
$qb->andWhere('p.metadata->>"Key" = :value'); // Exact match
Doctrine Event Conflicts:
prePersist) may interfere with JSONB/array serialization.Doctrine\DBAL\Exception\InvalidFieldNameException for malformed JSON paths.Query Logging: Enable SQL logging in config/packages/doctrine.yaml:
doctrine:
dbal:
logging: true
profiling: true
Inspect generated SQL for incorrect function calls (e.g., JSON_CONTAINS vs. JSON_CONTAINS_PATH).
Schema Validation:
php bin/console doctrine:schema:validate
Ensure PostgreSQL-specific types are recognized.
Custom DQL Functions: Extend the bundle by adding custom PostgreSQL functions to Doctrine’s DQL parser:
// src/EventListener/AddCustomDqlFunctions.php
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Parser;
class AddCustomDqlFunctions implements Query\AST\QueryASTWalker
{
public function walkFunctionNode(FunctionNode $node)
{
if ($node->getFunctionName() === 'CUSTOM_POSTGRES_FUNC') {
// Handle custom logic
}
return $node;
}
}
Type Mapping:
Override default type mappings in config/packages/doctrine.yaml:
doctrine:
dbal:
types:
jsonb: Camelot\DoctrinePostgres\DBAL\Types\JsonbType
Performance:
CREATE INDEX idx_product_metadata ON product USING GIN (metadata jsonb_path_ops);
jsonb_path_ops or jsonb_path_query_ops for efficient path queries.How can I help you explore Laravel packages today?