【问题标题】:Doctrine 2 ORM DateTime field in identifier标识符中的 Doctrine 2 ORM 日期时间字段
【发布时间】:2013-02-26 02:57:26
【问题描述】:

在我们的数据库表中,refIDdate 列是复合主键,标识符的一个字段被映射为 datetime

class corpWalletJournal
{
    /**
     * @ORM\Column(name="refID", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $refID;

    /**
     * @ORM\Column(name="date", type="datetime", nullable=false)     
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $date;

    public function setRefID($refID)
    {
        $this->refID = $refID;
    }

    public function setDate(\DateTime $date)
    {
        $this->date = $date;
    }
}

如果我们在实体中将它们描述为@ORM\Id,则此代码将返回异常“无法将日期时间转换为字符串”...

$filter = array(
    'date' => $this->stringToDate($loopData['date']), 
    'refID' => $loopData['refID']
));

$oCorpWJ = $this->em->getRepository('EveDataBundle:corpWalletJournal')->findOneBy($filter);
// ...
$oCorpWJ->setDate($this->stringToDate($loopData['date']));
// ...

如果我们将corpWalletJournal#date 描述为一个简单的列,则代码可以正常工作。为什么?

我们该如何处理呢?我们需要在主键中同时包含 daterefID

添加:

所以我创建了新课程

use \DateTime;

class DateTimeEx extends DateTime
{

public function __toString()
{
    return $this->format('Y-m-d h:i:s');
}

}

还有新的类型

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Eve\DataBundle\Entity\Type\DateTimeEx;

class DateTimeEx extends Type
{
    const DateTimeEx = 'datetime_ex';

    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return 'my_datetime_ex';
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return new DateTimeEx($value);
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value->format('Y-m-d h:i:s');
    }

    public function getName()
    {
        return self::DateTimeEx;
    }

    public function canRequireSQLConversion()
    {
        return true;
    }

}

如何在实体中使用它们?

我的(编辑的)类型类

    use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;

class DateTimeEx extends Type
{
    const DateTimeEx = 'datetime_ex';

    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return 'my_datetime_ex';
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return $value;
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value;
    }

    public function getName()
    {
        return self::DateTimeEx;
    }

}

【问题讨论】:

    标签: doctrine-orm


    【解决方案1】:

    Doctrine 2 ORM 需要将标识符字段转换为 UnitOfWork 中的字符串。这是让EntityManager 能够跟踪您的对象的更改所必需的。

    由于DateTime 类型的对象本身没有实现__toString 方法,因此将它们转换为字符串并不像将它们转换为字符串那么简单。

    因此,默认的datedatetimetime 类型为are not supported as part of the identifier

    要处理它,您应该定义自己的custom field type mydatetime 映射到您自己的实现__toStringMyDateTime 类。这样,如果标识符包含对象,ORM 也可以处理它们。

    以下是该类的外观示例:

    class MyDateTime extends \DateTime 
    {
        public function __toString()
        {
            return $this->format('U');
        }
    }
    

    下面是自定义 DBAL 类型的示例:

    use Doctrine\DBAL\Types\DateTimeType;
    use Doctrine\DBAL\Platforms\AbstractPlatform;
    
    class MyDateTimeType extends DateTimeType
    {
        public function convertToPHPValue($value, AbstractPlatform $platform)
        {
            $dateTime = parent::convertToPHPValue($value, $platform);
            
            if ( ! $dateTime) {
                return $dateTime;
            }
    
            $val = new MyDateTime('@' . $dateTime->format('U'));
            $val->setTimezone($dateTime->getTimezone());
            return $val;
        }
    
        public function requiresSQLCommentHint(AbstractPlatform $platform)
        {
            return true;
        }
    
        public function getName()
        {
            return 'mydatetime';
        }
    }
    

    然后您在引导期间将其注册到您的 ORM 配置中(取决于您使用的框架)。在 symfony 中,它记录在 symfony doctrine documentation

    之后,您可以在实体中使用它:

    class corpWalletJournal
    {
        // ...
    
        /**
         * @ORM\Column(name="date", type="mydatetime", nullable=false)     
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="NONE")
         */
        private $date;
    

