[PR #1398] When using a custom data type, SchemaTool does not pass column length field mapping to relation join columns #9456

Closed
opened 2026-01-22 16:04:22 +01:00 by admin · 0 comments
Owner

Original Pull Request: https://github.com/doctrine/orm/pull/1398

State: closed
Merged: No


We have a custom data type for storing UUIDs in a BINARY column in MySQL:

class UuidType extends Type
{
    const UUID = 'uuid';


    /**
     * @inheritdoc
     */
    public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return $platform->getBinaryTypeDeclarationSQL($fieldDeclaration);
    }


    /**
     * @inheritdoc
     */
    public function getName()
    {
        return self::UUID;
    }


    /**
     * @inheritdoc
     */
    public function convertToPhpValue($value, AbstractPlatform $platform)
    {
        if ($value !== null) {
            return strtoupper(bin2hex($value));
        }
    }


    /**
     * @inheritdoc
     */
    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if ($value !== null) {
            // If the app put any dashes in, we strip them, just in case
            return hex2bin(str_replace('-', '',$value));
        }
    }


    /**
     * Generate a UUID that is optimized for MySQL's InnoDB engine
     * Based on UUID1, but transposed for more optimal inserts and sized for binary(16) column
     *
     * @return string MySQL-optimized UUID that works well with UUID column type
     */
    public static function generateUuid()
    {
        $uuid = Uuid::uuid1()->toString();

        $uuidFormattedForMySQL = self::transposeUuid($uuid);

        return $uuidFormattedForMySQL;
    }


    /**
     * Optimize format of UUID for storing as binary in MySQL
     *
     * @param $uuid
     *
     * @return string
     *
     * @see http://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
     */
    public static function transposeUuid($uuid)
    {
        /*
         * orig UUID1: 13341cb5-c1f8-11e4-91e7-080027880ca6
         * transpose:  11e4-c1f8-13341cb5-91e7-080027880ca6
         * format:     11E4C1F813341CB591E7080027880CA6  <-- this is what can be ideally stored as binary in MySQL
         */
        $uuidOptimizedOrderForMySQL = substr($uuid, 14, 4) . substr($uuid, 9, 4) . substr($uuid, 0, 8) . substr(
                $uuid,
                19,
                17
            );
        $uuidFormattedForMySQL = strtoupper(str_replace('-', '', $uuidOptimizedOrderForMySQL));

        return $uuidFormattedForMySQL;
    }


    public function requiresSQLCommentHint(AbstractPlatform $platform)
    {
        return true;
    }

...and the following related (example) entities:

/**
 * @Entity
 */
class Thing 
{
    /**
     * @Id
     * @var string UUID
     * @Column(type="uuid", length=16)
     * @GeneratedValue(strategy="CUSTOM")
     * @CustomIdGenerator(class="Fisdap\Doctrine\Extensions\IdGenerator\UuidGenerator")
     */
    protected $id;

   /**
     * @OneToOne(targetEntity="OtherThing", mappedBy="thing")
     */
    protected $otherThing;
}

/**
 * @Entity
 */
class OtherThing 
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue
     */
    protected $id;

    /**
     * @OneToOne(targetEntity="Thing", inversedBy="otherThing")
     */
    protected $thing;
}

In our case, since SchemaTool::gatherRelationJoinColumns() doesn't copy $fieldMapping['length'] to $columnOptions['length'] for non-string / custom data types, the resulting CREATE TABLE SQL for the OtherThing entity will have a column definition for its association with Thing as thing_id BINARY(0). Of course, MySQL/InnoDB will be unable to create the index for a column with zero length, and multiple exceptions are thrown.

I believe the fix here is to simply check whether $fieldMapping['length'] is set, regardless of the value of $fieldMapping['type']. For good measure, I did the same for $fieldMapping['scale'] and $fieldMapping['precision'].

**Original Pull Request:** https://github.com/doctrine/orm/pull/1398 **State:** closed **Merged:** No --- We have a custom data type for storing UUIDs in a BINARY column in MySQL: ``` php class UuidType extends Type { const UUID = 'uuid'; /** * @inheritdoc */ public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { return $platform->getBinaryTypeDeclarationSQL($fieldDeclaration); } /** * @inheritdoc */ public function getName() { return self::UUID; } /** * @inheritdoc */ public function convertToPhpValue($value, AbstractPlatform $platform) { if ($value !== null) { return strtoupper(bin2hex($value)); } } /** * @inheritdoc */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value !== null) { // If the app put any dashes in, we strip them, just in case return hex2bin(str_replace('-', '',$value)); } } /** * Generate a UUID that is optimized for MySQL's InnoDB engine * Based on UUID1, but transposed for more optimal inserts and sized for binary(16) column * * @return string MySQL-optimized UUID that works well with UUID column type */ public static function generateUuid() { $uuid = Uuid::uuid1()->toString(); $uuidFormattedForMySQL = self::transposeUuid($uuid); return $uuidFormattedForMySQL; } /** * Optimize format of UUID for storing as binary in MySQL * * @param $uuid * * @return string * * @see http://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/ */ public static function transposeUuid($uuid) { /* * orig UUID1: 13341cb5-c1f8-11e4-91e7-080027880ca6 * transpose: 11e4-c1f8-13341cb5-91e7-080027880ca6 * format: 11E4C1F813341CB591E7080027880CA6 <-- this is what can be ideally stored as binary in MySQL */ $uuidOptimizedOrderForMySQL = substr($uuid, 14, 4) . substr($uuid, 9, 4) . substr($uuid, 0, 8) . substr( $uuid, 19, 17 ); $uuidFormattedForMySQL = strtoupper(str_replace('-', '', $uuidOptimizedOrderForMySQL)); return $uuidFormattedForMySQL; } public function requiresSQLCommentHint(AbstractPlatform $platform) { return true; } ``` ...and the following related (example) entities: ``` php /** * @Entity */ class Thing { /** * @Id * @var string UUID * @Column(type="uuid", length=16) * @GeneratedValue(strategy="CUSTOM") * @CustomIdGenerator(class="Fisdap\Doctrine\Extensions\IdGenerator\UuidGenerator") */ protected $id; /** * @OneToOne(targetEntity="OtherThing", mappedBy="thing") */ protected $otherThing; } /** * @Entity */ class OtherThing { /** * @Id * @Column(type="integer") * @GeneratedValue */ protected $id; /** * @OneToOne(targetEntity="Thing", inversedBy="otherThing") */ protected $thing; } ``` In our case, since `SchemaTool::gatherRelationJoinColumns()` doesn't copy `$fieldMapping['length']` to `$columnOptions['length']` for non-string / custom data types, the resulting `CREATE TABLE` SQL for the OtherThing entity will have a column definition for its association with Thing as `thing_id BINARY(0)`. Of course, MySQL/InnoDB will be unable to create the index for a column with zero length, and multiple exceptions are thrown. I believe the fix here is to simply check whether `$fieldMapping['length']` is set, regardless of the value of `$fieldMapping['type']`. For good measure, I did the same for `$fieldMapping['scale']` and `$fieldMapping['precision']`.
admin added the pull-request label 2026-01-22 16:04:22 +01:00
admin closed this issue 2026-01-22 16:04:22 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#9456