Skip to content
This repository has been archived by the owner on Dec 11, 2020. It is now read-only.

Add CakePHP ORM #428

Merged
merged 5 commits into from
Dec 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
64 changes: 64 additions & 0 deletions src/Faker/ORM/CakePHP/ColumnTypeGuesser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Faker\ORM\CakePHP;

class ColumnTypeGuesser
{
protected $generator;

public function __construct(\Faker\Generator $generator)
{
$this->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 () use ($generator) {
return $generator->randomNumber(10);
};
case 'biginteger':
return function () use ($generator) {
return $generator->randomNumber(20);
};
case 'decimal':
case 'float':
return function () use ($generator) {
return $generator->randomFloat();
};
case 'uuid':
return function () use ($generator) {
return $generator->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;
}
}
}
132 changes: 132 additions & 0 deletions src/Faker/ORM/CakePHP/EntityPopulator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

namespace Faker\ORM\CakePHP;

use Cake\ORM\TableRegistry;
use Faker\Guesser\Name as NameGuesser;

class EntityPopulator
{
protected $class;
protected $connectionName;
protected $columnFormatters = [];
protected $modifiers = [];

public function __construct($class)
{
$this->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 = $this->getTable($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 = $this->getTable($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 = $this->getTable($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};
}

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);
}
}
84 changes: 84 additions & 0 deletions src/Faker/ORM/CakePHP/Populator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Faker\ORM\CakePHP;

class Populator
{

protected $generator;
protected $entities = [];
protected $quantities = [];
protected $guessers = [];

public function __construct(\Faker\Generator $generator)
{
$this->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;
}
}