将类继承与容器感知抽象基类一起使用

时间:2017-01-30 10:27:30

标签: php yaml symfony

在Symfony 3.x中,我们使用API​​层将Doctrine实体作为JSON响应(使用JMSSerializer和FOSRestBundle)提供 - 将它想象成某种Symfony序列化组件的自定义ObjectNormalizers。

其中一些API类需要容器识别。目前我们使用全局abstract class ApiWrapper implements ContainerAwareInterface { use ContainerAwareTrait; protected $container; public function setContainer(ContainerInterface $container = null) { $this->container = $container } } ,这是出于很多原因的不良做法。

这是我到目前为止所尝试的(非常简化):

抽象类

protected class BaseApi extends ApiWrapper
{
    //...some stuff
}

BaseClass的

protected class MyApi1 extends BaseApi
{
    protected $entity;

    public function __construct(SomeEntityClass $entity) {
        $this->entity = $entity;

    }
}

RealApiClass1

protected class MyApi2 extends BaseApi
{
    protected $entity;

    public function __construct(AnotherEntityClass $entity) {
        $this->entity = $entity;
    }
}

RealApiClass2

services:
    bundle.api_wrapper: 
        class: ApiWrapper
        abstract: true
        shared: false // to make sure to get a new instance everytime it's called
        calls:
            - [ setContainer, [ @service_container ] ]
    bundle.base_api:
        class: BaseApi
        parent: api_wrapper
    bundle.my_api:
        class: MyApi
        parent: base_api

services.yml

$myApi = new MyApi1($myEntity);

控制器

$myApi

我可以看到null有一个属性容器,但它提供了@media (max-width:600px) { .red, .blue { width: 100%; } } 。 在这种情况下,有没有办法共享容器?

2 个答案:

答案 0 :(得分:3)

无论您如何配置服务,将实体映射到其api类都会有点痛苦。这样做的一种方法是使用工厂来实际创建api类。这样可以更容易配置。

此代码尚未经过测试,因此可能会出现一些语法错误。

class ApiFactory
{
    $container;
    public function __construct($container) {
        $this->container = $container;
    }
    public function create($entity) {
        $api = null;
        switch(get_class($entity)) { // Map entity to api class
            case MyEntity1::class :
                $api = new MyApi1($entity);
                break;
            case MyEntity2::class :
                $api = new MyApi2($entity);
                break;
            default:
                throw new Exception('Oops');
        }
        // Steal a trick from the controller resolver
        if ($api instanceof ContainerAwareInterface) {
            $api->setContainer($this->container);
        }
        return $api;
    }
}

请注意,您的抽象ApiWrapper类并不是真正需要的。如果特定的api需要容器,那么只需让它实现容器感知接口并添加特征。

// Usage in a controller
$apiFactory = $this->get('my_api_factory');
$api = $apiFactory->create($myEntity);

// services.yml
services:
    my_api_factory:
        class: ApiFactory
        arguments: ['@service_container']

您使用了许多变体。我认为最好为每个api定义一个服务,并注入它需要的确切依赖项而不是容器。您可以使用api的类名称命名每个api服务。然后,您的api工厂将使用实体类名称从容器中提取所需的完全配置的api以生成密钥。

答案 1 :(得分:1)

在控制器中,您必须通过容器检索API,例如:

控制器

$myApi = $this->get('bundle.my_api');