laminas / laminas-cli

Console command runner, exposing commands written in Laminas MVC and Mezzio components and applications
https://docs.laminas.dev/laminas-cli
BSD 3-Clause "New" or "Revised" License
55 stars 22 forks source link

Custom `ContainerLoaderInterface` by application #81

Closed boesing closed 2 years ago

boesing commented 2 years ago
Q A
Documentation yes
Bugfix yes
BC Break no
New Feature yes

Description

I am currently migrating a project from zf-console to laminas-cli. This project also works with doctrine and thus currently provides cli-config.php which is parsed by doctrine when using vendor/bin/doctrine.

To avoid having multiple symfony command toolings around, I would like to integrate these commands from doctrine/orm into laminas-cli.


As of now, this would only be possible by manually adding all of the commands to a config file. If there will be new commands, they wont be added automatically to laminas-cli and thus, manual work needs to be done.

I was thinking on how that could work and implemented this as a PoC.

boesing commented 2 years ago

Current workaround to add doctrine commands to laminas-cli:

<?php

declare(strict_types=1);

use Application\Doctrine\DoctrineCommandAbstractFactory;
use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand;
use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand;
use Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand;

return (static function (): array {
    return [
        'dependencies' => [
            'factories' => [
                MetadataCommand::class => DoctrineCommandAbstractFactory::class,
                QueryCommand::class => DoctrineCommandAbstractFactory::class,
                ResultCommand::class => DoctrineCommandAbstractFactory::class,
                GenerateProxiesCommand::class => DoctrineCommandAbstractFactory::class,
                ValidateSchemaCommand::class => DoctrineCommandAbstractFactory::class,
            ],
        ],
        'laminas-cli' => [
            'commands' => [
                'orm:clear-cache:metadata' => MetadataCommand::class,
                'orm:clear-cache:query' => QueryCommand::class,
                'orm:clear-cache:result' => ResultCommand::class,
                'orm:generate-proxies' => GenerateProxiesCommand::class,
                'orm:validate-schema' => ValidateSchemaCommand::class,
            ],
        ],
    ];
})();
<?php

declare(strict_types=1);

namespace Application\Doctrine;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand;
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand;
use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand;
use Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand;
use Doctrine\ORM\Tools\Console\EntityManagerProvider;
use Interop\Container\ContainerInterface;
use InvalidArgumentException;
use Laminas\ServiceManager\Factory\AbstractFactoryInterface;
use Webmozart\Assert\Assert;
use function assert;
use function in_array;

final class DoctrineCommandAbstractFactory implements AbstractFactoryInterface
{
    private const COMMANDS = [
        MetadataCommand::class,
        QueryCommand::class,
        ResultCommand::class,
        GenerateProxiesCommand::class,
        ValidateSchemaCommand::class,
    ];

    public function canCreate(ContainerInterface $container, $requestedName)
    {
        return in_array($requestedName, self::COMMANDS, true);
    }

    public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null)
    {
        if (!in_array($requestedName, self::COMMANDS, true)) {
            throw new InvalidArgumentException(sprintf(
                'Command %s is not explicitly allowed by %s',
                $requestedName,
                self::class
            ));
        }

        return new $requestedName($this->createEntityManagerProvider($container));
    }

    private function createEntityManagerProvider(ContainerInterface $container): EntityManagerProvider
    {
        return new class($container) implements EntityManagerProvider {
            private ContainerInterface $container;

            public function __construct(ContainerInterface $container)
            {
                $this->container = $container;
            }

            public function getDefaultManager(): EntityManagerInterface
            {
                return $this->container->get(EntityManagerInterface::class);
            }

            public function getManager(string $name): EntityManagerInterface
            {
                Assert::startsWith($name, 'doctrine.entitymanager.');
                $entityManager = $this->container->get($name);
                assert($entityManager instanceof EntityManagerInterface);

                return $entityManager;
            }
        };
    }
}
Ocramius commented 2 years ago

@boesing I'be removed this from the 1.2.0 milestone, as we first want 8.1 support to be pushed out

boesing commented 2 years ago

Closing here as it is possible to do this manually and this would be only convenience to users who wants to use non-laminas-specific symfony commands (e.g. for doctrine, etc.)