Skip to content

Commit

Permalink
Merge pull request #36 from jeremyharris/types
Browse files Browse the repository at this point in the history
[RFC] Added typing support
  • Loading branch information
josegonzalez authored Jul 9, 2018
2 parents 9805605 + 2f55cb8 commit 10ad5a7
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ script:

notifications:
email: false

addons:
postgresql: "9.4"
33 changes: 32 additions & 1 deletion src/Model/Behavior/VersionBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use ArrayObject;
use Cake\Collection\Collection;
use Cake\Database\Type;
use Cake\Datasource\EntityInterface;
use Cake\Event\Event;
use Cake\Event\EventManager;
Expand All @@ -26,6 +27,7 @@
use Cake\Utility\Hash;
use Cake\Utility\Inflector;
use DateTime;
use InvalidArgumentException;

/**
* This behavior provides a way to version dynamic data by keeping versions
Expand Down Expand Up @@ -183,11 +185,13 @@ public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $o
continue;
}

$converted = $this->_convertFieldsToType([$field => $content], 'toDatabase');

$data = [
'version_id' => $versionId,
'model' => $this->_config['referenceName'],
'field' => $field,
'content' => $content,
'content' => $converted[$field],
'created' => $created,
] + $this->_extractForeignKey($entity);

Expand Down Expand Up @@ -309,6 +313,8 @@ public function groupVersions($results)
$versionField => $versionId
];

$keys = $this->_convertFieldsToType($keys, 'toPHP');

/* @var \Cake\Datasource\EntityInterface $versionRow */
$versionRow = $grouped->match(['version_id' => $versionId])->first();

Expand Down Expand Up @@ -428,4 +434,29 @@ protected function _referenceName()

return $name;
}

/**
* Converts fields to the appropriate type to be stored in the version, and
* to be converted from the version record to the entity
*
* @param array $fields Fields to convert
* @param string $direction Direction (toPHP or toDatabase)
* @return array
*/
protected function _convertFieldsToType(array $fields, $direction)
{
if (!in_array($direction, ['toPHP', 'toDatabase'])) {
throw new InvalidArgumentException(sprintf('Cannot convert type, Cake\Database\Type::%s does not exist', $direction));
}

$driver = $this->_table->getConnection()->getDriver();
foreach ($fields as $field => $content) {
$column = $this->_table->getSchema()->getColumn($field);
$type = Type::build($column['type']);

$fields[$field] = $type->{$direction}($content, $driver);
}

return $fields;
}
}
1 change: 1 addition & 0 deletions tests/Fixture/ArticlesFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ArticlesFixture extends TestFixture
'title' => ['type' => 'string', 'null' => true],
'body' => 'text',
'published' => ['type' => 'string', 'length' => 1, 'default' => 'N'],
'settings' => ['type' => 'json', 'null' => true],
'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]]
];

Expand Down
110 changes: 109 additions & 1 deletion tests/TestCase/Model/Behavior/VersionBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
use Cake\ORM\Entity;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;
use InvalidArgumentException;
use Josegonzalez\Version\Model\Behavior\VersionBehavior;
use Josegonzalez\Version\Model\Behavior\Version\VersionTrait;
use ReflectionObject;

class TestEntity extends Entity
{
Expand Down Expand Up @@ -67,7 +69,7 @@ public function testSaveNew()
->toArray();

$this->assertEquals(3, $article->version_id);
$this->assertCount(12, $results);
$this->assertCount(13, $results);
}

/**
Expand Down Expand Up @@ -390,4 +392,110 @@ public function testGetVersionId()
$this->assertEquals(2, $article->version_id);
$this->assertEquals(3, $table->getVersionId($article));
}

/**
* tests saving a non scalar db type, such as JSON
*
* @return void
*/
public function testSaveNonScalarType()
{
$table = TableRegistry::get('Articles', [
'entityClass' => 'Josegonzalez\Version\Test\TestCase\Model\Behavior\TestEntity',
]);
$schema = $table->getSchema();
$schema->setColumnType('settings', 'json');
$table->setSchema($schema);
$table->addBehavior('Josegonzalez/Version.Version');

$data = ['test' => 'array'];
$article = $table->get(1);
$article->settings = $data;
$table->saveOrFail($article);

$version = $article->version($article->version_id);
$this->assertSame($data, $version->settings);
}

/**
* tests versions convert types
*
* @return void
*/
public function testVersionConvertsType()
{
$table = TableRegistry::get('Articles', [
'entityClass' => 'Josegonzalez\Version\Test\TestCase\Model\Behavior\TestEntity',
]);
$table->addBehavior('Josegonzalez/Version.Version');

$article = $table->get(1);
$version = $article->version($article->version_id);
$this->assertInternalType('int', $version->author_id);
}

/**
* tests _convertFieldsToType
*
* @return void
*/
public function testConvertFieldsToType()
{
$table = TableRegistry::get('Articles', [
'entityClass' => 'Josegonzalez\Version\Test\TestCase\Model\Behavior\TestEntity',
]);
$schema = $table->getSchema();
$schema->setColumnType('settings', 'json');
$table->setSchema($schema);
$behavior = new VersionBehavior($table);

$reflection = new ReflectionObject($behavior);
$method = $reflection->getMethod('_convertFieldsToType');
$method->setAccessible(true);

$data = ['test' => 'array'];
$fields = [
'settings' => json_encode($data),
'author_id' => '1',
'body' => 'text',
];
$fields = $method->invokeArgs($behavior, [$fields, 'toPHP']);
$this->assertInternalType('array', $fields['settings']);
$this->assertSame($data, $fields['settings']);
$this->assertInternalType('int', $fields['author_id']);
$this->assertInternalType('string', $fields['body']);

$data = ['test' => 'array'];
$fields = [
'settings' => ['test' => 'array'],
'author_id' => 1,
'body' => 'text',
];
$fields = $method->invokeArgs($behavior, [$fields, 'toDatabase']);
$this->assertInternalType('string', $fields['settings']);
$this->assertSame(json_encode($data), $fields['settings']);
$this->assertInternalType('int', $fields['author_id']);
$this->assertInternalType('string', $fields['body']);
}

/**
* tests passing an invalid direction to _convertFieldsToType
*
* @return void
*/
public function testConvertFieldsToTypeInvalidDirection()
{
$this->expectException(InvalidArgumentException::class);

$table = TableRegistry::get('Articles', [
'entityClass' => 'Josegonzalez\Version\Test\TestCase\Model\Behavior\TestEntity',
]);
$behavior = new VersionBehavior($table);

$reflection = new ReflectionObject($behavior);
$method = $reflection->getMethod('_convertFieldsToType');
$method->setAccessible(true);

$method->invokeArgs($behavior, [[], 'invalidDirection']);
}
}

0 comments on commit 10ad5a7

Please sign in to comment.