Search code examples
unit-testingcakephpfixturescakephp-2.x

Save image to DB in unit test doesn't work with type binary in CakePHP 2


Here is the fixture code:

class UploadfileFixture extends CakeTestFixture {

/**
 * Fields
 *
 * @var array
 */
    public $fields = array(
        'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'key' => 'primary'),
        'data' => array('type' => 'binary', 'null' => false, 'default' => null),
        'path' => array('type' => 'string', 'null' => false, 'default' => null, 'collate' => 'utf8_general_ci',
'charset' => 'utf8'),
...

As you can see I'm using binary type for BLOB in data column as specified in documentation.

On save method:

 $file['data'] = @file_get_contents($uri);
 ...
 $model_file->save($file);

I'm getting this message when trying to save 151K image:

PDOException: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'data' at row 1

Solution

  • I would assume that you are using MySQL, which means that your BLOB type column will be limited to

    L + 2 bytes, where L < 216

    http://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html#idm140456796731584

    ie, 64k, which your 151k exceeds by far.

    By default CakePHPs fixtures only support binary, which will map to blob no matter what. You can however add custom column definitions, or modify existing ones via the Mysql::$columns property. You can do so by for example

    using an extended datasource

    app/Model/Datasource/Database/AppMysql.php

    <?php
    App::uses('Mysql', 'Model/Datasource/Database');
    
    class AppMysql extends Mysql
    {
        public function __construct($config, $autoConnect)
        {
            $this->columns['mediumbinary'] = array('name' => 'mediumblob');
            parent::__construct($config, $autoConnect);
        }
    }
    

    app/Config/database.php

    public $test = array(
        'datasource' => 'Database/AppMysql',
        // ...
    );
    

    Your fixture

    public $fields = array(
        // ...
        'data' => array('type' => 'mediumbinary', 'null' => false, 'default' => null),
        // ...
    );
    

    or by modifying the datasource on the fly

    for example in that specific fixture, like

    public function init()
    {
        $source = ConnectionManager::getDataSource($this->useDbConfig);
        $source->columns['mediumbinary'] = array('name' => 'mediumblob');
    
        parent::init();
    }