Skip to content

[Bug]: Building (bootstrapping) Data Object classes in CI not always possible #17865

Open

Description

Pimcore version

11.x (since 6.x)

Steps to reproduce

Pimcore generates its Data Object classes on class creation/update in backend and via commands pimcore:deployment:classes-rebuild and the newer pimcore:build:classes. The latter doesn't require a database and thus is especially useful in CI workflows such as running static analysis with PHPStan.

There is no issue with this approach with a working environment, but may become one in CI, where those data object classes do not yet exist and need to be created.

This will be the case when you reference the data object class within a Symfony service: Either by referencing a class constant (e.g. of a model class override) or referencing it in the service method's signature - see examples below.

As the console commands for (re)building the data object classes itself is a Symfony Console Command, running it will result in all services being built (CompilerPasses, DI-Container). If one of those services reference the data object class in a specific way, there's a chicken-and-egg problem as the class doesn't exist yet and the build-command can't run and exits with an error, that the class file cannot be found.

Example referencing class constant (of class override)

<?php
namespace App\EventListener;

use App\Model\DataObject\Foobar;    // extends Pimcore\Model\DataObject\Foobar
use Pimcore\Event\Model\DataObjectEvent;

class TestListener
{
    private const VALID_STATES = [Foobar::STATE_FOO, Foobar::STATE_BAR];      // Autoloader error

    // ...
}

Example error when referencing a class constant:
Attempted to load class "Foobar" from namespace "Pimcore\Model\DataObject". Did you forget a "use" statement for another namespace?

Example referencing class in signature

    App\TargetResolver\FoobarTargetResolver: ~

    App\TargetResolver\TargetResolverRegistry:
        arguments:
            $resolvers:
                foobar: '@App\TargetResolver\FoobarTargetResolver'
<?php
namespace App\TargetResolver;

readonly class TargetResolverRegistry
{
    /**
     * @param TargetResolverInterface[] $resolvers
     */
    public function __construct(private array $resolvers)
    {
    }

    public function get(string $type): TargetResolverInterface
    {
        if (!isset($this->resolvers[$type])) {
            throw new \InvalidArgumentException("No resolver registered for type: $type");
        }

        return $this->resolvers[$type];
    }
}
<?php
namespace App\TargetResolver;

use Pimcore\Model\DataObject\Concrete;

interface TargetResolverInterface
{
    public function resolve(string $targetId): ?Concrete;
}
<?php
namespace App\TargetResolver;

use Pimcore\Model\DataObject\Concrete;
use Pimcore\Model\DataObject\Foobar;

class FoobarTargetResolver implements TargetResolverInterface
{
    public function resolve(string $targetId): ?Foobar          // ReflectionError
    {
        return Foobar::getById($targetId);
    }
}

Example error when returning an instance of the class in a service:
Error: During inheritance of App\TargetResolver\FoobarTargetResolver, while autoloading Pimcore\Model\DataObject\Foobar: Uncaught ReflectionException: Class "Pimcore\Model\DataObject\Foobar" not found while loading "App\TargetResolver\FoobarTargetResolver".

Workarounds/Solutions

  1. App: Don't reference a data object class model/listing in a way described above.
  2. Pimcore: Make sure data object classes can be built or bootstrapped even without a functioning Symfony environment.
  3. Docs: Mention that in some cases it's not possible to reference those classes in services.

Crude bootstrap workaround

A CLI script (not using Symfony framework) iterates over all class definitions and creates stub/bootstrap classes for data object models/listings so that pimcore:build:classes can build the real class files.

Actual Behavior

In CI or a pristine dev environment data object classes can't be build, if they are referenced in a way that Symfony resolves them while building DI container/services.

Expected Behavior

In CI environment, data object classes can be bootstrapped.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions