Skip to content

Commit

Permalink
ADD time_interval_int type
Browse files Browse the repository at this point in the history
  • Loading branch information
nojimage committed Apr 24, 2019
1 parent cd6a087 commit 875fb77
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 127 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
</a>
</p>

This plugin provide `time_interval` custom type for MySQL's `TIME`, Postgres's `INTERVAL`.
This plugin provide `time_interval` custom type for MySQL's `TIME`, Postgres's `INTERVAL`,
and provide `time_interval_int` custom type for seconds as `INTEGER`.
This is a custom type to represent intervals, which CakePHP can treat as a `TimeInterval` object that inherits from `DateInterval`.

## Installation
Expand Down Expand Up @@ -59,6 +60,9 @@ class WorkTimesTable extends Table
// CakePHP <= 3.4.x use columnType() instead.
$schema->columnType('duration', 'time_interval');

// If your column type is seconds as INTEGER, Use `time_interval_int` instead.
$schema->setColumnType('duration_sec', 'time_interval_int');

return $schema;
}
}
Expand Down
2 changes: 2 additions & 0 deletions config/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
*/
use Cake\Database\Type;
use Cake\Validation\Validator;
use Elastic\TimeInterval\Database\Type\TimeIntervalAsIntType;
use Elastic\TimeInterval\Database\Type\TimeIntervalType;
use Elastic\TimeInterval\Validation\TimeIntervalValidation;

$getMap = method_exists(Type::class, 'getMap') ? 'getMap' : 'map';
if (!Type::$getMap('time_interval')) {
Type::map('time_interval', TimeIntervalType::class);
Type::map('time_interval_int', TimeIntervalAsIntType::class);
}