    【讨论】:

    • 你能解释一下,我需要在哪里以及如何更改课程日期时间?以及如何在我的示例中使用它
    • 您只需使用自己的class MyDateTime extends \DateTime { public function __toString() { return $this->format('U'); } } 扩展DateTime 类。我更新了答案。其余的在链接的文档中(链接到自定义字段类型:docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
    • 我在顶帖中添加了代码\类,你能解释一下我该如何使用它们吗?
    • @user1954544 我添加了完整示例
    • 我注意到这样做的一些副作用:1) doctrine:schema:update 工具不断尝试更新自定义日期字段,因为 Doctrine 的 Comparator 看到 @数据库中的 987654343@ 和本地代码中的 MyDateTime。 2)您必须将用于查询该字段的任何值转换为与自定义类型相同的类型,例如$queryBuilder->setParameter('date', new MyDateTime('@'.$dateTime->format('U')))
    【解决方案2】:

    小心

    return new DateTimeEx('@' . $dateTime->format('U'));
    

    时区不好。你应该这样做:

    $val = new DateTimeEx('@' . $dateTime->format('U'));
    $val->setTimezone($dateTime->getTimezone());
    
    return $val;
    

    【讨论】:

      【解决方案3】:

      基于@Ocramius answer

      namespace App\Interface;
      
      interface StringableDateTimeInterface extends \DateTimeInterface, \Stringable
      {
      }
      
      namespace App\Type;
      
      use App\Interface\StringableDateTimeInterface;
      
      class DateTime extends \DateTime implements StringableDateTimeInterface
      {
          public function __toString(): string
          {
              return $this->format('U');
          }
      }
      
      
      namespace App\DBAL;
      
      use App\Type\DateTime;
      use Doctrine\DBAL\Types\DateTimeType as DoctrineDateTimeType;
      use Doctrine\DBAL\Platforms\AbstractPlatform;
      
      class DateTimeType extends DoctrineDateTimeType
      {
          public function convertToPHPValue($value, AbstractPlatform $platform): mixed
          {
              $dateTime = parent::convertToPHPValue($value, $platform);
      
              if (!$dateTime) {
                  return $dateTime;
              }
      
              $val = new DateTime('@' . $dateTime->format('U'));
              $val->setTimezone($dateTime->getTimezone());
      
              return $val;
          }
      }
      
      namespace App\DBAL;
      
      use App\Type\DateTime;
      use Doctrine\DBAL\Types\DateType as DoctrineDateType;
      use Doctrine\DBAL\Platforms\AbstractPlatform;
      
      class DateType extends DoctrineDateType
      {
          public function convertToPHPValue($value, AbstractPlatform $platform): mixed
          {
              $dateTime = parent::convertToPHPValue($value, $platform);
      
              if (!$dateTime) {
                  return $dateTime;
              }
      
              $val = new DateTime('@' . $dateTime->format('U'));
              $val->setTimezone($dateTime->getTimezone());
      
              return $val;
          }
      }
      

      然后添加config/packages/doctrine.yaml

      doctrine:
          dbal:
              types:
                  datetime: App\DBAL\DateTimeType
                  date: App\DBAL\DateType
      

      最后在您的实体中使用如下:

          #[ORM\Id]
          #[ORM\Column(type: 'date')]
          private ?StringableDateTimeInterface $date;
      
          public function getDate(): ?StringableDateTimeInterface
          {
              return $this->date;
          }
      
          public function setDate(StringableDateTimeInterface $date): self
          {
              $this->date = $date;
      
              return $this;
          }
      

      在设置实例的值时不要忘记创建新的App\Type\DateTime 而不是DateTime

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-14
        • 2015-12-19
        • 2019-11-20
        • 1970-01-01
        • 1970-01-01
        • 2021-04-08
        • 2018-01-29
        • 2013-02-07
        相关资源
        最近更新 更多