roach-php / core

The complete web scraping toolkit for PHP.
https://roach-php.dev
1.36k stars 70 forks source link

League Container and Symfony container conflicts #21

Closed alejgr closed 2 years ago

alejgr commented 2 years ago

I am using in roach-php in a Symfony 6 project. I am trying to inject the EntityManagerInterface in my ItemProcessorInterface class to save the object in the DB. But doing that looks like it creates some kind of conflict between the containers:

Alias (Doctrine\ORM\EntityManagerInterface) is not being managed by the container or delegates
in (League) Container.php, 188

This is happening also if I inject dependencies in the Spider class. Any workaround for this? Maybe telling symfony to ignore these classes and using League Container instead? No idea how to do that since league container is instantiated in vendor/roach-php

alejgr commented 2 years ago

Ok I didn't read all the doc lol. Here is the answer

https://roach-php.dev/docs/dependency-injection/#swapping-out-the-container

We can close this :)

alejgr commented 2 years ago

Ok so now the problem is, its not working because Roach now uses Symfony container even for loading their own classes and they are not loaded by Symfony since they're in vendor directory,

You have requested a non-existent service "RoachPHP\Core\Engine".

I think we need a bundle for this then

ksassnowski commented 2 years ago

Yeah, that’s essentially what the framework integrations would have to do (that’s what the Laravel package does). Mostly providing a bunch of container bindings. I do want to provide a first-party Symfony package eventually, but I’m not super familiar with the Symfony ecosystem and I’ve never written a bundle before, so it’s been going a bit slow.

alejgr commented 2 years ago

I could make it work by creating a bundle, but now I found some issues... services must be public since Roach library are getting them directly via container ($container->get()). Even the services in the userland (MySpider in the examples)

So I need to create some tags:

# RoachPHP\Bundle\DependencyInjection
class RoachPHPExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        // bindings go here

        $container->registerForAutoconfiguration(SpiderInterface::class)
            ->addTag('roach.spider');

        $container->registerForAutoconfiguration(ItemProcessorInterface::class)
            ->addTag('roach.item_processor');

        // extensions middlewares etc
    }
}

And a compiler pass:

// RoachPHP\Bundle\DependencyInjection\Compiler
class RoachCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
         // does the trick and inject the container even if its a static method
         $container
            ->getDefinition(Roach::class)
            ->addMethodCall('useContainer', [new Reference('service_container')])
            ->setPublic(true);

        foreach ($container->findTaggedServiceIds('roach.spider') as $id => $tags) {
            $container->getDefinition($id)->setPublic(true);
        }

        foreach ($container->findTaggedServiceIds('roach.item_processor') as $id => $tags) {
            $container->getDefinition($id)->setPublic(true);
        }

        foreach ($container->findTaggedServiceIds('roach.extension') as $id => $tags) {
            $container->getDefinition($id)->setPublic(true);
        }
    }
}

Not sure if its a good practice to do that in a symfony bundle, probably not

This is my symfony console command:

class AppCommand extends Command
{
    public function __construct(private Roach $roach)
    {
        parent::__construct();
    }
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        Roach::startSpider(MySpider::class);
        return Command::SUCCESS;
    }
}

Or creating a Roach service in the bundle, as a wrapper for the actual Roach static class and do something like $roach->startSpider(MySpider::class)

Ne-Lexa commented 2 years ago

I wrote a bundle for Symfony. Now you can plug it in through the composer. https://github.com/Ne-Lexa/roach-php-bundle

ksassnowski commented 2 years ago

That looks really nice. Good job! I’ll add it to the docs shortly 👍

ksassnowski commented 2 years ago

I added a link to the repo to the docs. I assume the bundle fixes this issue so I'll close it for now.