Relations between Extbase models
Extbase supports different types of hierarchical relationships between domain objects. All relationships can be defined unidirectional or multidimensional in the model.
On the side of the relationship that can only have one counterpart, you must decide whether it is possible to have no relationship (allow null) or not.
Nullable relations
There are two ways to allow
null
for a property in PHP:
-
Mark the type declaration as nullable by prefixing the type name with a question mark:
Example for a nullable propertyprotected ?Person $secondAuthor = null;
Copied! -
Use a union type:
Example for a union type of null and Personprotected Person|null $secondAuthor = null;
Copied!
Both declarations have the same meaning.
1:1-relationship
A blog post can have, in our case, exactly one additional info attached to it. The info always belongs to exactly one blog post. If the blog post gets deleted, the info does get related.
class Post extends AbstractEntity implements \Stringable
{
/**
* 1:1 optional relation
*/
protected ?Info $additionalInfo = null;
public function getAdditionalInfo(): ?Info
{
return $this->additionalInfo;
}
public function setAdditionalInfo(?Info $additionalInfo): void
{
$this->additionalInfo = $additionalInfo;
}
}
1:n-relationship
A blog can have multiple posts in it. If a blog is deleted all of its posts should be deleted. However a blog might get displayed without displaying the posts therefore we load the posts of a blog lazily:
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
class Blog extends AbstractEntity
{
/**
* @var ?ObjectStorage<Post>
*/
public ?ObjectStorage $posts = null;
/**
* Adds a post to this blog
*/
public function addPost(Post $post): void
{
$this->posts?->attach($post);
}
/**
* Remove a post from this blog
*/
public function removePost(Post $postToRemove): void
{
$this->posts?->detach($postToRemove);
}
/**
* Returns all posts in this blog
*
* @return ObjectStorage<Post>
*/
public function getPosts(): ObjectStorage
{
return $this->posts;
}
/**
* @param ObjectStorage<Post> $posts
*/
public function setPosts(ObjectStorage $posts): void
{
$this->posts = $posts;
}
}
Note
Note the subtle differences here. The methods
set
and
get
refer simultaneously to all blog posts.
They expect and hence provide an
Object
object.
The methods
add
and
remove
refer to a single
post object that is added to the list or removed from.
Attention
It is possible to get an array of objects from an
Object
by calling
$this->posts->to
. However doing so and looping
the resulting array might cause performance problems.
Each post belongs to exactly one blog, of course a blog does not get deleted when one of its posts gets deleted.
class Post extends AbstractEntity implements \Stringable
{
protected ?Blog $blog = null;
}
A post can also have multiple comments and each comment belongs to exactly one blog. However we never display a comment without its post therefore we do not need to store information about the post in the comment's model: The relationship is unidirectional.
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
class Post extends AbstractEntity implements \Stringable
{
/**
* @var ?ObjectStorage<Comment>
*/
public ?ObjectStorage $comments = null;
}
The model of the comment has no property to get the blog post in this case.
n:1-relationships
n:1-relationships are the same like 1:n-relationsships but from the perspective of the object:
Each post has exactly one main author but an author can write several blog posts or none at all. He can also be a second author and no main author.
class Post extends AbstractEntity
{
/**
* @var Person
*/
protected Person $author;
protected Person|null $secondAuthor;
}
Once more the model of the author does not have a property containing the authors posts. If you would want to get all posts of an author you would have to make a query in the PostRepository taking one or both relationships (first author, second author) into account.
m:n-relationship
A blog post can have multiple categories, each category can belong to multiple blog posts.
class Post extends AbstractEntity implements \Stringable
{
/**
* @var Person
*/
protected ?Person $author = null;
protected ?Person $secondAuthor = null;
}
Eager loading and lazy loading
By default, Extbase loads all child objects with the parent object (so for
example, all posts of a blog). This behavior is called eager loading.
The annotation
@TYPO3\
causes Extbase to
load and build the objects only when they
are actually needed (lazy loading). This can lead to a significant
increase in performance.
On cascade remove
The annotation
@TYPO3\
has
the effect that, if a blog is deleted, its posts will also be deleted
immediately. Extbase usually leaves all child objects' persistence unchanged.
Besides these two, there are a few more annotations available, which will be used in other contexts. For the complete list of all Extbase supported annotations, see the chapter Annotations in Extbase.