Doctrine používá classname místo value
- zoid
- Člen | 12
Zdravím,
Mám problém s doctrine. Udělal jsem si vlastní typ pro UUID (ukládám ho jako Binary(16) místo stringu – kvůli výkonu a velikosti indexů). V PHP mám pro to speciální třídu (HexString) která kontroluje, zda se jedná o validní hex. dec. string. To používám jako primární klíč pro entity.
Pokud vytáhnu entitu přes $em->findBy(), vytáhne mi to správně entitu a všechny její vazby, pokud ale použiju queryBuilder, vytáhne mi to správně entitu, ale vazby jsou null – generuje to totiž tento dotaz:
SELECT t0.id AS id_1, t0.payment_type AS payment_type_2, t0.variable AS
variable_3, t0.paid AS
paid_4, t0.price AS price_5, t0.price_include_vat AS
price_include_vat_6, t0.order_id AS order_id_7
FROM order_payments t0
WHERE t0.order_id = Peblo\Shared\Types\HexString
Nevím proč, ale místo, aby vygeneroval query where order_id = (id entity),
použije to název typu, ve kterém je ID uchováno.
Typ registruju do doctrine v bootstrapu. Níže přikládám kód mé třídy a
typu v doctrine:
final class HexString
{
/** @var string */
private $value;
/**
* HexString constructor.
* @param string $value
*/
private function __construct(string $value)
{
if (!static::isValid($value)) {
throw new InvalidParameterException('Invalid hex value');
}
$this->value = $value;
}
/**
* @return string
*/
public function __toString() : string
{
return $this->value;
}
/**
* @param string $value
* @return HexString
*/
public static function from(?string $value): ?self
{
if($value === null) return null;
return new self($value);
}
/**
* @return string
*/
public function getValue(): string
{
return $this->value;
}
/**
* @param string $value
* @return bool
*/
private static function isValid(string $value): bool
{
if (Strings::length($value) !== 32) {
return false;
}
return ctype_xdigit($value);
}
}
final class BinaryUuidType extends Type
{
const TypeName = 'uuid';
/**
* @param array $fieldDeclaration
* @param AbstractPlatform $platform
* @return string
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) : string
{
$length = isset($fieldDeclaration['length']) ? $fieldDeclaration['length'] : 16;
return "BINARY($length)";
}
/**
* @param mixed $value
* @param AbstractPlatform $platform
* @return null|HexString
*/
public function convertToPHPValue($value, AbstractPlatform $platform) :? HexString
{
return $value === null ? null : HexString::from(self::hexToString($value));
}
/**
* @param HexString $value
* @param AbstractPlatform $platform
* @return null|string
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform) :? string
{
return $value === null ? null : pack('H*', $value->getValue());
}
/**
* @return string
*/
public function getName() : string
{
return self::TypeName;
}
/**
* @param $hex
* @return string
*/
public static function hexToString($hex) : string
{
return unpack("H*", $hex)[1];
}
}
Děkuji za jakékoliv rady :)
Editoval zoid (6. 1. 2018 17:59)
- zoid
- Člen | 12
akadlec napsal(a):
broblém je právě s tím custom typem a binary. To samé dělá doctrine pokud použiješ uuid od ramseyho. Je potřeba trochu přiohnout doctrine aby to id převáděl na binary hodnotu.
Ahoj, mohl bych vědet, jak doctrine přiohnout? Popř. pokud s tímto máš zkušenosti, mohl bys mě navést na nějaký workaround? Vážně by mi to pomohlo.
Díky :)
- akadlec
- Člen | 1326
Já to vyřešil tak že tam kde dělám selecty jednoho záznamu podle ID, dělám select pomocí query builderu
public function byId(Uuid\UuidInterface $id)
{
$this->filter[] = function (Doctrine\QueryBuilder $qb) use ($id) {
$qb->andWhere('ch.id = :id')->setParameter('id', $id->getBytes());
};
}
a tam rovnou setuju jako binární hodnota.
No pak je ještě problém v kolekcích když potřebuješ dělat delete.
v BasicEntityPersister
public function delete($entity)
{
$class = $this->class;
$identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($identifier as $key=>$value) {
if ($value instanceof Uuid) {
$identifier[$key] = $value->getBytes();
} else {
$identifier[$key] = $value;
}
}
a v JoinedSubclassPersister
public function delete($entity)
{
$identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($identifier as $key=>$value) {
if ($value instanceof Uuid) {
$identifier[$key] = $value->getBytes();
} else {
$identifier[$key] = $value;
}
}
jestli je to to nej řešení nevím, můj problém to vyřešilo. Zkoušel sem hledat jestli to někoho s ramseymym taky nepáli ale asi ne.
Editoval akadlec (8. 1. 2018 12:42)
- zoid
- Člen | 12
Jan Endel:
Typ registruji takto:
Doctrine\DBAL\Types\Type::addType(BinaryUuidType::TypeName, BinaryUuidType::class);
Zkoušel jsem si i vytáhnout connection a zaregistrovat do databasePlatform,
ale bohužel se stejným efektem.
K tobě Adame:
Nemám problém při tahání entit podle ID – to je má chyba, že jsem
zjednodušil příklad. U tahání podle ID mi vše funguje jak má, problém
je, pokud vytáhnu entity přes queryBuilder – např.
$qb->select('o')->from(Order::class, 'o');
Vytáhne mi to
všechny objednávky (správně), ale jejich relace (vazby, jejiž klíče nemá
entita order v sobě – jsou řešeny joinColumnem v druhé entitě, pokud
je joinColumn přímo v order, je vše ok) to neumí načíst, vytváří to ty
divné query viz první post.
Editoval zoid (8. 1. 2018 15:22)
- zoid
- Člen | 12
Tak nakonec jsem to vyřešil – nevím sice kde byl problém :D ale jelikož ukládám uuid jako binary, zkusil jsem ten svůj doctrine UuidType podědit přímo od BinaryType a teď už to vypadá, že vše funguje.
Podezření mám na tuhle metodu:
/**
* {@inheritdoc}
*/
public function getBindingType()
{
return \PDO::PARAM_LOB;
}