The storagePid: where Extbase looks for records
Every Extbase repository query — except
find and
find — is restricted to records stored on specific TYPO3
pages, the storage pages, configured through the storagePid. This is not an
optional filter you opt into: it is a constraint Extbase adds to the query
automatically, alongside the language and visibility constraints.
Because it is applied silently, the storagePid is the setting that most often makes a query behave differently from what you expect — too few records, too many, or the wrong ones. This page collects the whole story: how the value is resolved, how to widen it to subpages, and how to override or drop it for a single query.
On this page
storagePid as a query constraint
When a repository builds a query, Extbase adds a
WHERE condition limiting
the result to records whose
pid is one of the configured storage pages.
The only built-in methods that skip it are
find and
find, which look a record up by its unique identifier
regardless of page.
This matters for two reasons. First, a repository with no configured storagePid
queries page
0 and finds nothing — see
Why storagePid = 0 does not disable the restriction. Second, the constraint is resolved
from several configuration sources that override one another, so the page a query
actually searches is not always the one you set.
The storagePid resolution chain in the frontend
Several options feed the storagePid, and later ones override earlier ones. In a frontend request Extbase resolves them in this order, depending on the controller that handles the request:
- Framework TypoScript —
config.. This is the framework-level default that applies to every Extbase plugin, and the same key a backend module reads (see below). When nothing sets it, the frontend default istx_ extbase. persistence. storage Pid 0. - Extension plugin TypoScript —
plugin.overrides the framework value for all plugins of one extension.tx_ myextension. persistence. storage Pid - Plugin-specific TypoScript —
plugin.overrides the extension-wide value for that one plugin.tx_ myextension_ conferencelist. persistence. storage Pid - The Startingpoint field on a plugin's content element. When an editor
fills in the Behaviour > Starting point field, the chosen pages
override the TypoScript value. The label hides the real database column,
which is
pages— useful to know when inspecting records or writing overrides, as the label is not guaranteed to read the same on every instance or in every language. - FlexForm — only if a plugin's FlexForm defines a field bound to
persistence.. A FlexForm overrides the storage page only if it deliberately exposes thestorage Pid persistencesheet. This is mentioned for completeness — it would be unusual to expose apersistence.FlexForm field on a plugin that already has astorage Pid pages(Startingpoint) field. - PHP, per query — query settings override everything above. Where in your
code you set them decides how wide the effect is: inside a single repository
method only that one query changes; inside an overridden
createevery query the repository builds is affected. See Overriding the storagePid for a single query.Query ()
The most specific option that supplies a value is applied; a specific query's settings always have the final say.
The storagePid resolution chain in a backend module
An Extbase repository behaves the same way inside a backend module as it does in the frontend: every query is restricted to the configured storage pages. What differs is how the storagePid is resolved. A backend module is not a content element on a page, so there is no plugin record, no FlexForm and no Starting point field to draw from. The chain is therefore much shorter:
- Framework TypoScript —
config.. Note thetx_ extbase. persistence. storage Pid configscope: a backend module reads the same framework-level key the frontend uses for its Extbase configuration, not apluginscope. - The current page — if nothing above sets a storagePid, Extbase falls back
to the page the module is currently showing, taken from the
idrequest parameter (the page selected in the page tree). When no page is selected — or the module has no page tree at all, as many backend modules do not — this falls back to0. - Module TypoScript —
module., overridden by the module-specifictx_ myextension. persistence. storage Pid module.. This is the backend counterpart of thetx_ myextension_ mymodule. persistence. storage Pid plugin.scope used in the frontend, and the usual place to set a storagePid for a module.tx_* - PHP, per query — query settings override everything above, exactly as in the frontend. See Overriding the storagePid for a single query.
module.tx_myextension.persistence {
storagePid = 42
# Include records stored one level below page 42
recursive = 1
}
Module TypoScript is conventionally registered in the extension's
ext_typoscript_setup.typoscript, as shown above, so the configuration is
global and does not depend on which page the module happens to be viewing. This
is the recommended placement, see module TypoScript reference.
Note
Because the current-page fallback depends on the id request parameter, a
module without a page tree (or with no page selected) resolves its storagePid
to
0. If your module is not meant to be scoped to the current page or the root,
set an explicit storagePid in
module. TypoScript, or drop
the restriction per query with
set — see
Overriding the storagePid for a single query.
The
recursive setting works the same in a backend module as in the
frontend, as described next.
Searching subpages with the recursive setting
By default, Extbase searches only the storage pages you configured — not their subpages. To include records stored in subfolders below the storage page, set a recursion depth.
In TypoScript,
persistence. applies to the
TypoScript-configured storagePids:
plugin.tx_myextension.persistence {
storagePid = 42
# Include records stored up to two levels below page 42
recursive = 2
}
The Starting point field has its own Recursive selector next to it that does the same for the editor-chosen pages.
Warning
Recursion is a frequent source of too many results, not too few. A recursive depth set too high pulls in records from unrelated subpages that happen to sit below the storage page. Set the depth to the smallest value that covers your folder structure.
Overriding the storagePid for a single query
The configured storagePid is the default for every query a repository builds. To change it for one query, use the query settings object inside a custom repository method:
<?php
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
class ConferenceRepository extends Repository
{
public function findAllAcrossPages(): QueryResultInterface
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
return $query->execute();
}
}
Two settings control the page restriction:
| Name | Type | Default |
|---|---|---|
bool
|
true
|
|
int
|
setRespectStoragePage(bool)
-
- Type
bool- Default
true
When
false, the storagePid restriction is dropped entirely and the query searches the whole table regardless of page. This is how you disable the page restriction.
setStoragePageIds(array)
-
- Type
int[]
Sets the storage pages explicitly for this query, overriding the configured storagePid. Expects an array of integer page UIDs. Has no effect once
setis set.Respect Storage Page (false)
The query settings object also controls language, enable fields and deleted records, which are independent of the storagePid:
See also
Overriding query behaviour with query settings — the language, enable-field and deleted-record settings on the same query settings object.
Why
storagePid = 0 does not disable the restriction
A common misconception is that setting the storagePid to
0 switches
off the page restriction. It does not. For an ordinary table,
0 is the UID of
the page tree root, a level no editor ever stores records on — so the query looks
for records on page 0 and finds none. The effect is an empty result, not an
unrestricted query.
To search the whole table irrespective of page, drop the restriction in PHP instead:
<?php
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
class ConferenceRepository extends Repository
{
public function findEverywhere(): QueryResultInterface
{
$query = $this->createQuery();
// Search the whole table, ignoring the configured storage page
$query->getQuerySettings()->setRespectStoragePage(false);
return $query->execute();
}
}
Building the storagePid array from editor input
When for any reason the storagePid array can't be built by Extbase but you want
to do it manually, use Core API rather than constructing the recursive page list
yourself. The core
\TYPO3\ resolves the recursive
list of page UIDs for you, honouring access rights and the enable-field rules:
<?php
namespace MyVendor\MyExtension\Domain\Repository;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
class ConferenceRepository extends Repository
{
public function __construct(
protected readonly PageRepository $pageRepository,
) {
parent::__construct();
}
/**
* @param int[] $startPages Pages chosen by an editor or by plugin settings
*/
public function findInPagesRecursive(array $startPages, int $depth): QueryResultInterface
{
$storagePageIds = $this->pageRepository->getPageIdsRecursive($startPages, $depth);
$query = $this->createQuery();
$query->getQuerySettings()->setStoragePageIds($storagePageIds);
return $query->execute();
}
}
The resulting integer array is exactly what
set expects.
Letting the core API build it keeps the recursion logic identical to what Extbase
applies internally.
See also
- The Extbase repository — where the storagePid constraint applies to the built-in find methods.
- Querying the database with Extbase — building queries, constraints, ordering and the other query settings.