Connection
Table of Contents
Introduction
The \TYPO3\
class extends the basic Doctrine
DBAL \Doctrine\
class and is mainly used internally in
TYPO3 to establish, maintain and terminate connections to single database
endpoints. These internal methods are not the scope of this documentation, since
an extension developer usually does not have to deal with them.
However, for an extension developer, the class provides a list of short-hand
methods that allow you to deal with query cases without the complexity
of the query builder. Using these methods
usually ends up in rather short and easy-to-read code. The methods have in common
that they only support "equal" comparisons in WHERE
conditions, that all
fields and values are automatically fully quoted, and that the created queries
are executed right away.
Note
The Connection
object is designed to work on a single table only. If
queries are performed on multiple tables, the object must not be reused.
Instead, a single Connection
instance per target table should be
retrieved via ConnectionPool. However, it
is allowed to use one Connection
object for multiple queries on the
same table.
Instantiation
Using the connection pool
An instance of the \TYPO3\
class is retrieved
from the ConnectionPool by calling
->get
and passing the table name for which a query
should be executed. The Connection
can be injected via constructor:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_domain_model_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function findSomething()
{
$connection = $this->connectionPool
->getConnectionForTable(self::TABLE_NAME);
}
}
Via dependency injection
Another way is to inject the Connection
object directly via
dependency injection if you only use one table.
-
Configure the concrete connection as a service
To make a concrete
Connection
object available as a service, use the factory option in the service configuration:services: _defaults: autowire: true autoconfigure: true public: false MyVendor\MyExtension\: resource: '../Classes/*' connection.tx_myextension_domain_model_mytable: class: 'TYPO3\CMS\Core\Database\Connection' factory: ['@TYPO3\CMS\Core\Database\ConnectionPool', 'getConnectionForTable'] arguments: - 'tx_myextension_domain_model_mytable' MyVendor\MyExtension\Domain\Repository\MyTableRepository: arguments: - '@connection.tx_myextension_domain_model_mytable'
-
Use constructor injection in your class
Now the
Connection
object for a specific table can be injected via the constructor:<?php declare(strict_types=1); namespace MyVendor\MyExtension\Domain\Repository; use TYPO3\CMS\Core\Database\Connection; final class MyTableRepository { public function __construct( private readonly Connection $connection, ) {} public function findSomething() { // Here you can use $this->connection directly } }
Parameter types
The parameter types are used in various places to bind values to types, for example, when using named parameters in the query builder:
// use TYPO3\CMS\Core\Database\Connection;
$queryBuilder->createNamedParameter(42, Connection::PARAM_INT);
The following parameter types are available:
\TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ NULL - Represents an SQL
NULL
data type. \TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ INT - Represents an SQL
INTEGER
data type. \TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ STR - Represents an SQL
CHAR
orVARCHAR
data type. \TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ LOB - Represents an SQL large object data type.
\TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ BOOL - Represents a boolean data type.
\TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ INT_ ARRAY - Represents an array of integer values.
\TYPO3\
CMS\ Core\ Database\ Connection:: PARAM_ STR_ ARRAY - Represents an array of string values.
The default parameter type is Connection::
, if this argument
is omitted.
Internally, these parameter types are mapped to the types Doctrine DBAL expects.
insert()
The insert
method creates and executes an INSERT INTO
statement.
Example:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function insertSomething(
int $pid,
string $someString,
array $someData,
): void {
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->insert(
self::TABLE_NAME,
[
'pid' => $pid,
'some_string' => $someString,
'json_field' => $someData,
],
);
}
}
Read how to instantiate a connection with the connection pool. See available parameter types.
New in version 12.1
This method supports the native database field declaration json
,
see Native JSON database field type support.
Arguments of the insert
method:
- The name of the table the row should be inserted. Required.
- An associative array containing field/value pairs. The key is a field name, the value is the value to be inserted. All keys are quoted to field names and all values are quoted to string values. Required.
-
Specify how single values are quoted. This is useful if a date, number or similar should be inserted. Optional.
The example below quotes the first value to an integer and the second one to a string:
<?php declare(strict_types=1); namespace MyVendor\MyExtension\Domain\Repository; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; final class MyTableRepository { private const TABLE_NAME = 'tx_myextension_mytable'; public function __construct( private readonly ConnectionPool $connectionPool, ) {} public function insertSomething( int $pid, string $someString, ): void { $this->connectionPool ->getConnectionForTable(self::TABLE_NAME) ->insert( self::TABLE_NAME, [ 'pid' => $pid, 'some_string' => $someString, ], [ Connection::PARAM_INT, Connection::PARAM_STR, ], ); } }
Read how to instantiate a connection with the connection pool. See available parameter types.
insert
returns the number of affected rows. Guess what? That is the
number 1
... If something goes wrong, a \Doctrine\
is
thrown.
Note
A list of allowed field types for proper quoting can be found in the
\TYPO3\
class.
bulkInsert()
This method insert multiple rows at once:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function bulkInsertSomething(
int $pid1,
int $pid2,
string $someString1,
string $someString2,
): void {
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->bulkInsert(
self::TABLE_NAME,
[
[$pid1, $someString1],
[$pid2, $someString2],
],
[
'pid',
'title',
],
[
Connection::PARAM_INT,
Connection::PARAM_STR,
],
);
}
}
Read how to instantiate a connection with the connection pool. See available parameter types.
Arguments of the bulk
method:
- The name of the table the row should be inserted. Required.
- An array of the values to be inserted. Required.
- An array containing the column names of the data which should be inserted. Optional.
- Specify how single values are quoted. Similar to insert(); if omitted, everything will be quoted to strings. Optional.
The number of inserted rows are returned. If something goes wrong, a
\Doctrine\
is thrown.
Note
MySQL is quite forgiving when it comes to insufficient field quoting:
Inserting a string into an int
field does not cause an error and
MySQL adjusts internally. However, other DBMSes are not that relaxed and may raise errors. It is good practice to
specify field types for each field, especially if they are not strings.
Doing this immediately will reduce the number of bugs that occur when people
run your extension an anything else than MySQL.
update()
Create an UPDATE
statement and execute it. The example from FAL's
Resource
sets a storage to offline:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function updateSomething(
int $uid,
string $someValue,
array $someData,
): void {
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->update(
self::TABLE_NAME,
[
'some_value' => $someValue,
'json_data' => $someData,
],
['uid' => $uid],
[Connection::PARAM_INT],
);
}
}
New in version 12.1
This method supports the native database field declaration json
,
see Native JSON database field type support.
Read how to instantiate a connection with the connection pool. See available parameter types.
Arguments of the update
method:
- The name of the table to update. Required.
- An associative array containing field/value pairs to be updated. The key is
a field name, the value is the value. In SQL they are mapped to the
SET
keyword. Required. - The update criteria as an array of key/value pairs. The key is the field
name, the value is the value. In SQL they are mapped in a
WHERE
keyword combined withAND
. Required. - Specify how single values are quoted. Similar to insert(); if omitted, everything will be quoted to strings. Optional.
The method returns the number of updated rows. If something goes wrong, a
\Doctrine\
is thrown.
Note
The third argument WHERE `foo` = 'bar'
supports only equal =
. For more
complex stuff the query builder must be used.
delete()
Execute a DELETE
query using equal
conditions in WHERE
, example
from Backend
, to mark rows as no longer locked by a user:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function deleteSomething(
int $uid,
): void {
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->delete(
self::TABLE_NAME,
['uid' => $uid],
[Connection::PARAM_INT],
);
}
}
Read how to instantiate a connection with the connection pool. See available parameter types.
Arguments of the delete
method:
- The name of the table. Required.
- The delete criteria as an array of key/value pairs. The key is the field
name, the value is the value. In SQL they are mapped in a
WHERE
keyword combined withAND
. Required. - Specify how single values are quoted. Similar to insert(); if omitted, everything will be quoted to strings. Optional.
The method returns the number of deleted rows. If something goes wrong, a
\Doctrine\
is thrown.
Note
TYPO3 uses a "soft delete" approach for
many tables. Instead of deleting a row directly in the database, a field -
often called deleted
- is set from 0 to 1. Executing a DELETE
query circumvents this and really removes rows from a table. For most
tables, it is better to use the DataHandler API
to handle deletions instead of executing such low-level queries directly.
truncate()
This method empties a table, removing all rows. It is usually much faster than a delete() of all rows. This typically resets "auto increment primary keys" to zero. Use with care:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyCacheRepository
{
private const TABLE_NAME = 'cache_myextension';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function truncateSomething(
int $uid,
): void {
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->truncate(self::TABLE_NAME);
}
}
Read how to instantiate a connection with the connection pool.
The argument is the name of the table to be truncated. If something goes wrong,
a \Doctrine\
is thrown.
count()
This method executes a COUNT
query. Again, this becomes useful when very
simple COUNT
statements are to be executed. The example below returns the
number of active rows (not hidden or deleted or disabled by time) from the
table tx_
whose
field some_
field set to $something
:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function countSomething(
int $something,
): int {
$connection = $this->connectionPool
->getConnectionForTable(self::TABLE_NAME);
return $connection->count(
'*',
self::TABLE_NAME,
['some_value' => $something],
);
}
}
Read how to instantiate a connection with the connection pool.
Arguments of the count
method:
- The field to count, usually
*
oruid
. Required. - The name of the table. Required.
- The select criteria as an array of key/value pairs. The key is the field
name, the value is the value. In SQL they are mapped in a
WHERE
keyword combined withAND
. Required.
The method returns the counted rows.
Remarks:
Connection::
returns the number directly as an integer, unlike the method of the query builder it is not necessary to callcount () ->fetch
or similar.Columns (0) - The third argument expects all
WHERE
values to be strings, each single expression is combined withAND
. - The restriction builder kicks in and
adds additional
WHERE
conditions based on TCA settings. - Field names and values are quoted automatically.
- If anything more complex than a simple
equal
condition onWHERE
is needed, the query builder methods are the better choice: next to select(), the->count
query is often the least useful method of the() Connection
object.
select()
This method creates and executes a simple SELECT
query based on equal
conditions. Its usage is limited, the restriction builder kicks in and key/value pairs are automatically
quoted:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use Doctrine\DBAL\Result;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function countSomething(
int $something,
): Result {
return $this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->select(
['*'],
self::TABLE_NAME,
['some_value' => $something],
);
}
}
Read how to instantiate a connection with the connection pool.
Arguments of the select
method:
- The columns of the table which to select as an array. Required.
- The name of the table. Required.
- The select criteria as an array of key/value pairs. The key is the field
name, the value is the value. In SQL they are mapped in a
WHERE
keyword combined withAND
. Optional. - The columns to group the results by as an array. In SQL they are mapped
in a
GROUP BY
keyword. Optional. - An associative array of column name/sort directions pairs. In SQL they are
mapped in an
ORDER BY
keyword. Optional. - The maximum number of rows to return. In SQL it is mapped in a
LIMIT
keyword. Optional. - The first result row to select (when used the maximum number of rows). In
SQL it is mapped in an
OFFSET
keyword. Optional.
In contrast to the other short-hand methods, ->select
returns a
Result object ready for ->fetch
to
get single rows or for ->fetch
to get all rows at once.
Remarks:
- For non-trivial
SELECT
queries it is often better to switch to the according method of the query builder object. - The restriction builder adds default
WHERE
restrictions. If these restrictions do not match the query requirements, it is necessary to switch to theQuery
method for fine-grainedBuilder->select () WHERE
manipulation.
lastInsertId()
Changed in version 13.0
The method no longer accepts the table name as first argument and the name of the auto-increment field as second argument.
This method returns the uid
of the last insert() statement. This is useful if the ID is to be used
directly afterwards:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function insertSomething(
array $someData,
): int {
$connection = $this->connectionPool
->getConnectionForTable(self::TABLE_NAME);
$connection
->insert(
self::TABLE_NAME,
$someData,
);
return (int)$connection->lastInsertId();
}
}
Read how to instantiate a connection with the connection pool.
Remarks:
- The last inserted ID needs to be retrieved directly before inserting a record to another table. That should be the usual workflow used in the wild - but be aware of this.
createQueryBuilder()
The query builder should not be reused for multiple different queries. However, sometimes it is convenient to first fetch a connection object for a specific table and execute a simple query, and later create a query builder for a more complex query from that connection object. The usefulness of this method is limited, however, and at the time of writing no good example could be found in the Core.
The method can also be useful in loops to save some precious code characters:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function useQueryBuilder(
array $someData,
): void {
$connection = $this->connectionPool->getConnectionForTable(self::TABLE_NAME);
foreach ($someData as $value) {
$queryBuilder = $connection->createQueryBuilder();
$myResult = $queryBuilder
->select('*')
->from(self::TABLE_NAME)
->where(
$queryBuilder->expr()->eq(
'some_field',
$queryBuilder->createNamedParameter($value),
),
)
->executeQuery()
->fetchAllAssociative();
// do something
}
}
}
Read how to instantiate a connection with the connection pool.
Native JSON database field type support
New in version 12.1
TYPO3 Core's Database API based on Doctrine DBAL supports the native
database field type json
, which is already available for all supported DBMS
of TYPO3 v12.
JSON-like objects or arrays are automatically serialized during writing a dataset to the database, when the native JSON type was used in the database schema definition.
By using the native database field declaration json
in ext_
file within an extension, TYPO3 converts arrays or objects of type
\Json
into a serialized JSON value in the database when
persisting such values via Connection->insert
or
Connection->update
if no explicit DB types are handed in as additional
method argument.
TYPO3 now utilizes the native type mapping of Doctrine to convert special types such as JSON database field types automatically for writing.
Example:
CREATE TABLE tx_myextension_mytable
(
some_string varchar(200) DEFAULT '',
json_field json
);
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Database\ConnectionPool;
final class MyTableRepository
{
private const TABLE_NAME = 'tx_myextension_mytable';
public function __construct(
private readonly ConnectionPool $connectionPool,
) {}
public function insertSomething(
int $pid,
string $someString,
array $someData,
): void {
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->insert(
self::TABLE_NAME,
[
'pid' => $pid,
'some_string' => $someString,
'json_field' => $someData,
],
);
}
}
Note
When reading a record from the database via QueryBuilder, it is still necessary though to transfer the serialized value to an array or object doing custom serialization.