From 9c0940017d57ebfd40147d9f8308c1d40f2dc856 Mon Sep 17 00:00:00 2001 From: Jad Bitar Date: Tue, 23 Sep 2014 20:28:12 -0400 Subject: [PATCH 1/5] Add CakePHP ORM --- src/Faker/ORM/CakePHP/ColumnTypeGuesser.php | 65 +++++++++++ src/Faker/ORM/CakePHP/EntityPopulator.php | 117 ++++++++++++++++++++ src/Faker/ORM/CakePHP/Populator.php | 85 ++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 src/Faker/ORM/CakePHP/ColumnTypeGuesser.php create mode 100644 src/Faker/ORM/CakePHP/EntityPopulator.php create mode 100644 src/Faker/ORM/CakePHP/Populator.php diff --git a/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php new file mode 100644 index 0000000000..48937367fd --- /dev/null +++ b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php @@ -0,0 +1,65 @@ +generator = $generator; + } + + public function guessFormat($column, $table) + { + $generator = $this->generator; + $schema = $table->schema(); + + switch ($schema->columnType($column)) { + case 'boolean': + return function() use ($generator) { + return $generator->boolean; + }; + case 'integer': + return function() { + return mt_rand(0, intval('2147483647')); + }; + case 'biginteger': + return function() { + return mt_rand(0, intval('18446744073709551615')); + }; + case 'decimal': + case 'float': + return function() { + return mt_rand(0, intval('4294967295')) / mt_rand(1, intval('4294967295')); + }; + case 'uuid': + return function() { + return \Cake\Utility\String::uuid(); + }; + case 'string': + $columnData = $schema->column($column); + $length = $columnData['length']; + return function () use ($generator, $length) { + return $generator->text($length); + }; + case 'text': + return function () use ($generator) { + return $generator->text(); + }; + case 'date': + case 'datetime': + case 'timestamp': + case 'time': + return function () use ($generator) { + return $generator->datetime(); + }; + + case 'binary': + default: + return null; + } + } + +} diff --git a/src/Faker/ORM/CakePHP/EntityPopulator.php b/src/Faker/ORM/CakePHP/EntityPopulator.php new file mode 100644 index 0000000000..c9ebc677f8 --- /dev/null +++ b/src/Faker/ORM/CakePHP/EntityPopulator.php @@ -0,0 +1,117 @@ +class = $class; + } + + public function __get($name) + { + return $this->{$name}; + } + + public function __set($name, $value) + { + $this->{$name} = $value; + } + + public function mergeColumnFormattersWith($columnFormatters) + { + $this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); + } + + public function mergeModifiersWith($modifiers) + { + $this->modifiers = array_merge($this->modifiers, $modifiers); + } + + public function guessColumnFormatters($populator) + { + $formatters = []; + $class = $this->class; + $table = TableRegistry::get($class); + $schema = $table->schema(); + $pk = $schema->primaryKey(); + $guessers = $populator->getGuessers() + ['ColumnTypeGuesser' => new ColumnTypeGuesser($populator->getGenerator())]; + $isForeignKey = function ($column) use ($table) { + foreach ($table->associations()->type('BelongsTo') as $assoc) { + if ($column == $assoc->foreignKey()) { + return true; + } + } + return false; + }; + + + foreach ($schema->columns() as $column) { + if ($column == $pk[0] || $isForeignKey($column)) { + continue; + } + + foreach ($guessers as $guesser) { + if ($formatter = $guesser->guessFormat($column, $table)) { + $formatters[$column] = $formatter; + break; + } + } + } + + return $formatters; + } + + public function guessModifiers($populator) + { + $modifiers = []; + $table = TableRegistry::get($this->class); + + $belongsTo = $table->associations()->type('BelongsTo'); + foreach ($belongsTo as $assoc) { + $modifiers['belongsTo' . $assoc->name()] = function ($data, $insertedEntities) use ($assoc) { + $table = $assoc->target(); + $foreignModel = $table->alias(); + $foreignKey = $insertedEntities[$foreignModel][array_rand($insertedEntities[$foreignModel])]; + $primaryKey = $table->primaryKey(); + $data[$assoc->foreignKey()] = $foreignKey; + return $data; + }; + } + + // TODO check if TreeBehavior attached to modify lft/rgt cols + + return $modifiers; + } + + public function execute($class, $insertedEntities, $options = []) + { + $table = TableRegistry::get($class); + $entity = $table->newEntity(); + + foreach ($this->columnFormatters as $column => $format) { + if (!is_null($format)) { + $entity->{$column} = is_callable($format) ? $format($insertedEntities, $table) : $format; + } + } + + foreach ($this->modifiers as $modifier) { + $entity = $modifier($entity, $insertedEntities); + } + + if (!$entity = $table->save($entity, $options)) { + throw new \RuntimeException("Failed saving $class record"); + } + + $pk = $table->primaryKey(); + return $entity->{$pk}; + } +} diff --git a/src/Faker/ORM/CakePHP/Populator.php b/src/Faker/ORM/CakePHP/Populator.php new file mode 100644 index 0000000000..e8ff9663ac --- /dev/null +++ b/src/Faker/ORM/CakePHP/Populator.php @@ -0,0 +1,85 @@ +generator = $generator; + } + + public function getGenerator() + { + return $this->generator; + } + + public function getGuessers() + { + return $this->guessers; + } + + public function removeGuesser($name) + { + if ($this->guessers[$name]) { + unset($this->guessers[$name]); + } + return $this; + } + + public function addGuesser($class) + { + if (!is_object($class)) { + $class = new $class($this->generator); + } + + if (!method_exists($class, 'guessFormat')) { + throw new \Exception('Missing required custom guesser method: ' . get_class($class) . '::guessFormat()'); + } + + $this->guessers[get_class($class)] = $class; + return $this; + } + + public function addEntity($entity, $number, $customColumnFormatters = [], $customModifiers = []) + { + if (!$entity instanceof EntityPopulator) { + $entity = new EntityPopulator($entity); + } + + $entity->columnFormatters = $entity->guessColumnFormatters($this); + if ($customColumnFormatters) { + $entity->mergeColumnFormattersWith($customColumnFormatters); + } + + $entity->modifiers = $entity->guessModifiers($this); + if ($customModifiers) { + $entity->mergeModifiers($customModifiers); + } + + $class = $entity->class; + $this->entities[$class] = $entity; + $this->quantities[$class] = $number; + return $this; + } + + public function execute($options = []) + { + $insertedEntities = []; + + foreach ($this->quantities as $class => $number) { + for ($i = 0; $i < $number; $i++) { + $insertedEntities[$class][] = $this->entities[$class]->execute($class, $insertedEntities, $options); + } + } + + return $insertedEntities; + } + +} From c2055c7c723cf7693fba2962b1d2dc226054f08f Mon Sep 17 00:00:00 2001 From: Jad Bitar Date: Wed, 24 Sep 2014 03:54:00 -0400 Subject: [PATCH 2/5] Use built-in formatters --- src/Faker/ORM/CakePHP/ColumnTypeGuesser.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php index 48937367fd..01767fdee7 100644 --- a/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php +++ b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php @@ -22,21 +22,21 @@ public function guessFormat($column, $table) return $generator->boolean; }; case 'integer': - return function() { - return mt_rand(0, intval('2147483647')); + return function() use ($generator) { + return $generator->randomNumber(10); }; case 'biginteger': - return function() { - return mt_rand(0, intval('18446744073709551615')); + return function() use ($generator) { + return $generator->randomNumber(20); }; case 'decimal': case 'float': - return function() { - return mt_rand(0, intval('4294967295')) / mt_rand(1, intval('4294967295')); + return function() use ($generator) { + return $generator->randomFloat(); }; case 'uuid': - return function() { - return \Cake\Utility\String::uuid(); + return function() use ($generator) { + return $generator->uuid(); }; case 'string': $columnData = $schema->column($column); From 1b06bf8c2d5df6e87e904e842ce0b4daddd58819 Mon Sep 17 00:00:00 2001 From: Jad Bitar Date: Wed, 24 Sep 2014 03:55:44 -0400 Subject: [PATCH 3/5] Add CakePHP ORM support --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index e321ad6d80..3eada6cd2c 100644 --- a/readme.md +++ b/readme.md @@ -308,7 +308,7 @@ You can check available Faker locales in the source code, [under the `Provider` ## Populating Entities Using an ORM or an ODM -Faker provides adapters for Object-Relational and Object-Document Mappers (currently, [Propel](http://www.propelorm.org), [Doctrine2](http://www.doctrine-project.org/projects/orm/2.0/docs/en), and [Mandango](https://github.com/mandango/mandango) are supported). These adapters ease the population of databases through the Entity classes provided by an ORM library (or the population of document stores using Document classes provided by an ODM library). +Faker provides adapters for Object-Relational and Object-Document Mappers (currently, [Propel](http://www.propelorm.org), [Doctrine2](http://www.doctrine-project.org/projects/orm/2.0/docs/en), [CakePHP](http://cakephp.org) and [Mandango](https://github.com/mandango/mandango) are supported). These adapters ease the population of databases through the Entity classes provided by an ORM library (or the population of document stores using Document classes provided by an ODM library). To populate entities, create a new populator class (using a generator instance as parameter), then list the class and number of all the entities that must be generated. To launch the actual data population, call the `execute()` method. @@ -387,7 +387,7 @@ echo $faker->name; // 'Jess Mraz I'; > // make sure you fix the $max parameter > $faker->dateTime('2014-02-25 08:37:17'); // will return always the same date when seeded > ``` -> +> > **Tip**: Formatters won't reproduce the same fake data if you use the `rand()` php function. Use `$faker` or `mt_rand()` instead: > > ```php From 95da1a967585527b0c48f172916d7f7570974011 Mon Sep 17 00:00:00 2001 From: Jad Bitar Date: Wed, 3 Dec 2014 10:31:10 -0500 Subject: [PATCH 4/5] Fix CI errors --- src/Faker/ORM/CakePHP/ColumnTypeGuesser.php | 11 +++++------ src/Faker/ORM/CakePHP/Populator.php | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php index 01767fdee7..0f36f60769 100644 --- a/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php +++ b/src/Faker/ORM/CakePHP/ColumnTypeGuesser.php @@ -18,24 +18,24 @@ public function guessFormat($column, $table) switch ($schema->columnType($column)) { case 'boolean': - return function() use ($generator) { + return function () use ($generator) { return $generator->boolean; }; case 'integer': - return function() use ($generator) { + return function () use ($generator) { return $generator->randomNumber(10); }; case 'biginteger': - return function() use ($generator) { + return function () use ($generator) { return $generator->randomNumber(20); }; case 'decimal': case 'float': - return function() use ($generator) { + return function () use ($generator) { return $generator->randomFloat(); }; case 'uuid': - return function() use ($generator) { + return function () use ($generator) { return $generator->uuid(); }; case 'string': @@ -61,5 +61,4 @@ public function guessFormat($column, $table) return null; } } - } diff --git a/src/Faker/ORM/CakePHP/Populator.php b/src/Faker/ORM/CakePHP/Populator.php index e8ff9663ac..cd5aa47562 100644 --- a/src/Faker/ORM/CakePHP/Populator.php +++ b/src/Faker/ORM/CakePHP/Populator.php @@ -81,5 +81,4 @@ public function execute($options = []) return $insertedEntities; } - } From b8d05ad6fad645d0447eb520a9990e4d7a90983e Mon Sep 17 00:00:00 2001 From: Jad Bitar Date: Thu, 4 Dec 2014 00:50:11 -0500 Subject: [PATCH 5/5] Add option to change db connection --- src/Faker/ORM/CakePHP/EntityPopulator.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Faker/ORM/CakePHP/EntityPopulator.php b/src/Faker/ORM/CakePHP/EntityPopulator.php index c9ebc677f8..db1f705600 100644 --- a/src/Faker/ORM/CakePHP/EntityPopulator.php +++ b/src/Faker/ORM/CakePHP/EntityPopulator.php @@ -8,6 +8,7 @@ class EntityPopulator { protected $class; + protected $connectionName; protected $columnFormatters = []; protected $modifiers = []; @@ -40,7 +41,7 @@ public function guessColumnFormatters($populator) { $formatters = []; $class = $this->class; - $table = TableRegistry::get($class); + $table = $this->getTable($class); $schema = $table->schema(); $pk = $schema->primaryKey(); $guessers = $populator->getGuessers() + ['ColumnTypeGuesser' => new ColumnTypeGuesser($populator->getGenerator())]; @@ -73,7 +74,7 @@ public function guessColumnFormatters($populator) public function guessModifiers($populator) { $modifiers = []; - $table = TableRegistry::get($this->class); + $table = $this->getTable($this->class); $belongsTo = $table->associations()->type('BelongsTo'); foreach ($belongsTo as $assoc) { @@ -94,7 +95,7 @@ public function guessModifiers($populator) public function execute($class, $insertedEntities, $options = []) { - $table = TableRegistry::get($class); + $table = $this->getTable($class); $entity = $table->newEntity(); foreach ($this->columnFormatters as $column => $format) { @@ -114,4 +115,18 @@ public function execute($class, $insertedEntities, $options = []) $pk = $table->primaryKey(); return $entity->{$pk}; } + + public function setConnection($name) + { + $this->connectionName = $name; + } + + protected function getTable($class) + { + $options = []; + if (!empty($this->connectionName)) { + $options['connection'] = $this->connectionName; + } + return TableRegistry::get($class, $options); + } }