Skip to content

Commit

Permalink
Entities can automatically mutate date fields to Time instances. Crea…
Browse files Browse the repository at this point in the history
…ted_at and updated_at are automatically mutated by default. Needs timezone and locale integration
  • Loading branch information
lonnieezell committed Jul 29, 2017
1 parent 496b807 commit 454a097
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 89 deletions.
6 changes: 6 additions & 0 deletions system/Database/Forge.php
Expand Up @@ -488,6 +488,12 @@ public function dropTable($table_name, $if_exists = false)
return false;
}

// If the prefix is already starting the table name, remove it...
if (strpos($table_name, $this->db->DBPrefix) === 0)
{
$table_name = substr($table_name, strlen($this->db->DBPrefix));
}

if (($query = $this->_dropTable($this->db->DBPrefix . $table_name, $if_exists)) === true)
{
return true;
Expand Down
62 changes: 59 additions & 3 deletions system/Entity.php
@@ -1,5 +1,7 @@
<?php namespace CodeIgniter;

use CodeIgniter\I18n\Time;

/**
* CodeIgniter
*
Expand Down Expand Up @@ -52,6 +54,10 @@ class Entity
*/
protected $datamap = [];

protected $_options = [
'dates' => ['created_at', 'updated_at', 'deleted_at'],
];

/**
* Allows filling in Entity parameters during construction.
*
Expand Down Expand Up @@ -116,15 +122,23 @@ public function __get(string $key)
// use that method to insert this value.

if (method_exists($this, $method))
{
return $this->$method();
$result = $this->$method();
}

// Otherwise return the protected property
// if it exists.
if (property_exists($this, $key))
else if (property_exists($this, $key))
{
$result = $this->$key;
}

// Do we need to mutate this into a date?
if (in_array($key, $this->_options['dates']))
{
return $this->$key;
$result = $this->mutateDate($result);
}

return $result;
}

//--------------------------------------------------------------------
Expand All @@ -147,6 +161,12 @@ public function __set(string $key, $value = null)
{
$key = $this->mapProperty($key);

// Check if the field should be mutated into a date
if (in_array($key, $this->_options['dates']))
{
$value = $this->mutateDate($value);
}

// if a set* method exists for this key,

// use that method to insert this value.

$method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
Expand Down Expand Up @@ -233,4 +253,40 @@ protected function mapProperty(string $key)
}

//--------------------------------------------------------------------

/**
* Converts the given string|timestamp|DateTime|Time instance
* into a \CodeIgniter\I18n\Time object.
*
* @param $value
*
* @return \CodeIgniter\I18n\Time
*/
private function mutateDate($value)
{
if ($value instanceof Time)
{
return $value;
}

if ($value instanceof \DateTime)
{
return Time::instance($value);
}

if (is_numeric($value))
{
return Time::createFromTimestamp($value);
}

if (is_string($value))
{
return Time::parse($value);
}

return $value;
}

//--------------------------------------------------------------------

}
33 changes: 28 additions & 5 deletions system/Model.php
Expand Up @@ -35,6 +35,7 @@
* @since Version 3.0.0
* @filesource
*/
use CodeIgniter\I18n\Time;
use CodeIgniter\Pager\Pager;
use CodeIgniter\Validation\ValidationInterface;
use Config\App;
Expand Down Expand Up @@ -582,7 +583,7 @@ public function save($data)
// them as an array.
if (is_object($data) && ! $data instanceof \stdClass)
{
$data = static::classToArray($data);
$data = static::classToArray($data, $this->dateFormat);
}

if (is_object($data) && isset($data->{$this->primaryKey}))
Expand Down Expand Up @@ -620,7 +621,7 @@ public function save($data)
*
* @return array
*/
public static function classToArray($data): array
public static function classToArray($data, string $dateFormat = 'datetime'): array
{
$mirror = new \ReflectionClass($data);
$props = $mirror->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED);
Expand All @@ -633,7 +634,29 @@ public static function classToArray($data): array
{
// Must make protected values accessible.
$prop->setAccessible(true);
$properties[$prop->getName()] = $prop->getValue($data);
$propName = $prop->getName();
$properties[$propName] = $prop->getValue($data);

// Convert any Time instances to appropriate $dateFormat
if ($properties[$propName] instanceof Time)
{
$converted = (string)$properties[$propName];

switch($dateFormat)
{
case 'datetime':
$converted = $properties[$propName]->format('Y-m-d H:i:s');
break;
case 'date':
$converted = $properties[$propName]->format('Y-m-d');
break;
case 'int':
$converted = $properties[$propName]->getTimestamp();
break;
}

$properties[$prop->getName()] = $converted;
}
}

return $properties;
Expand All @@ -657,7 +680,7 @@ public function insert($data, bool $returnID = true)
// them as an array.
if (is_object($data) && ! $data instanceof \stdClass)
{
$data = static::classToArray($data);
$data = static::classToArray($data, $this->dateFormat);
}

