【问题标题】:Doctrine Data Type (use Factory)Doctrine 数据类型(使用工厂)
【发布时间】:2016-12-12 09:54:02
【问题描述】:

我想实现一个新的学说数据类型 (http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html)。

我已经实现了一个国家服务,它通过适配器从任何库加载国家数据。知道我有以下实现:

<?php
     interface CountryInterface;

     interface Address
     {
         public function setCountry(CountryInterface $country);
         public function getCountry() : CountryInterface;
     }
?>

所以,我想做的是 - 制作一个 CountryType,它将 Country 对象转换为特定的字符串值(使用的字段将通过 OptionClass 设置,例如:Alpha2、Alpha3、IsoNumber)。

我的问题是,学说只允许通过类名映射数据类型,所以我无法实现工厂来加载所有需要的依赖项。

我希望这是可以理解的。

问候

【问题讨论】:

  • 您使用的是 Doctrine 2 ORM 还是 DBAL?
  • 嗨,目前我使用的是学说 2 ORM,但将来我也会支持学说 2 ODM

标签: php doctrine-orm zend-framework2


【解决方案1】:

首先,您需要为扩展 Doctrine\DBAL\Types\Type 类的国家/地区注册自定义 DBAL 类型:

<?php

namespace Application\DBAL\Types;

use Application\Resource\Country;
use Application\Service\CountryService;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Type;
use InvalidArgumentException;

class CountryType extends Type
{
    const NAME = 'country';

    /**
     * Country service
     */
    protected $countryService;

    /**
     * @return string
     */
    public function getName()
    {
        return self::NAME;
    }

    /**
     * {@inheritdoc}
     */
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return $platform->getDoctrineTypeMapping('text');
    }

    /**
     * {@inheritdoc}
     */
    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if($value === null){
            return null;
        }

        if ($value instanceof Country) {
            return (string) $value;
        }

        throw ConversionException::conversionFailed($value, self::NAME);
    }

    /**
     * {@inheritdoc}
     */
    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        if($value === null){
            return null;
        }

        $country = $this->countryService->getCountry($value);
        if( ! $country instanceof Country ){
            throw ConversionException::conversionFailed($value, self::NAME);
        }

        return $country;
    }

    /**
     * Set country service
     *
     * @var CountryService $service
     */
    public function setCountryService ($service){
        $this->countryService = $service;
    }
}

该类型需要实现getNamegetSQLDeclarationconvertToDatabaseValueconvertToPHPValue四个方法。

  • 第一个返回类型的名称
  • 第二个是用于(SQL)数据库中的类型声明(我在示例中使用了text,但您也可以使用整数或任何其他有效的原则数据库类型)。
  • 第三种方法将您的国家/地区对象转换为数据库值(在本例中为文本值)。
  • 最后一种方法正好相反;它从数据库中转换文本值。在这种情况下,我只是实例化 Country 类并将数据库值传递给构造函数。您需要在类构造函数中添加自定义逻辑。

在我的示例中,我假设 null 值也是允许的。

您的 Country 类的简单版本可能如下所示:

<?php

namespace Application\Resource;

class Country{

    protected $value;

    /**
     * Magic stringify to cast country object to a string
     */
    public function __toString(){
        return $value;
    }

    /**
     * Constructor method
     */
    public function construct($value){
        $this->value = $value
        // set other properties...
    }

    // setters and getters...
}

值是否应该是 alpha2/alpha3/country_name 或您希望在数据库中显示的任何值都取决于您。您还应该以某种方式使用构造函数方法中的其他属性填充另一个国家/地区。我把这部分留给你。

现在您需要注册您的自定义国家类型,以便学说使用它:

'doctrine' => array(
    //...
    'configuration' => array(
        'orm_default' => array(
            Application\DBAL\Types\CountryType::NAME =>  Application\DBAL\Types\CountryType::class,
        )
    )
)

您可以在您的应用程序Module.php 文件中将您的服务设置为引导程序:

/**
 * @param EventInterface|MvcEvent $event
 * @return void
 */
public function onBootstrap(EventInterface $event)
{
    $application    = $event->getApplication();
    $eventManager   = $application->getEventManager();

    $eventManager->attach(MvcEvent::EVENT_BOOTSTRAP, array($this, 'initializeCountryType');
}

/**
 * @param MvcEvent $event
 */
public function initializeCountryType(MvcEvent $event)
{
    $application    = $event->getApplication();
    $serviceManager = $application->getServiceManager();

    //get your country service from service manager
    $countryService = $serviceManager->getCountryService();

    $countryType = \Doctrine\DBAL\Types\Type::getType('country');
    $countryType->setCountryService($countryService);
}

现在您可以在任何实体定义中使用您的国家/地区类型,如下所示:

/**
 * @var string
 * @ORM\Column(type="country", nullable=true)
 */
protected $country;

详细了解如何在Doctrine2 documentation chapter Custom Mapping Types 中映射自定义 DBAL 类型

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多