2016-08-05 62 views
1

我想实现一个新的学说数据类型(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设置,例:α-2, Alpha3,IsoNumber)。

我的问题是,doctrine只允许通过classname进行数据类型映射,所以我不能实现一个工厂来加载所有需要的依赖关系。

我希望这是可以理解的。

问候

+0

您是使用Doctrine 2 ORM还是仅使用DBAL? – edigu

+0

嗨,此刻我使用了主义2 ORM,但是在将来我还会支持主义2 ODM – user3549136

回答

1

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

<?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

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

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

你的国家类的简单版本看起来是这样的:

<?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; 

阅读更多关于如何映射自定义DBAL类型Doctrine2 documentation chapter Custom Mapping Types

+0

优秀的回答,thx – user3549136