Doctrine 数据类型(使用工厂)

Doctrine Data Type (use Factory)

我想实现一个新的学说数据类型 (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;
     }
?>

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

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

我希望这是可以理解的。

问候

首先,您需要为扩展 Doctrine\DBAL\Types\Type class:

的国家/地区注册您的自定义 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四个方法。

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

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

您的国家 class 的简单版本可能如下所示:

<?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 文件中的 bootstrap 上设置您的服务:

/**
 * @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 类型的更多信息