// If it's still a stdClass, go ahead and convert to
Expand Down Expand Up @@ -735,7 +758,7 @@ public function update($id, $data)
// them as an array.
if (is_object($data) && ! $data instanceof \stdClass)
{
$data = static::classToArray($data);
$data = static::classToArray($data, $this->dateFormat);
}

// If it's still a stdClass, go ahead and convert to
Expand Down
14 changes: 13 additions & 1 deletion system/Test/CIDatabaseTestCase.php
Expand Up @@ -154,7 +154,19 @@ public function setUp()
$this->migrations->setNamespace('Tests\Support');
}

$this->db->table('migrations')->truncate();
// Delete all of the tables to ensure we're at a clean start.
$tables = $this->db->listTables();

if (is_array($tables))
{
$forge = \Config\Database::forge('tests');

foreach ($tables as $table)
{
$forge->dropTable($table, true);
}
}

$this->migrations->version(0, null, 'tests');
$this->migrations->latest(null, 'tests');
}
Expand Down
Expand Up @@ -6,79 +6,38 @@ public function up()
{
// User Table
$this->forge->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 3,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 80,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'country' => [
'type' => 'VARCHAR',
'constraint' => 40,
],
'deleted' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => '0'
],
'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true],
'name' => ['type' => 'VARCHAR', 'constraint' => 80,],
'email' => ['type' => 'VARCHAR', 'constraint' => 100],
'country' => ['type' => 'VARCHAR', 'constraint' => 40,],
'deleted' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => '0'],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('user', true);

// Job Table
$this->forge->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 3,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 40,
],
'description' => [
'type' => 'TEXT',
],
'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true],
'name' => ['type' => 'VARCHAR', 'constraint' => 40],
'description' => ['type' => 'TEXT'],
'created_at' => ['type' => 'DATETIME', 'null' => true]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('job', true);

// Misc Table
$this->forge->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 3,
'auto_increment' => true,
],
'key' => [
'type' => 'VARCHAR',
'constraint' => 40,
],
'value' => [
'type' => 'TEXT',
],
'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true ],
'key' => ['type' => 'VARCHAR', 'constraint' => 40],
'value' => ['type' => 'TEXT'],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('misc', true);

// Empty Table
$this->forge->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 3,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 40,
],
'id' => ['type' => 'INTEGER', 'constraint' => 3, 'auto_increment' => true],
'name' => ['type' => 'VARCHAR', 'constraint' => 40,],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('empty', true);
Expand Down
4 changes: 2 additions & 2 deletions tests/_support/Models/EntityModel.php
Expand Up @@ -10,9 +10,9 @@ class EntityModel extends Model

protected $useSoftDeletes = false;

protected $dateFormat = 'integer';
protected $dateFormat = 'datetime';

protected $allowedFields = [
'name', 'description'
'name', 'description', 'created_at'
];
}
26 changes: 4 additions & 22 deletions tests/_support/Models/SimpleEntity.php
@@ -1,5 +1,7 @@
<?php namespace Tests\Support\Models;

use CodeIgniter\Entity;

/**
* Class SimpleEntity
*
Expand All @@ -8,31 +10,11 @@
*
* @package Tests\Support\Models
*/
class SimpleEntity
class SimpleEntity extends Entity
{
protected $id;
protected $name;
protected $description;

protected $datamap = [];

public function __get($key)
{
if (isset($this->$key))
{
return $this->$key;
}
}

//--------------------------------------------------------------------

public function __set($key, $value)
{
if (isset($this->$key))
{
$this->$key = $value;
}
}

protected $created_at;

}
10 changes: 9 additions & 1 deletion tests/system/Database/Live/ModelTest.php
@@ -1,6 +1,8 @@
<?php namespace CodeIgniter\Database\Live;

use CodeIgniter\I18n\Time;
use CodeIgniter\Model;
use CodeIgniter\Test\ReflectionHelper;
use Tests\Support\Models\EntityModel;
use Tests\Support\Models\EventModel;
use Tests\Support\Models\JobModel;
Expand All @@ -13,6 +15,8 @@
*/
class ModelTest extends \CIDatabaseTestCase
{
use ReflectionHelper;

protected $refresh = true;

protected $seed = 'CITestSeeder';
Expand Down Expand Up @@ -487,10 +491,14 @@ public function testCanCreateAndSaveEntityClasses()
$this->assertEquals('Awesome job, but sometimes makes you bored', $entity->description);

$entity->name = 'Senior Developer';
$entity->created_at = '2017-07-15';

$date = $this->getPrivateProperty($entity, 'created_at');
$this->assertTrue($date instanceof Time);

$model->save($entity);

$this->seeInDatabase('job', ['name' => 'Senior Developer']);
$this->seeInDatabase('job', ['name' => 'Senior Developer', 'created_at' => '2017-07-15 00:00:00']);
}

/**
Expand Down

0 comments on commit 454a097

Please sign in to comment.