MartinGeorgiev\Doctrine\DBAL\Types\Geography (see note)
geography[]
_geography
MartinGeorgiev\Doctrine\DBAL\Types\GeographyArray
geometry
geometry
MartinGeorgiev\Doctrine\DBAL\Types\Geometry (see note)
geometry[]
_geometry
MartinGeorgiev\Doctrine\DBAL\Types\GeometryArray
cube
cube
MartinGeorgiev\Doctrine\DBAL\Types\Cube (see note)
cube[]
_cube
MartinGeorgiev\Doctrine\DBAL\Types\CubeArray
hstore
hstore
MartinGeorgiev\Doctrine\DBAL\Types\Hstore (see note)
hstore[]
_hstore
MartinGeorgiev\Doctrine\DBAL\Types\HstoreArray
lquery
lquery
MartinGeorgiev\Doctrine\DBAL\Types\Lquery
lquery[]
_lquery
MartinGeorgiev\Doctrine\DBAL\Types\LqueryArray
ltree
ltree
MartinGeorgiev\Doctrine\DBAL\Types\Ltree
ltree[]
_ltree
MartinGeorgiev\Doctrine\DBAL\Types\LtreeArray
ltxtquery
ltxtquery
MartinGeorgiev\Doctrine\DBAL\Types\Ltxtquery
ltxtquery[]
_ltxtquery
MartinGeorgiev\Doctrine\DBAL\Types\LtxtqueryArray
money
money
MartinGeorgiev\Doctrine\DBAL\Types\Money (see note)
money[]
_money
MartinGeorgiev\Doctrine\DBAL\Types\MoneyArray
xml
xml
MartinGeorgiev\Doctrine\DBAL\Types\Xml
xml[]
_xml
MartinGeorgiev\Doctrine\DBAL\Types\XmlArray
halfvec
halfvec
MartinGeorgiev\Doctrine\DBAL\Types\Halfvec (see note)
sparsevec
sparsevec
MartinGeorgiev\Doctrine\DBAL\Types\Sparsevec (see note)
vector
vector
MartinGeorgiev\Doctrine\DBAL\Types\Vector (see note)
(user-defined enum)
(any)
MartinGeorgiev\Doctrine\DBAL\Types\Enum (see Enum Types)
(user-defined enum)[]
(any)
MartinGeorgiev\Doctrine\DBAL\Types\EnumArray (see Enum Types)
(user-defined composite)
(any)
MartinGeorgiev\Doctrine\DBAL\Types\Composite (see Composite Types)
(user-defined composite)[]
(any)
MartinGeorgiev\Doctrine\DBAL\Types\CompositeArray (see Composite Types)
PostGIS Spatial Types
The geometry and geography types accept the geometry_type and srid column options, which emit a PostGIS type modifier so the subtype and spatial reference system are enforced by the database:
useDoctrine\ORM\MappingasORM;useMartinGeorgiev\Doctrine\DBAL\Types\ValueObject\WktSpatialData;#[ORM\Entity]classPlace{// GEOGRAPHY(POINT,4326) — only WGS 84 points are accepted#[ORM\Column(type: 'geography', options: ['geometry_type' => 'Point', 'srid' => 4326])]privateWktSpatialData$location;// GEOMETRY — unconstrained, accepts any geometry#[ORM\Column(type: 'geometry')]privateWktSpatialData$shape;}
Omitting both options keeps the bare GEOMETRY / GEOGRAPHY declaration.
📖 See also: Spatial Types for the full option reference
pgvector Types
The vector, halfvec, and sparsevec types use the length column option to specify the number of dimensions:
useDoctrine\ORM\MappingasORM;useMartinGeorgiev\Doctrine\DBAL\Types\ValueObject\Sparsevec;#[ORM\Entity]classEmbedding{// VECTOR(1536) — fixed 1536-dimensional float vector#[ORM\Column(type: 'vector', length: 1536)]privatearray$embedding;// HALFVEC(1024) — half-precision float vector#[ORM\Column(type: 'halfvec', length: 1024)]privatearray$smallEmbedding;// SPARSEVEC(4096) — sparse vector with up to 4096 dimensions#[ORM\Column(type: 'sparsevec', length: 4096)]privateSparsevec$sparseEmbedding;}
Important: Omitting length produces a dimensionless column (VECTOR with no size), which is valid DDL but cannot be indexed with HNSW or IVFFlat indexes. Always specify length for production use.
Bit String Types
The bit and bit varying types support an optional length parameter via column attribute:
useDoctrine\ORM\MappingasORM;#[ORM\Entity]classPermissions{// BIT(1) — fixed single bit (default when no length specified)#[ORM\Column(type: 'bit')]privatestring$active;// BIT(8) — fixed 8-bit flags#[ORM\Column(type: 'bit', length: 8)]privatestring$flags;// BIT VARYING — unlimited length (default when no length specified)#[ORM\Column(type: 'bit varying')]privatestring$mask;// BIT VARYING(64) — variable length, up to 64 bits#[ORM\Column(type: 'bit varying', length: 64)]privatestring$features;}
Important:BIT without a length defaults to BIT(1) in PostgreSQL, which stores exactly one bit. Use BIT VARYING for variable-length bit strings, or specify an explicit length with BIT(n).
Numeric Array Type
The numeric[] type maps array items to PHP strings (e.g. '502.00') rather than floats. PostgreSQL’s numeric is an arbitrary-precision type, and converting its values to PHP floats would silently lose precision and trailing zeros — the same reason Doctrine’s own decimal type uses strings.
Array items written to the database must be numeric strings (or null); PHP integers and floats are rejected
decimal[] is a PostgreSQL alias of numeric[] — columns declared as DECIMAL[] are reported by PostgreSQL as numeric[], so this type covers both
numeric also carries the non-finite values 'NaN', 'Infinity' and '-Infinity', which are items like any other here:
Each is accepted in the single spelling PostgreSQL prints. It reads 'nan' and 'inf' too, but emits NaN and Infinity, so allowing the other spellings would break the string round-trip the same way scientific notation would. Infinity needs PostgreSQL 14 or newer; earlier servers store NaN only.
Datetime Array Types
Items of date[], timestamp[] and timestamptz[] map to \DateTimeImmutable, with one exception: PostgreSQL’s infinity and -infinity sort before and after every other value of their type and have no \DateTimeImmutable counterpart, so they map to the MartinGeorgiev\Doctrine\DBAL\Types\ValueObject\DateTimeInfinity enum instead. Reading a column that may hold them means widening the item check:
useMartinGeorgiev\Doctrine\DBAL\Types\ValueObject\DateTimeInfinity;foreach($entity->getValidOn()as$item){match(true){$item===null=>$this->skip(),// SQL NULL - the value is unknown$item===DateTimeInfinity::POSITIVE=>$this->open(),// later than every other date$item===DateTimeInfinity::NEGATIVE=>$this->open(),// earlier than every other datedefault=>$this->useDate($item),};}
Both are written back by putting the same enum case into the array. They are deliberately kept apart from null, which stays the SQL NULL: an unbounded date is not an unknown one.
Two further ranges PostgreSQL accepts are mapped without a sentinel, because \DateTimeImmutable can hold them:
Years before 1 AD. PostgreSQL numbers them in the BC era and has no year zero, while PHP numbers them astronomically and does. 0001-01-15 BC therefore reads back as PHP year 0000, and 0002-01-15 BC as PHP year -0001. Format such values with X rather than Y to keep the sign visible.
Years past 9999. They round-trip unchanged; Y prints them in full.
📖 See also: Infinity Values for why an array item uses this enum while a float uses INF and a range bound uses a flag.
UUID Array Type
The uuid[] type validates UUID format and returns string[] rather than UUID value objects. This design decision keeps the library lightweight and framework-agnostic:
No additional dependencies - Works without requiring ramsey/uuid or symfony/uid
Consistent with other array types - Follows the same pattern as TextArray, IntegerArray, etc.
Framework agnostic - Compatible with any UUID library of your choice
If you need UUID objects, you can easily convert the strings:
// With ramsey/uuiduseRamsey\Uuid\Uuid;$uuids=array_map(fn(string$uuid)=>Uuid::fromString($uuid),$entity->getUuidArray());// With symfony/uiduseSymfony\Component\Uid\Uuid;$uuids=array_map(fn(string$uuid)=>Uuid::fromString($uuid),$entity->getUuidArray());
Money Type
The money type maps PostgreSQL’s money data type and returns locale-formatted strings (e.g. $1,234.56). PostgreSQL formats money values according to the server’s lc_monetary locale setting, so the exact output format depends on your database configuration.
Important considerations:
PostgreSQL’s money type does not store currency information — the currency symbol is purely a formatting artifact of the server locale
If you need multi-currency support, consider using numeric with application-level currency handling instead
Values written to the database must contain at least one digit; full format validation is deferred to PostgreSQL
If you need rich money objects for arithmetic or multi-currency support, you can convert the string after retrieval:
// With moneyphp/money (requires parsing the locale-formatted string)useMoney\Money;useMoney\Currency;$amount=(int)round((float)preg_replace('/[^0-9.\-]/','',$entity->getPrice())*100);$money=newMoney($amount,newCurrency('USD'));// With brick/moneyuseBrick\Money\Money;$money=Money::of(preg_replace('/[^0-9.\-]/','',$entity->getPrice()),'USD');
Note that both examples above assume USD — you must know the currency independently since PostgreSQL does not store it.
Hstore Type
The hstore type requires the PostgreSQL hstore extension. Enable it with:
CREATEEXTENSIONIFNOTEXISTShstore;
It maps to array<string, string|null> in PHP. Keys and values are always strings; a NULL value in hstore is represented as null in PHP.
Citext Type
The citext type requires the PostgreSQL citext extension. Enable it with:
CREATEEXTENSIONIFNOTEXISTScitext;
It is a case-insensitive text type: comparisons are case-insensitive in PostgreSQL while the original casing of values is preserved. It maps to string in PHP and behaves identically to text for storage and retrieval — the difference is purely in how PostgreSQL evaluates equality and ordering.
Use citext when you want case-insensitive lookups (e.g. usernames, email addresses) without lowercasing values on write.
ULID Type
The ulid type requires the third-party pgx_ulid PostgreSQL extension. Enable it with:
CREATEEXTENSIONIFNOTEXISTSulid;
A ULID is a 26-character Crockford base32 identifier (uppercase, first character 0–7) stored as a compact 128-bit binary value. It maps to string in PHP. PostgreSQL outputs the canonical uppercase form on retrieval; the DBAL type normalizes values to uppercase on write as well, so round-trips are stable even for lowercase input.
Use ulid when you want sortable, timestamp-prefixed identifiers that are shorter and more index-friendly than UUIDs.
Cube Type
The cube type requires the PostgreSQL cube extension. Enable it with:
CREATEEXTENSIONIFNOTEXISTScube;
A cube is a multidimensional value that is either a point — (1, 2, 3) — or a box spanned by two opposite corners — (1, 2, 3),(4, 5, 6). Both corners always carry the same number of dimensions. It maps to the MartinGeorgiev\Doctrine\DBAL\Types\ValueObject\Cube value object in PHP: