当使用手册'选择'时,Doctrine ORM产生不同的输出。

时间:2016-11-23 15:48:01

标签: doctrine-orm symfony silex

我是Doctrine ORM(v2.5.5)和Silex(v2.0.4)/ Symfony(v3.1.6)的初学者。我需要将日期字段输出为 YYYY-MM-DD 格式。让我们说我在我的实体上有这个注释和getter方法:

// src/App/Entity/Tnkb.php (simplified)
// 'expire' field
/**
* @ORM\Column(type="date")
*/
protected $expire;

// getter
public function getExpire()
{
    return !is_object($this->expire) ? new \DateTime() : $this->expire->format('Y-m-d');
}

这是我的简化控制器,用于调试目的:

$app->get('/debug', function() use ($app) {
    $tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
    $normalizer = new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer();
    $encoder = new \Symfony\Component\Serializer\Encoder\JsonEncoder();
    $normalizer->setCircularReferenceHandler(function($obj){
        return $obj->getId();
    });
    $serializer = new \Symfony\Component\Serializer\Serializer(array($normalizer), array($encoder));

    $qb = $tnkbRepo->createQueryBuilder('c')
    ->setMaxResults(1);
    //$query = $qb->getQuery();    // [1] <<-- this line produce proper YYYY-MM-DD format
    //$query = $qb->select('c.expire')->getQuery();   // [2] <<-- this (manual select) line produce DateTime object.
    $results = $query->getResult();
    return $serializer->serialize($results, 'json');
});

在第一个 [1] 行取消注释后,我得到了我想要的正确输出:

[more json output here]...,"expire":"1970-10-25",...

但是第二个 [2] 行没有注释(我打算省略其他测试字段),我得到了以下输出,这不是我的预期:

[{"expire":{"timezone":{"name":"UTC","location":{"country_code":"??","latitude":0,"longitude":0,"comments":""}},"offset":0,"timestamp":25660800}}]

我也注意到,使用 [2] 行,Doctrine似乎忽略了我的实体的getter方法(我尝试返回空字符串)。我希望输出与 [1] 情况相同,这让我很好奇。我的问题是:

  1. 如何使用 [2] 版本实现相同的 YYYY-MM-DD 格式?
  2. 为什么它们会产生不同的输出格式?
  3. 谢谢。

    更新

    更简化的 / debug 控制器用于测试(无序列化):

    $app->get('/debug', function() use ($app) {
        $tnkbRepo = $app['orm.em']->getRepository('\App\Entity\Tnkb');
        $qb = $tnkbRepo->createQueryBuilder('c');
    
        // [1a] normal query. doesn't return Entity, getExpire() isn't called.
        /*$query = $qb->select('c.expire')
            ->setMaxResults(1)->getQuery();*/
    
        // [2a] partial query. returns Entity, getExpire() called.
        /*$query = $qb->select('partial c.{id,expire}')
            ->setMaxResults(1)->getQuery();*/
    
        $results = $query->getResult();
        var_dump($results);die;
    });
    

    更新了实体方法getExpire()

    // src/App/Entity/Tnkb.php (simplified)
    // 'expire' field
    /**
    * @ORM\Column(type="date")
    */
    protected $expire;
    
    protected $dateAsString = true;
    protected $dateFormat = 'Y-m-d';
    
    // getter
    public function getExpire()
    {
        return ($this->expire instanceof \DateTime) ? $this->dateOutput($this->expire) 
            : $this->dateOutput(new \DateTime());
    }
    
    protected function dateOutput(\DateTime $date) {
        if ($this->dateAsString) {
            return $date->format($this->dateFormat);
        }
        return $date;
    }
    

    控制器转储结果:

    [1a]正常查询

    // non-entity
    array(1) { [0]=> array(1) { ["expire"]=> object(DateTime)#354 (3) { ["date"]=> string(26) "1970-10-25 00:00:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } } }
    

    [2a]部分对象查询

    // array of entity
    array(1) { [0]=> object(App\Entity\Tnkb)#353 (23) { /* more properties */...["expire":protected]=> object(DateTime).../* more properties */
    

    我发现这是Doctrine的正常行为,它与部分对象有关。请参阅下面的评论。链接:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/partial-objects.html

1 个答案:

答案 0 :(得分:0)

我不认为有时候返回一个\ DateTime,但有时会返回一个格式化的字符串。不过,也许你有理由这样做。

我能想到结果差异的唯一原因是Doctrine在加载实体时是否在属性上调用getter。我测试了一个简单的类,它具有相同的expire属性和getter。返回类仍然有序列化(未格式化)\ DateTime对象,这表示在某些时候你的getter被调用,属性设置为新的\ DateTime。

我的建议是查看Symfony在3.1中提供的the DateTimeNormalizer。如果你无法升级到3.1,那么你可以轻松地建立自己的。然后,您可以确保在所有回复中始终具有一致的\ DateTime格式。您可以从getter中删除->format(...),然后始终返回\ DateTime对象。我认为这是一种更清洁的方法。