“VARBINARY”字段在CakePHP 4中

huangapple go评论67阅读模式
英文:

VARBINARY field in CakePHP 4

问题

I have a varbinary(200) NOT NULL field on a table.

Let's say the table is called users and the field is called secret.

In CakePHP 3.X, accessing

$user->secret;

after retrieving one row of the table would give me a string.

In CakePHP 4.X, instead, accessing the same property gives me a resource.
If I do

stream_get_contents($user->secret);

I can then access the string value, but why do I have do that now? I couldn't find anything specific in the CakePHP changelogs pertaining to varbinary fields.

It's quite annoying now to have to go through all the places in my code where I access that property, and add stream_get_contents() everywhere.

Is there any way I can change my model instead so that the behavior remains consistent with what it was before?

英文:

I have a varbinary(200) NOT NULL field on a table.

Let's say the table is called users and the field is called secret.

In CakePHP 3.X, accessing

$user->secret;

after retrieving one row of the table would give me a string.

In CakePHP 4.X, instead, accessing the same property gives me a resource.
If I do

stream_get_contents($user->secret);

I can then access the string value, but why do I have do that now? I couldn't find anything specific in the CakePHP changelogs pertaining to varbinary fields.

It's quite annoying now to have to go through all the places in my code where I access that property, and add stream_get_contents() everywhere.

Is there any way I can change my model instead so that the behavior remains consistent with what it was before?

答案1

得分: 1

以下是您要翻译的内容:

"Binary data columns were always meant to return resources in 3.x, but you're probably using a very old 3.x version, where VARBINARY wasn't recognized (pre 3.6.12), resulting in it to default to treating it as a string.

Binary data columns are generally returned as resources in order to unify the behavior across the different supported DBMS and their various binary data types, as for some of them PDO returns the data as strings, and for others it returns it as resource handles.

Using entity accessors as suggested by @manuel-guzman is a way to always retrieve strings, but be aware that accessors will be used when persisting entities, meaning they will be invoked when saving, and the data they return is what will end up in the database!

Another option would be result formatters:

// in your `UsersTable` class

public function beforeFind(
    \Cake\Event\EventInterface $event,
    \Cake\ORM\Query $query,
    \ArrayObject $options,
    $primary
) {
    $query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
        return $results->map(function ($row) {
            if (
                isset($row['secret']) &&
                is_resource($row['secret'])
            ) {
                $row['secret'] = stream_get_contents($row['secret']);
            }
            
            return $row;
        });
    });
}

See also

And there's also custom database types that could be used to always return the data as strings when reading from the database using the query builder:

// in `src/Database/Type/StringBinaryType.php`

namespace App\Database\Type;

use Cake\Database\DriverInterface;
use Cake\Database\Type\BinaryType;
use InvalidArgumentException;
use PDO;

class StringBinaryType extends BinaryType
{
    public function toPHP($value, DriverInterface $driver)
    {
        if ($value === null) {
            return null;
        }
        
        if (is_string($value)) {
            return $value;
        }
        
        if (is_resource($value)) {
            return stream_get_contents($value);
        }
        
        throw an InvalidArgumentException(sprintf(
            'Cannot convert value of type `%s` to string',
            getTypeName($value)
        ));
    }
}
// in `config/bootstrap.php`

use Cake\Database\TypeFactory;

TypeFactory::map('stringBinary', \App\Database\Type\StringBinaryType::class);
// in your `UsersTable` class

public function initialize(): void
{
    // ...
    
    $this->getSchema()->setColumnType('secret', 'stringBinary');
}

See also

英文:

Binary data columns were always meant to return resources in 3.x, but you're probably using a very old 3.x version, where VARBINARY wasn't recognized (pre 3.6.12), resulting in it to default to treating it as a string.

Binary data columns are generally returned as resources in order to unify the behavior across the different supported DBMS and their various binary data types, as for some of them PDO returns the data as strings, and for others it returns it as resource handles.

Using entity accessors as suggested by @manuel-guzman is a way to always retrieve strings, but be aware that accessors will be used when persisting entities, meaning they will be invoked when saving, and the data they return is what will end up in the database!

Another option would be result formatters:

// in your `UsersTable` class

public function beforeFind(
    \Cake\Event\EventInterface $event,
    \Cake\ORM\Query $query,
    \ArrayObject $options,
    $primary
) {
    $query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
        return $results->map(function ($row) {
            if (
                isset($row['secret']) &&
                is_resource($row['secret'])
            ) {
                $row['secret'] = stream_get_contents($row['secret']);
            }
            
            return $row;
        });
    });
}

See also

And there's also custom database types that could be used to always return the data as strings when reading from the database using the query builder:

// in `src/Database/Type/StringBinaryType.php`

namespace App\Database\Type;

use Cake\Database\DriverInterface;
use Cake\Database\Type\BinaryType;
use InvalidArgumentException;
use PDO;

class StringBinaryType extends BinaryType
{
    public function toPHP($value, DriverInterface $driver)
    {
        if ($value === null) {
            return null;
        }
        
        if (is_string($value)) {
            return $value;
        }
        
        if (is_resource($value)) {
            return stream_get_contents($value);
        }
        
        throw new InvalidArgumentException(sprintf(
            'Cannot convert value of type `%s` to string',
            getTypeName($value)
        ));
    }
}
// in `config/bootstrap.php`

use Cake\Database\TypeFactory;

TypeFactory::map('stringBinary', \App\Database\Type\StringBinaryType::class);
// in your `UsersTable` class

public function initialize(): void
{
    // ...
    
    $this->getSchema()->setColumnType('secret', 'stringBinary');
}

See also

答案2

得分: 0

I'm not familiar with the cake fw itself, but it looks like you can use the accessors in entities so you can alter the return when retrieving the property:

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Test extends Entity
{
    protected function _getSecret($secret)
    {
        return stream_get_contents($secret);
    }
}

echo $test->secret; // returns FOO instead of foo
echo $test->get('secret'); // returns FOO instead of foo

Example taken from the link above.

英文:

I'm not familiar with the cake fw itself, but it looks like you can use the accessors in entities so you can alter the return when retrieving the property:

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Test extends Entity
{
    protected function _getSecret($secret)
    {
        return stream_get_contents($secret);
    }
}

echo $test->secret; // returns FOO instead of foo
echo $test->get('secret'); // returns FOO instead of foo

Example taken from the link above.

huangapple
  • 本文由 发表于 2023年4月17日 21:55:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76035941.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定