Logo

dev-resources.site

for different kinds of informations.

Extbase entities and lazy loaded typed properties

Published at
9/18/2022
Categories
typo3
extbase
lazyloading
php74
Author
alexanderschnitzler
Categories
4 categories in total
typo3
open
extbase
open
lazyloading
open
php74
open
Author
19 person written this
alexanderschnitzler
open
Extbase entities and lazy loaded typed properties

Hi folks,

today I want to write about property types for lazy loaded entity properties in extbase:

use TYPO3\CMS\Extbase\Annotation as Extbase;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar
     */
    private $bar;
}
Enter fullscreen mode Exit fullscreen mode

That's how things looked until PHP 7.3, but how do we deal with it with it with PHP 7.4+ (property types) and PHP 8.0+ (union types)?

Well, let's start with 7.4 and also take \TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage into account.

Something we cannot do with PHP 7.4 is to use union types. The following example look neat but is impossible to implement.

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     */
    private Bar|LazyLoadingProxy|null $bar = null;
}
Enter fullscreen mode Exit fullscreen mode

But missing union types in PHP 7.4 aren't the only limitation we are facing here. There is a limitation/bug in extbase's reflection framework which lets extbase only detect a single type even in phpdoc blocks. So annotating @var Bar|LazyLoadingProxy already leads to an error as extbase does skip the whole property processing.

So the last example isn't working due to a missing language feature (in PHP 7.4), but the following example isn't working because of a limitation/bug in extbase:

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|LazyLoadingProxy
     */
    private $bar;
}
Enter fullscreen mode Exit fullscreen mode

Given the circumstances, what's the best approach here to be as strict and meaningful as possible? Here's my current best practice:

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|null
     * @phpstan-var Bar|LazyLoadingProxy|null
     */
    private ?object $bar = null;
}
Enter fullscreen mode Exit fullscreen mode

As phpdoc take precedence in extbase, the ?object property type is not relevant for extbase, but it already narrows down the allowed types. Depending on your IDE and it's support for custom annotations for static code analysis tools like phpstan, it will or will not know that the property can be a LazyLoadingProxy, but it will at least know the type is null or Bar. PHPStan, in this case, will know the whole truth and to me that's more important than full IDE support.

Speaking if LazyLoadingProxy...
\TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy::_loadRealInstance() is our public api to resolve the actual value of the proxy but unfortunately, the return type annotation of that method is just wrong. It says object which is neither specific nor correct. The actual return type is hard to tell because it is actually dynamic, in theory at least. It's very unlikely that the internal logic may fail but from a static code analysis standpoint, it's possible. If the actual value of the proxy cannot be resolved, _loadRealInstance() returns the actual value of the property, which in most cases is null. This means, the following code can break:

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|null
     * @phpstan-var Bar|LazyLoadingProxy|null
     */
    private ?object $bar = null;

    public function getBar(): ?Bar
    {
        return $this->bar instanceof LazyLoadingProxy
            ? $this->bar->_loadRealInstance()
            : $this->bar;
    }
}
Enter fullscreen mode Exit fullscreen mode

It's really just a theoretical case but my best practice for those getters looks like this:

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     * @var Bar|null
     * @phpstan-var Bar|LazyLoadingProxy|null
     */
    private ?object $bar = null;

    public function getBar(): ?Bar
    {
        if ($this->bar instanceof LazyLoadingProxy) {
            /** @var Bar|null $resolvedValue */
            $resolvedValue = $this->bar->_loadRealInstance();
            return $this->bar = $resolvedValue instanceof Bar
                ? $resolvedValue
                : null;
        }

        return $this->bar;
    }
}
Enter fullscreen mode Exit fullscreen mode

What about LazyObjectStorage?

Right, there is LazyObjectStorage as well. How to deal with that? Well, this is a lot easier because LazyObjectStorage is a sub class of ObjectStorage which enables us to write this code:

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;

class Foo
{
    /**
     * @Extbase\ORM\Lazy
     */
    private ObjectStorage $bar;

    public function __construct() {
        $this->initializeObject();
    }

    public function initializeObject() {
        $this->bar = new ObjectStorage();
    }

    public function getBar(): ObjectStorage
    {
        return $this->bar;
    }
}
Enter fullscreen mode Exit fullscreen mode

We completely omit the LazyObjectStorage type here because we really don't need it. There is no benefit for PHPStan and the like to know, neither for your IDE or you personally because the api of LazyObjectStorage is the same as for ObjectStorage and the underlying code can work the same both. Easy, right?

That's it for today.
Have a nice one!

typo3 Article's
30 articles in total
Favicon
What is TYPO3? Everything You Need to Know
Favicon
Symfony Station Communiqué — 10 January 2025 — A look at Symfony, Drupal, PHP, and other programming news!
Favicon
Discover What's New in TYPO3 CMS v13
Favicon
WordPress vs TYPO3 vs Craft CMS
Favicon
Generate Articles and Images Faster with TYPO3 AI Extension
Favicon
Symfony Station Communiqué — 06 December 2024 — A look at Symfony, Drupal, PHP, and other programming news!
Favicon
TYPO3 v13 LTS — Overview and Key Features Explained Simply
Favicon
Symfony Station Communiqué — 08 November 2024. A look at Symfony, Drupal, PHP, and programming news!
Favicon
Symfony Station Communiqué — 01 November 2024. A look at Symfony, Drupal, PHP, and programming news!
Favicon
Symfony Station Communiqué — 25 October 2024. A look at Symfony, Drupal, PHP, and programming news!
Favicon
Symfony Station Communiqué - 18 October 2024. A look at Symfony, Drupal, PHP, and programming news!
Favicon
TYPO3 v13.3 - Feature Freeze Fun
Favicon
Symfony Station Communiqué — 06 September 2024: A Look at Symfony, Drupal, PHP, Cybersec, and Fediverse News!
Favicon
TYPO3 AI Extension by T3Planet
Favicon
30+ Breaking Changes in TYPO3 v13.2
Favicon
TYPO3 v13.2—Release Notes
Favicon
Task-1 Testing methodologies & SDLC
Favicon
TYPO3 and ChatGPT: Increase performance with AI
Favicon
Effortless Language Redirection - TYPO3 Extension
Favicon
Integrate Svelte into PHP CMS: Typo3 and WordPress 👨‍🔧
Favicon
Extbase entities and property types
Favicon
Extbase entities and lazy loaded typed properties
Favicon
Adding external frontend widgets in TYPO3
Favicon
TYPO3 and minimalism
Favicon
TYPO3 Backend Access, User Roles & Permissions
Favicon
8+ Free TYPO3 Demo Sites To Explore Now!
Favicon
TYPO3 Talk with Luisa: Head of Marketing Team
Favicon
8 Best TYPO3 Docker Development Approaches
Favicon
TYPO3 Templates & Extensions Updates Release
Favicon
How to do TYPO3 Localization with Crowdin?

Featured ones: