03/24/2015
Doctrine field type mappings: tinyint is a boolean
A MySQL tinyint maps to Doctrine's boolean regardless of the
declared length. TINYINT(1), TINYINT(4), a column holding 0 to 100 --
all of them come back from the database as true or false.
That is DBAL's own platform mapping, not a bug, and it is still the
behavior in DBAL 4. It surprises people because TINYINT(4) looks like a
number and behaves like a flag.
So how do you store a small integer?
Two ways, and the first is almost always the right one.
Use smallint and let the column be SMALLINT:
#[ORM\Column(type: Types::SMALLINT)]
private int $rating;
One byte more per row than TINYINT, and everything works: schema tools, migrations, and other applications reading the same table.
Or keep TINYINT and say so explicitly:
#[ORM\Column(type: Types::SMALLINT, columnDefinition: 'TINYINT NOT NULL')]
private int $rating;
columnDefinition writes that SQL verbatim, so the column stays TINYINT
while PHP sees an integer. The cost is real: Doctrine can no longer
compare the column to its own idea of what the type should be, so schema
diffing gets less useful and migrations need more care. Reach for it when
the schema is not yours to change.
The full mapping
MySQL column type on the left, the Doctrine type it becomes on the right.
| MySQL | Doctrine |
|---|---|
tinyint |
boolean |
smallint |
smallint |
mediumint, int, integer |
integer |
bigint |
bigint |
tinytext, mediumtext, longtext, text |
text |
varchar, string, char |
string |
date |
date |
datetime, timestamp |
datetime |
time |
time |
float, double, real |
float |
decimal, numeric |
decimal |
year |
date |
blob, tinyblob, mediumblob, longblob |
blob |
binary, varbinary |
blob |
set |
simple_array |
Three of these are worth a second look.
Every kind of text is text. The distinction between TINYTEXT and
LONGTEXT — which is a real difference in maximum length — disappears at
the mapping layer. Doctrine will happily let you write more than a
TINYTEXT can hold and MySQL will truncate it, silently or not depending
on strict mode.
timestamp and datetime both become datetime, so the automatic
updating behavior a TIMESTAMP column may have in MySQL is invisible to
Doctrine. If the database is updating that column behind you, refresh the
entity or Doctrine will hand back the value it last knew.
set becomes simple_array. It works, and simple_array is
comma-joined text with no escaping, so a value containing a comma breaks
it. MySQL's SET has its own constraints that Doctrine knows nothing
about.
Where the source of truth lives
This table comes from the MySQL platform class in DBAL. If you need to
check a type not listed here, that class is the answer rather than any
article — including this one. It moved from lib/ to src/ and from
MySqlPlatform to MySQLPlatform between DBAL 2 and 4, which is why old
links into the file no longer resolve. The
types reference
is the stable address.
Questions this keeps raising
Why does my TINYINT column return true instead of a number?
Because DBAL's MySQL platform maps tinyint to boolean regardless of the declared length, so TINYINT(4) holding 7 comes back as true. Use smallint for the field, or keep the column and set columnDefinition to TINYINT so PHP sees an integer.
Does TINYINT(1) versus TINYINT(4) change anything?
Not to Doctrine. The display width in parentheses has never affected the range MySQL stores, and DBAL maps every tinyint the same way. MySQL 8.0.17 deprecated the display width for integer types entirely.
Is it safe to use columnDefinition?
It works and it costs you something. Doctrine writes your SQL verbatim and can no longer reason about the column, so schema comparison and migration diffing become less reliable for that field. Use it when the schema belongs to somebody else; prefer smallint when it is yours.
Why is my long text being truncated?
Because tinytext, mediumtext, longtext and text all map to Doctrine's text, so the mapping layer cannot tell you the column is a TINYTEXT holding 255 bytes. Doctrine will send more than fits and MySQL decides whether to truncate or error depending on strict mode.