Поле Doctrine 2 ORM DateTime в идентификаторе
В наших таблицах БД столбцы refID
и date
являются составным первичным ключом, причем одно поле идентификатора отображается как 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, этот код вернет исключение "не может преобразовать datetime в строку"...
$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
как простой столбец, код работает нормально. Зачем?
Как мы можем справиться с этим? Мы должны иметь как date
, так и refID
в первичном ключе.
ДОБАВЛЕНО:
Итак, я создал новый класс
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;
}
}
Ответы
Ответ 1
Doctrine 2 ORM необходимо преобразовать поля идентификатора в строки в UnitOfWork
. Это необходимо для того, чтобы EntityManager
мог отслеживать изменения ваших объектов.
Так как объекты типа DateTime
не реализуют метод __toString
, преобразование их в строки не так просто, как отведение их к строкам.
Поэтому date
, datetime
и time
по умолчанию не поддерживаются как часть идентификатора.
Чтобы справиться с этим, вы должны определить свой собственный пользовательский тип поля mydatetime
сопоставленный с вашим собственным классом MyDateTime
который реализует __toString
. Таким образом, 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;
}
return new MyDateTime('@' . $dateTime->format('U'));
}
public function getName()
{
return 'mydatetime';
}
}
Затем вы регистрируете его со своей конфигурацией ORM во время загрузки (в зависимости от используемой структуры). В symfony он документирован в документации по доктрине symfony.
После этого вы можете использовать его в своих объектах:
class corpWalletJournal
{
// ...
/**
* @ORM\Column(name="date", type="mydatetime", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
*/
private $date;
Ответ 2
Будьте осторожны с
return new DateTimeEx('@' . $dateTime->format('U'));
Часовой пояс не будет хорошим. Вы должны сделать:
$val = new DateTimeEx('@' . $dateTime->format('U'));
$val->setTimezone($dateTime->getTimezone());
return $val;