Search code examples
phpsymfonydoctrine

How to treat BLOB column as string in Doctrine?


Using Doctrine 3.1 - In my entity I have this column:

    #[ORM\Column(type: 'blob')]
    private $dataJpg;

If I make it string, PHP complains that resource is used and it cannot assign it during hydration. The problem is that I dont want to use resources at all, I want the binary data in its string form.

It is easy to work with resource when doing getter:

    public function getDataJpg(): ?string
    {
        $jpgData = $this->dataJpg;
        /** @var resource|null $jpgData*/
        if ($jpgData !== null) {
            return stream_get_contents($jpgData);
        }
        return null;
    }

But for setter I do not know how to approach it. I get string variable which I want to persist, but I cannot assign it to a resource. A workaround would be to fopen('php://memory') but I dont like that.

I need a way to tell doctrine to hydrate blob data as strings, so that I can get and set strings only and not care about resources.


Solution

  • For those interested, as suggested by @BERKUT in the comment, here is the custom type I made:

    <?php
    
    declare(strict_types=1);
    
    namespace App\DBAL\Types;
    
    use Doctrine\DBAL\ParameterType;
    use Doctrine\DBAL\Platforms\AbstractPlatform;
    use Doctrine\DBAL\Types\ConversionException;
    use Doctrine\DBAL\Types\Type;
    
    class BlobStringType extends Type
    {
        /**
         * {@inheritDoc}
         */
        public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
        {
            return $platform->getBlobTypeDeclarationSQL($column);
        }
    
        public function getName(): string
        {
            return 'blob_string';
        }
    
        /**
         * {@inheritDoc}
         */
        public function getBindingType(): ParameterType
        {
            return ParameterType::LARGE_OBJECT;
        }
    
        /**
         * {@inheritDoc}
         **/
        public function convertToPHPValue($value, AbstractPlatform $platform): mixed
        {
            if ($value === null) {
                return null;
            }
    
            if (is_string($value)) {
                return $value;
            }
    
            if (! is_resource($value)) {
                throw new ConversionException('Could not convert database value to string.');
            }
    
            if (stream_get_contents($value)) {
                return stream_get_contents($value);
            }
    
            throw new ConversionException('Could not convert database value to string.');
        }
    
        /**
         * {@inheritDoc}
         */
        public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed
        {
            if (is_null($value)) {
                return null;
            }
    
            if (is_string($value)) {
                $value = $this->convertStringToResource($value);
            }
    
            return $value;
        }
    
        /**
         * @return resource
         */
        public function convertStringToResource(string $value)
        {
            $fp = fopen('php://temp', 'rb+');
            assert(is_resource($fp));
            fwrite($fp, $value);
            fseek($fp, 0);
    
            return $fp;
        }
    }