xsolve-pl/model-factory

此库提供了一个通用的骨架来组织模型工厂。

v1.0.3 2021-09-29 11:03 UTC

This package is not auto-updated.

Last update: 2024-09-26 01:45:43 UTC


README

Build Status Scrutinizer Code Quality Latest Stable Version Total Downloads Monthly Downloads License

目录

简介

此库提供了一个通用的骨架来组织模型工厂。

它可以用来提供对象,这些对象稍后将被传递给某些序列化器,并通过API返回,或者在某些对象被转交给其他库或包之前,提供对象的一些适配器或外观。

它旨在赋予模型能力,使它们可以轻松访问一些服务或在不要求太多前期工作的前提下,延迟创建嵌套模型。

如果您想在Symfony应用程序中使用此库,您可能对使用在xsolve-pl/model-factory-bundle可用的专用bundle感兴趣。

许可证

此库采用MIT许可证。请参阅LICENSE文件中的完整许可证。

入门指南

使用以下命令将此库包含到您的项目中(假设已全局安装Composer):

$ composer require xsolve-pl/model-factory

有关Composer的更多信息,请参阅其简介

使用示例

实现模型工厂

此库定义了一个简单的接口,用于模型工厂,该接口提供了有关它是否支持给定对象(即能否为给定对象生成适当的模型对象)的信息,并实例化这样的模型对象。它还包括方便的方法,允许一次操作多个对象。有关更多信息,请参阅Xsolve\ModelFactory\ModelFactory\ModelFactoryInterface

您可以自由实现此接口,以使用自己的模型工厂类,同时Xsolve\ModelFactory\ModelFactory\ModelFactory中还包括一个用于模型工厂的基本抽象类。它包含所有必要的逻辑,只留出需要实现的公共supportsObject方法和受保护的instantiateModel方法。使用它作为基类创建新的模型工厂类变得非常简单。

<?php

namespace Example;

use Xsolve\ModelFactory\ModelFactory\ModelFactory;

class FooModelFactory extends ModelFactory
{
    /**
     * {@inheritdoc}
     */
    public function supportsObject($object)
    {
        return ($object instanceof Foo);
    }

    /**
     * {@inheritdoc}
     */
    protected function instantiateModel($object)
    {
        /* @var Foo $object */
        return new FooModel($object);
    }
}

在模型中使用外部依赖

在某些情况下,模型对象需要一些外部依赖来返回某些值。一个简单的例子是有一个表示包装的模型对象,需要计算体积重量(这是通过将体积乘以每个运输公司特有的系数得到的)。通常,计算此类值的辅助类会被定义为DI容器中的服务,系数通过某些配置文件或从某些数据存储中获取。

通过使用Xsolve\ModelFactory\ModelFactory\ModelFactoryAwareModelInterface,此库可以非常容易地在模型对象中访问此类服务。如果Xsolve\ModelFactory\ModelFactory\ModelFactory被用作模型工厂类的基类,则实现上述接口的每个模型都将注入用于生成它的模型工厂。由于模型工厂本身也可以定义为服务,因此可以将任何服务从DI容器中注入,并公开代理方法,以便模型对象可以访问它们。

以下示例展示了此接口的示例用法。首先,我们定义模型工厂类:

<?php

namespace Example;

use Xsolve\ModelFactory\ModelFactory\ModelFactory;

class BazModelFactory extends ModelFactory
{
    /**
     * @var VolumetricWeightCalculator
     */
    protected $volumetricWeightCalculator;

    /**
     * @param VolumetricWeightCalculator $volumetricWeightCalculator
     */
    public function __construct(VolumetricWeightCalculator $volumetricWeightCalculator)
    {
        $this->volumetricWeightCalculator = $volumetricWeightCalculator;
    }

    /**
     * @return VolumetricWeightCalculator
     */
    public function getVolumetricWeightCalculator()
    {
        return $this->volumetricWeightCalculator;
    }

    /**
     * {@inheritdoc}
     */
    public function supportsObject($object)
    {
        return ($object instanceof Baz);
    }

    /**
     * {@inheritdoc}
     */
    protected function instantiateModel($object)
    {
        /* @var Baz $object */
        return new BazModel($object);
    }
}

我们的模型类如下所示(注意这里使用了Xsolve\ModelFactory\ModelFactory\ModelFactoryAwareModelTrait来提供方便的setModelFactorygetModelFactory方法)

<?php

namespace Example;