if (method_exists(Validator::class, 'addDefaultProvider')) {
Expand Down
67 changes: 67 additions & 0 deletions src/Database/Type/TimeIntervalAsIntType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Elastic\TimeInterval\Database\Type;

use Cake\Database\Driver;
use Cake\Database\Type;
use Elastic\TimeInterval\ValueObject\TimeInterval;
use Exception;
use PDO;
use UnexpectedValueException;

/**
* TimeInterval custom type for INTEGER column
*
* @link http://book.cakephp.org/3.0/en/orm/database-basics.html#adding-custom-database-types
*/
class TimeIntervalAsIntType extends Type
{
use TimeIntervalMarshalTrait;

/**
* @param mixed $value the value from database
* @param Driver $driver db driver
* @return mixed|null
* @throws Exception
*/
public function toPHP($value, Driver $driver)
{
if ($value === null) {
return null;
}

return TimeInterval::createFromSeconds($value);
}

/**
* @param mixed $value the value to database
* @param Driver $driver db driver
* @return false|mixed|string
* @throws UnexpectedValueException
* @throws Exception
*/
public function toDatabase($value, Driver $driver)
{
if ($value === null) {
return null;
}

if (!$value instanceof TimeInterval) {
$value = $this->marshal($value);
}

return $value->toSeconds();
}

/**
* {@inheritDoc}
*/
public function toStatement($value, Driver $driver)
{
if ($value === null) {
return PDO::PARAM_NULL;
}

return PDO::PARAM_INT;
}
}
38 changes: 38 additions & 0 deletions src/Database/Type/TimeIntervalMarshalTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Elastic\TimeInterval\Database\Type;

use DateInterval;
use Elastic\TimeInterval\ValueObject\TimeInterval;
use Exception;
use UnexpectedValueException;

trait TimeIntervalMarshalTrait
{
/**
* @param mixed $value the value
* @return mixed
* @throws UnexpectedValueException
* @throws Exception
*/
public function marshal($value)
{
if ($value === null || $value instanceof TimeInterval) {
return $value;
}

if ($value instanceof DateInterval) {
return TimeInterval::createFromDateInterval($value);
}

if (is_numeric($value)) {
return TimeInterval::createFromSeconds($value);
}

if (is_string($value)) {
return TimeInterval::createFromString($value);
}

throw new UnexpectedValueException('Invalid interval value.');
}
}
35 changes: 5 additions & 30 deletions src/Database/Type/TimeIntervalType.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
class TimeIntervalType extends Type
{
use TimeIntervalMarshalTrait;

/**
* @param mixed $value the value from database
* @param Driver $driver db driver
Expand All @@ -31,33 +33,6 @@ public function toPHP($value, Driver $driver)
return TimeInterval::createFromString($value);
}

/**
* @param mixed $value the value
* @return mixed
* @throws UnexpectedValueException
* @throws Exception
*/
public function marshal($value)
{
if ($value === null || $value instanceof TimeInterval) {
return $value;
}

if ($value instanceof DateInterval) {
return TimeInterval::createFromDateInterval($value);
}

if (is_numeric($value)) {
return TimeInterval::createFromSeconds($value);
}

if (is_string($value)) {
return TimeInterval::createFromString($value);
}

throw new UnexpectedValueException('Invalid interval value.');
}

/**
* @param mixed $value the value to database
* @param Driver $driver db driver
Expand All @@ -71,10 +46,10 @@ public function toDatabase($value, Driver $driver)
return null;
}

if ($value instanceof TimeInterval) {
return (string)$value;
if (!$value instanceof TimeInterval) {
$value = $this->marshal($value);
}

return (string)$this->marshal($value);
return (string)$value;
}
}
7 changes: 4 additions & 3 deletions tests/Fixture/WorkTimesFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class WorkTimesFixture extends TestFixture
'start' => ['type' => 'datetime', 'null' => false],
'end' => ['type' => 'datetime', 'null' => false],
'rest' => ['type' => 'time', 'null' => false, 'default' => '00:00:00'],
'rest_seconds' => ['type' => 'integer', 'null' => false, 'default' => '0'],
'duration' => ['type' => 'time', 'null' => false, 'default' => '00:00:00'],
'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
];
Expand All @@ -29,8 +30,8 @@ class WorkTimesFixture extends TestFixture
* @var array
*/
public $records = [
['start' => '2019-04-01 10:00:00', 'end' => '2019-04-01 19:00:00', 'rest' => '01:00:00', 'duration' => '08:00:00'],
['start' => '2019-04-02 09:30:00', 'end' => '2019-04-02 12:30:00', 'rest' => '00:30:00', 'duration' => '02:30:00'],
['start' => '2019-04-03 09:45:00', 'end' => '2019-04-04 19:30:00', 'rest' => '08:00:00', 'duration' => '25:45:00'],
['start' => '2019-04-01 10:00:00', 'end' => '2019-04-01 19:00:00', 'rest' => '01:00:00', 'rest_seconds' => 3600, 'duration' => '08:00:00'],
['start' => '2019-04-02 09:30:00', 'end' => '2019-04-02 12:30:00', 'rest' => '00:30:00', 'rest_seconds' => 1800, 'duration' => '02:30:00'],
['start' => '2019-04-03 09:45:00', 'end' => '2019-04-04 19:30:00', 'rest' => '08:00:00', 'rest_seconds' => 28800, 'duration' => '25:45:00'],
];
}
120 changes: 120 additions & 0 deletions tests/TestCase/Database/Type/BaseTimeIntervalTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

namespace Elastic\TimeInterval\Test\TestCase\Database\Type;

use Cake\Database\Driver;
use Cake\Database\Type;
use Cake\I18n\FrozenTime;
use Cake\TestSuite\TestCase;
use Elastic\TimeInterval\Database\Type\TimeIntervalAsIntType;
use Elastic\TimeInterval\Database\Type\TimeIntervalType;
use Elastic\TimeInterval\ValueObject\TimeInterval;

abstract class BaseTimeIntervalTypeTest extends TestCase
{
/**
* @var Type|TimeIntervalType|TimeIntervalAsIntType
*/
protected $type;

/**
* @var Driver|\PHPUnit_Framework_MockObject_MockObject
*/
protected $driver;

public function tearDown()
{
unset($this->type, $this->driver);
parent::tearDown();
}

/**
* test convert DB to PHP
*
* @dataProvider dataToPHP
*/
public function testToPHP($database, $expected)
{
$result = $this->type->toPHP($database, $this->driver);
$this->assertInstanceOf(TimeInterval::class, $result);
$this->assertSame($expected, (string)$result);
}

/**
* data for testToPHP
*
* @return array
*/
abstract public function dataToPHP();

/**
* test convert null value to PHP
*/
public function testToPHPWithNull()
{
$this->assertNull($this->type->toPHP(null, $this->driver));
}

/**
* test convert value
*
* @dataProvider dataMarshal
*/
public function testMarshal($value, $expected)
{
$result = $this->type->marshal($value);
$this->assertInstanceOf(TimeInterval::class, $result);
$this->assertSame($expected, (string)$result);
}

/**
* data for testMarshal
*
* @return array
*/
public function dataMarshal()
{
$a = new FrozenTime('2019-01-01 00:00:00');
$b = new FrozenTime('2019-01-02 02:15:01');
$c = new FrozenTime('2019-01-31 00:00:00');
$d = new FrozenTime('2019-03-01 02:15:01');

return [
['00:00:01', '00:00:01'],
['00:15:01', '00:15:01'],
['25:15:01', '25:15:01'],
['-25:15:01', '-25:15:01'],
['25:15', '25:15:00'],
['-25:15', '-25:15:00'],
[new \DateInterval('PT25H15M1S'), '25:15:01'],
[new \DateInterval('P1DT2H15M1S'), '26:15:01'],
[$a->diff($b), '26:15:01'],
[$c->diff($d), '698:15:01'],
[0, '00:00:00'],
[1, '00:00:01'],
[-1, '-00:00:01'],
[3599, '00:59:59'],
[-3599, '-00:59:59'],
[86401, '24:00:01'],
[-86401, '-24:00:01'],
];
}

/**
* test convert null value
*/
public function testMarshalWithNull()
{
$this->assertNull($this->type->marshal(null));
}

/**
* test convert PHP to DB
*/
abstract public function testToDatabase();

/**
* test get statement
*/
abstract public function testToStatement();
}
57 changes: 57 additions & 0 deletions tests/TestCase/Database/Type/TimeIntervalAsIntTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Elastic\TimeInterval\Test\TestCase\Database\Type;

use Cake\Database\Driver;
use Elastic\TimeInterval\Database\Type\TimeIntervalAsIntType;
use Elastic\TimeInterval\ValueObject\TimeInterval;
use PDO;

class TimeIntervalAsIntTypeTest extends BaseTimeIntervalTypeTest
{
public function setUp()
{
parent::setUp();
$this->type = new TimeIntervalAsIntType();
$this->driver = $this->getMockForAbstractClass(Driver::class);
}

/**
* data for testToPHP
*
* @return array
*/
public function dataToPHP()
{
return [
[0, '00:00:00'],
[1, '00:00:01'],
[901, '00:15:01'],
[90901, '25:15:01'],
[-90901, '-25:15:01'],
];
}

/**
* test convert PHP to DB
*/
public function testToDatabase()
{
$this->assertNull($this->type->toDatabase(null, $this->driver));
$this->assertSame(1, $this->type->toDatabase(TimeInterval::createFromString('00:00:01'), $this->driver));
$this->assertSame(1, $this->type->toDatabase(TimeInterval::createFromSeconds(1), $this->driver));
$this->assertSame(86400, $this->type->toDatabase(TimeInterval::createFromSeconds(86400), $this->driver));
$this->assertSame(86401, $this->type->toDatabase(TimeInterval::createFromSeconds(86401), $this->driver));
$this->assertSame(0, $this->type->toDatabase(TimeInterval::createFromSeconds(0), $this->driver));
$this->assertSame(-86401, $this->type->toDatabase(TimeInterval::createFromSeconds(-86401), $this->driver));
}

/**
* test get statement
*/
public function testToStatement()
{
$this->assertSame(PDO::PARAM_NULL, $this->type->toStatement(null, $this->driver));
$this->assertSame(PDO::PARAM_INT, $this->type->toStatement(TimeInterval::createFromSeconds(1), $this->driver));
}
}
Loading

0 comments on commit 875fb77

Please sign in to comment.