symfony 如何使用twig扩展Node编译器将类传递给twig自定义标记

8fsztsew  于 2023-06-30  发布在  其他
关注(0)|答案(1)|浏览(110)

使用注入的实体存储库创建工厂

namespace App\DependencyInjection\Compiler;

use App\Factory\EntityFactoryRegistry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class EntityFactoryPass implements CompilerPassInterface
{

    public function process(ContainerBuilder $container): void
    {
        if (!$container->has(EntityFactoryRegistry::class)) {
            return;
        }

        $definition = $container->findDefinition(EntityFactoryRegistry::class);
        $taggedServices = $container->findTaggedServiceIds('app.entity_factory');
        $entityManager = $container->findDefinition('doctrine.orm.entity_manager');

        foreach ($taggedServices as $id => $tags) {
            $definition->addMethodCall('addFactory', [new Reference($id), $entityManager]);
        }
    }
} 

//using code like this to get factory for entity, now this part works, I have the factory accessible everywhere I want.
foreach ((new \ReflectionClass($this::class))->getAttributes(Access::class) as $attribute) {
    if (key_exists('entity', $attribute->getArguments())) {
        $this->factory = $this->entityFactoryRegistry->get($attribute->getArguments()['entity']);
    }
}

出厂后:

<?php

namespace App\Factory;

use App\Command\EntityCommandInterface;
use App\Command\PostCommand;
use App\Entity\Base\BaseEntity;
use App\Entity\Post;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(tags: ['app.entity_factory'])]
class PostFactory implements EntityFactoryInterface
{
    private ?EntityManagerInterface $entityManager = null;

    public function create(EntityCommandInterface|PostCommand $command): BaseEntity
    {

    }

    public function edit(BaseEntity|Post $entity, EntityCommandInterface|PostCommand $command): BaseEntity
    {

    }

    public function getMainPosts (): array
    {
        return $this->entityManager->getRepository($this->getEntity())
            ->findAll();
    }

    public function setEntityManager (EntityManagerInterface $entityManager): void
    {
        $this->entityManager = $entityManager;
    }

    public function getEntity(): string
    {
        return Post::class;
    }
}

现在,当涉及到树枝扩展,我想得到工厂同样的方式,并通过它自定义树枝节点。
扩展创建:

namespace App\Extension;

use App\Extension\Parser\FactoryTokenParser;
use App\Factory\EntityFactoryRegistry;
use Twig\Extension\AbstractExtension;

class FactoryExtension extends AbstractExtension
{
    public function __construct(
        protected readonly EntityFactoryRegistry $entityFactoryRegistry,
    )
    {
    }

    public function getTokenParsers(): array
    {
        return [
            new FactoryTokenParser($this->entityFactoryRegistry),
        ];
    }
}

令牌解析器:

<?php

namespace App\Extension\Parser;

use App\Extension\Node\FactoryTagNode;
use App\Factory\EntityFactoryRegistry;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;

class FactoryTokenParser extends AbstractTokenParser
{
    public function __construct(
        protected readonly EntityFactoryRegistry $entityFactoryRegistry,
    )
    {}

    public function parse(Token $token): FactoryTagNode
    {
        $stream = $this->parser->getStream();

        $entityClass = $this->parser->getExpressionParser()->parseExpression();

        if (!class_exists($entityClass->getAttribute("value"))) {
            throw new \Exception("Class provided to factory does not exist");
        }

        // Parse the content between {% factory %} and {% endfactory %}
        $stream->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this, 'decideFactoryEndTag'], true);
        $stream->expect(Token::BLOCK_END_TYPE);

        $entityFactory = $this->entityFactoryRegistry->get($entityClass->getAttribute("value"));

        return new FactoryTagNode(
            $entityFactory,
            $body,
            $token->getLine(),
            $this->getTag()
        );
    }

    public function decideFactoryEndTag(Token $token): bool
    {
        return $token->test('endfactory');
    }

    public function getTag(): string
    {
        return 'factory';
    }
}

节点:

<?php

namespace App\Extension\Node;

use App\Factory\EntityFactoryInterface;
use Twig\Compiler;
use Twig\Node\Node;

class FactoryTagNode extends Node
{
    private EntityFactoryInterface $factory;

    public function __construct(EntityFactoryInterface $entityFactory, Node $body, int $line, string $tag)
    {
        parent::__construct(['body' => $body], [], $line, $tag);
        $this->factory = $entityFactory;
    }

    public function compile(Compiler $compiler): void
    {
        $body = $this->getNode('body');

        $compiler
            ->addDebugInfo($this)
            ->write(sprintf('$context["factory"] = %s;', $this->getVarExport($this->factory)))
            ->write('ob_start();' . PHP_EOL)
            ->subcompile($body)
            ->write('echo strtoupper(ob_get_clean());' . PHP_EOL);
    }

    private function getVarExport($var): ?string
    {
        return var_export($var, true);
    }
}

现在是这部分,它不会工作。->write(sprintf('$context["factory"] = %s;', $this->getVarExport($this->factory)))错误:在编译模板期间引发了异常(“警告:var_export不处理循环引用”)。
当我像这样初始化类->write(sprintf('$context["factory"] = new %s();', get_class(this->factory)))
Twig有可用的类,但我想注入工厂类,而不是为Twig重新初始化类的新示例。
为什么,原因是,实体管理器被注入到工厂使用编译器通行证,当我重新初始化类,它不再可用。
还有其他我无法解决的问题吗?
细枝渲染零件:

{% factory 'App\\Entity\\Post' %}
    {{ dump(factory.mainPosts) }}
{% endfactory %}
hzbexzde

hzbexzde1#

好吧,我想明白了,基本上我是这么做的
FactoryExtension.php:

  • 添加了getter,从集合中获取工厂,不再传递给解析器。
<?php

namespace App\Extension;

use App\Extension\Parser\FactoryTokenParser;
use App\Factory\EntityFactoryRegistry;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class FactoryExtension extends AbstractExtension
{
    public function __construct(
        protected readonly EntityFactoryRegistry $entityFactoryRegistry
    )
    {
    }

    public function getTokenParsers(): array
    {
        return [
            new FactoryTokenParser(),
        ];
    }

    public function getEntityFactory(string $className): ?object
    {
        return $this->entityFactoryRegistry->get($className);
    }
}

Main thing:FactoryTagNode.php:

  • 由于Environment可以从Twig Compiler访问,我们实际上可以从它请求注册的扩展并访问它内部的方法。
  • 在我的例子中,$entityClass->getAttribute(“value”)= App\Entity\Post
<?php

namespace App\Extension\Node;

use Twig\Compiler;
use Twig\Node\Node;

class FactoryTagNode extends Node
{

    public function __construct(Node $entityClass, Node $body, int $line, string $tag)
    {
        parent::__construct(['entityClass' => $entityClass, 'body' => $body], [], $line, $tag);
    }

    public function compile(Compiler $compiler): void
    {
        $body = $this->getNode('body');
        $entityClass = $this->getNode('entityClass');

        $compiler
            ->addDebugInfo($this)
            ->write(sprintf('$context["factory"] = $this->env->getExtension(\'App\\Extension\\FactoryExtension\')->getEntityFactory(\'%s\');' . PHP_EOL, $entityClass->getAttribute("value")))
            ->write('ob_start();' . PHP_EOL)
            ->subcompile($body)
            ->write('echo strtoupper(ob_get_clean());' . PHP_EOL);
    }
}

和小枝:

{% factory 'App\\Entity\\Post' %}
    {{ dump(factory.mainPosts|length) }} {# prints out 2 in my case #}
{% endfactory %}

相关问题