use Xsolve\ModelFactory\ModelFactory\ModelFactoryAwareModelInterface;
use Xsolve\ModelFactory\ModelFactory\ModelFactoryAwareModelTrait;

class BazModel implements ModelFactoryAwareModelInterface
{
    use ModelFactoryAwareModelTrait;

    /**
     * @var Baz
     */
    protected $baz;

    /**
     * @param Baz $baz
     */
    public function __construct(Baz $baz)
    {
        $this->baz = $baz;
    }

    /**
     * @return float
     */
    public function getVolume()
    {
        return ($this->baz->getLength() * $this->baz->getWidth() * $this->baz->getHeight());
    }

    /**
     * @return float
     */
    public function getVolumetricWeight()
    {
        return $this
            ->getModelFactory()
            ->getVolumetricWeightCalculator()
            ->calculate($this->getVolume());
    }
}

将模型工厂分组到集合中

为了便于为多个对象生成模型,可以将模型工厂分组。如果您的应用程序提供了多个API(或多个差异很大的API版本,它们使用完全不同的模型),您可以将工厂分组到单独的集合中,以避免生成不正确模型的风险。

模型工厂集合的基本实现提供在Xsolve\ModelFactory\ModelFactoryCollection\ModelFactoryCollection类中。它允许通过其'addModelFactory'方法注册多个模型工厂,并提供了与单个模型工厂相同的接口,因此可以完全互换。它的方法尝试为每个提供的对象找到合适的模型工厂。

<?php

namespace Example;

use Xsolve\ModelFactory\ModelFactoryCollection\ModelFactoryCollection;

$fooModelFactory = new FooModelFactory();
$bazModelFactory = new BazModelFactory();

$modelFactoryCollection = new ModelFactoryCollection();
$modelFactoryCollection
    ->addModelFactory($fooModelFactory)
    ->addModelFactory($bazModelFactory);

$foo1 = $storage->getFoo(1);
$baz1 = $storage->getBaz(1);

$foo1Model = $modelFactoryCollection->createModel($foo1);
$baz1Model = $modelFactoryCollection->createModel($baz1);

此代码段定义了一个模型工厂集合$modelFactoryCollection和两个模型工厂$fooModelFactory$bazModelFactory,它们被添加到集合中。

之后,可以在集合上调用createModel方法(以及其他模型工厂特有的方法),只要有一个且仅有一个模型工厂支持给定的对象,就会创建模型。

创建嵌套模型

在某些情况下,您想要生成的模型可能包含其他模型(例如,为与根对象关联的对象生成的模型)。如果这种嵌套很深(如一些针对SPA应用程序优化的API,旨在减少获取数据所需请求的数量),那么预先构建所有模型并以适当的方式连接它们会变得繁琐。一个更简单的解决方案是使模型能够通过用于实例化自己的相同模型工厂集合来生成嵌套模型。

为了实现这一点,您的模型可以实现Xsolve\ModelFactory\ModelFactoryCollection\ModelFactoryCollectionAwareModelInterface,这将导致用于创建模型的模型工厂集合被注入到其中。此接口的基本实现提供在Xsolve\ModelFactory\ModelFactoryCollection\ModelFactoryCollectionAwareModelTrait中。

假设之前展示的Example\Foo类对象包含一个包含Example\Baz类对象数组的属性,并且我们希望这种关联也应用于模型对象。如果Example\FooModelFactoryExample\BazModelFactory都是同一模型工厂集合的一部分,并且通过该集合的createModelcreateModels方法实例化Example\FooModel类,则Example\FooModel类的实现可能如下所示

<?php

namespace Example;

use Xsolve\ModelFactory\ModelFactoryCollection\ModelFactoryCollectionAwareModelInterface;
use Xsolve\ModelFactory\ModelFactoryCollection\ModelFactoryCollectionAwareModelTrait;

class FooModel implements ModelFactoryCollectionAwareModelInterface
{
    use ModelFactoryCollectionAwareModelTrait;

    /**
     * @var Foo
     */
    protected $foo;

    /**
     * @param Foo $foo
     */
    public function __construct(Foo $foo)
    {
        $this->foo = $foo;
    }

    /**
     * @return BazModel[]
     */
    public function getBazs()
    {
        return $this
            ->getModelFactoryCollection()
            ->createModels($this->foo->getBazs());
    }
}

当然,如果Example\BazModel实现了相同的接口,它也会被注入相同的模型工厂集合,并能够为嵌套对象生成模型——就这么简单!