---
title: "Basic create, read, update, and delete operations (CRUD)"
manual: "TYPO3 Explained"
version: "11.5"
permalink: "https://docs.typo3.org/permalink/t3coreapi:database-basic-crud@11.5"
source: "ApiOverview/Database/BasicCrud/Index.rst"
rendered: "2026-10-21T15:30:00+00:00"
---

> [!WARNING]
> **Attention**
>
> TYPO3 v11 has reached end-of-life as of October 31th 2024 and
> is no longer being maintained. Use the version switcher on the top
> left of this page to select documentation for a supported version of TYPO3.
>
> Need more time before upgrading?
> You can purchase Extended Long Term Support (ELTS) for TYPO3 v11 here:
> [TYPO3 ELTS](https://typo3.com/services/extended-support-elts).

# Basic create, read, update, and delete operations (CRUD) {#database-basic-crud}

This section provides a list of basic usage examples of the query API. This is
just a starting point. Details about the single methods can be found in the
following chapters, especially [QueryBuilder](https://docs.typo3.org/permalink/t3coreapi:database-query-builder@11.5) and
[Connection](https://docs.typo3.org/permalink/t3coreapi:database-connection@11.5).

All examples use [dependency injection](https://docs.typo3.org/permalink/t3coreapi:dependencyinjection@11.5) to provide
the [ConnectionPool](https://docs.typo3.org/permalink/t3coreapi:database-connection-pool@11.5) in the classes.

****Table of Contents****

-   [Insert a row](https://docs.typo3.org/permalink/t3coreapi:insert-a-row@11.5)
-   [Select a single row](https://docs.typo3.org/permalink/t3coreapi:select-a-single-row@11.5)
-   [Update multiple rows](https://docs.typo3.org/permalink/t3coreapi:update-multiple-rows@11.5)
-   [Delete a row](https://docs.typo3.org/permalink/t3coreapi:delete-a-row@11.5)

## Insert a row {#insert-a-row}

A direct insert into a table:

**EXT:my_extension/Classes/Domain/Repository/MyInsertRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyInsertRepository
{
    private ConnectionPool $connectionPool;

    public function __construct(ConnectionPool $connectionPool)
    {
        $this->connectionPool = $connectionPool;
    }

    public function insertSomeData(): void
    {
        $this->connectionPool
            ->getConnectionForTable('tt_content')
            ->insert(
                'tt_content',
                [
                    'pid' => 42,
                    'bodytext' => 'ipsum',
                ]
            );
    }
}

```

This results in the following SQL statement:

```sql
INSERT INTO `tt_content` (`pid`, `bodytext`)
    VALUES ('42', 'ipsum')
```

## Select a single row {#database-select}

Fetching a single row directly from the `tt_content` table:

**EXT:my_extension/Classes/Domain/Repository/MySelectRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MySelectRepository
{
    private ConnectionPool $connectionPool;

    public function __construct(ConnectionPool $connectionPool)
    {
        $this->connectionPool = $connectionPool;
    }

    /**
     * @return array|false
     */
    public function selectSomeData()
    {
        $uid = 4;

        return $this->connectionPool
            ->getConnectionForTable('tt_content')
            ->select(
                ['uid', 'pid', 'bodytext'], // fields to select
                'tt_content',               // from
                ['uid' => $uid]             // where
            )
            ->fetchAssociative();
    }
}

```

Result in `$row`:

```none
array(3 items)
   uid => 4 (integer)
   pid => 35 (integer)
   bodytext => 'some content' (12 chars)
```

The engine encloses field names in quotes, adds default TCA restrictions such as
`deleted=0`, and prepares a query to be executed with this final statement:

```sql
SELECT `uid`, `pid`, `bodytext`
    FROM `tt_content`
    WHERE (`uid` = '4')
        AND ((`tt_content`.`deleted` = 0)
        AND (`tt_content`.`hidden` = 0)
        AND (`tt_content`.`starttime` <= 1669838885)
        AND ((`tt_content`.`endtime` = 0) OR (`tt_content`.`endtime` > 1669838885)))
```

> [!NOTE]
> The default restrictions `deleted`, `hidden`, `startime` and
> `endtime` based on the [TCA setting of a table](https://docs.typo3.org/m/typo3/reference-tca/11.5/en-us/Ctrl/Index.html#ctrl) are
> only applied to `select()` calls, they are **not** added for
> `delete()` or other query types.

### Select multiple rows with some "where" magic {#select-multiple-rows-with-some-where-magic}

Advanced query using the `QueryBuilder` and manipulating the default
restrictions:

**EXT:my_extension/Classes/Domain/Repository/MyQueryBuilderSelectRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Utility\GeneralUtility;

final class MyQueryBuilderSelectRepository
{
    private ConnectionPool $connectionPool;

    public function __construct(ConnectionPool $connectionPool)
    {
        $this->connectionPool = $connectionPool;
    }

    public function selectSomeData(): array
    {
        $uid = 4;

        $queryBuilder = $this->connectionPool
            ->getQueryBuilderForTable('tt_content');

        // Remove all default restrictions (delete, hidden, starttime, stoptime),
        // but add DeletedRestriction again
        $queryBuilder->getRestrictions()
            ->removeAll()
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));

        // Execute a query with "bodytext=lorem OR uid=4" and proper quoting
        return $queryBuilder
            ->select('uid', 'pid', 'bodytext')
            ->from('tt_content')
            ->where(
                $queryBuilder->expr()->or(
                    $queryBuilder->expr()->eq(
                        'bodytext',
                        $queryBuilder->createNamedParameter('lorem')
                    ),
                    $queryBuilder->expr()->eq(
                        'uid',
                        $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)
                    )
                )
            )
            ->executeQuery()
            ->fetchAllAssociative();
    }
}

```

Result in `$rows`:

```none
array(2 items)
    0 => array(3 items)
        uid => 4 (integer)
        pid => 35 (integer)
        bodytext => 'ipsum' (5 chars)
    1 => array(3 items)
        uid => 366 (integer)
        pid => 13 (integer)
        bodytext => 'lorem' (5 chars)
```

The executed query looks like this:

```sql
SELECT `uid`, `pid`, `bodytext`
    FROM `tt_content`
    WHERE ((`bodytext` = 'lorem') OR (`uid` = 4))
        AND (`tt_content`.`deleted` = 0)
```

## Update multiple rows {#update-multiple-rows}

**EXT:my_extension/Classes/Domain/Repository/MyUpdateRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyUpdateRepository
{
    private ConnectionPool $connectionPool;

    public function __construct(ConnectionPool $connectionPool)
    {
        $this->connectionPool = $connectionPool;
    }

    public function updateSomeData()
    {
        $this->connectionPool->getConnectionForTable('tt_content')
            ->update(
                'tt_content',
                [ 'bodytext' => 'ipsum' ], // set
                [ 'bodytext' => 'lorem' ]  // where
            );
    }
}

```

The executed query looks like this:

```sql
UPDATE `tt_content` SET `bodytext` = 'ipsum'
    WHERE `bodytext` = 'lorem'
```

> [!TIP]
> You can also use the `QueryBuilder` to create more complex update
> queries. For examples, see the [QueryBuilder chapter](https://docs.typo3.org/permalink/t3coreapi:database-query-builder-update-set@11.5).

## Delete a row {#delete-a-row}

**EXT:my_extension/Classes/Domain/Repository/MyDeleteRepository.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Repository;

use TYPO3\CMS\Core\Database\ConnectionPool;

final class MyDeleteRepository
{
    private ConnectionPool $connectionPool;

    public function __construct(ConnectionPool $connectionPool)
    {
        $this->connectionPool = $connectionPool;
    }

    public function deleteSomeData()
    {
        $this->connectionPool->getConnectionForTable('tt_content')
            ->delete(
                'tt_content', // from
                ['uid' => 4711]  // where
            );
    }
}

```

The executed query looks like this:

```sql
DELETE FROM `tt_content`
    WHERE `uid` = '4711'
```
