This repository has been archived by the owner on Dec 11, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #852 from iTechDhaval/propel2_support
Add Propel2 ORM support for faker
- Loading branch information
Showing
3 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
namespace Faker\ORM\Propel2; | ||
|
||
use \Propel\Generator\Model\PropelTypes; | ||
use \Propel\Runtime\Map\ColumnMap; | ||
|
||
class ColumnTypeGuesser | ||
{ | ||
protected $generator; | ||
|
||
/** | ||
* @param \Faker\Generator $generator | ||
*/ | ||
public function __construct(\Faker\Generator $generator) | ||
{ | ||
$this->generator = $generator; | ||
} | ||
|
||
/** | ||
* @param ColumnMap $column | ||
* @return \Closure|null | ||
*/ | ||
public function guessFormat(ColumnMap $column) | ||
{ | ||
$generator = $this->generator; | ||
if ($column->isTemporal()) { | ||
if ($column->getType() == PropelTypes::BU_DATE || $column->getType() == PropelTypes::BU_TIMESTAMP) { | ||
return function () use ($generator) { | ||
return $generator->dateTime; | ||
}; | ||
} else { | ||
return function () use ($generator) { | ||
return $generator->dateTimeAD; | ||
}; | ||
} | ||
} | ||
$type = $column->getType(); | ||
switch ($type) { | ||
case PropelTypes::BOOLEAN: | ||
case PropelTypes::BOOLEAN_EMU: | ||
return function () use ($generator) { | ||
return $generator->boolean; | ||
}; | ||
case PropelTypes::NUMERIC: | ||
case PropelTypes::DECIMAL: | ||
$size = $column->getSize(); | ||
|
||
return function () use ($generator, $size) { | ||
return $generator->randomNumber($size + 2) / 100; | ||
}; | ||
case PropelTypes::TINYINT: | ||
return function () { | ||
return mt_rand(0, 127); | ||
}; | ||
case PropelTypes::SMALLINT: | ||
return function () { | ||
return mt_rand(0, 32767); | ||
}; | ||
case PropelTypes::INTEGER: | ||
return function () { | ||
return mt_rand(0, intval('2147483647')); | ||
}; | ||
case PropelTypes::BIGINT: | ||
return function () { | ||
return mt_rand(0, intval('9223372036854775807')); | ||
}; | ||
case PropelTypes::FLOAT: | ||
return function () { | ||
return mt_rand(0, intval('2147483647'))/mt_rand(1, intval('2147483647')); | ||
}; | ||
case PropelTypes::DOUBLE: | ||
case PropelTypes::REAL: | ||
return function () { | ||
return mt_rand(0, intval('9223372036854775807'))/mt_rand(1, intval('9223372036854775807')); | ||
}; | ||
case PropelTypes::CHAR: | ||
case PropelTypes::VARCHAR: | ||
case PropelTypes::BINARY: | ||
case PropelTypes::VARBINARY: | ||
$size = $column->getSize(); | ||
|
||
return function () use ($generator, $size) { | ||
return $generator->text($size); | ||
}; | ||
case PropelTypes::LONGVARCHAR: | ||
case PropelTypes::LONGVARBINARY: | ||
case PropelTypes::CLOB: | ||
case PropelTypes::CLOB_EMU: | ||
case PropelTypes::BLOB: | ||
return function () use ($generator) { | ||
return $generator->text; | ||
}; | ||
case PropelTypes::ENUM: | ||
$valueSet = $column->getValueSet(); | ||
|
||
return function () use ($generator, $valueSet) { | ||
return $generator->randomElement($valueSet); | ||
}; | ||
case PropelTypes::OBJECT: | ||
case PropelTypes::PHP_ARRAY: | ||
default: | ||
// no smart way to guess what the user expects here | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
<?php | ||
|
||
namespace Faker\ORM\Propel2; | ||
|
||
use \Faker\Provider\Base; | ||
use \Propel\Runtime\Map\ColumnMap; | ||
|
||
/** | ||
* Service class for populating a table through a Propel ActiveRecord class. | ||
*/ | ||
class EntityPopulator | ||
{ | ||
protected $class; | ||
protected $columnFormatters = array(); | ||
protected $modifiers = array(); | ||
|
||
/** | ||
* Class constructor. | ||
* | ||
* @param string $class A Propel ActiveRecord classname | ||
*/ | ||
public function __construct($class) | ||
{ | ||
$this->class = $class; | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
public function getClass() | ||
{ | ||
return $this->class; | ||
} | ||
|
||
public function setColumnFormatters($columnFormatters) | ||
{ | ||
$this->columnFormatters = $columnFormatters; | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getColumnFormatters() | ||
{ | ||
return $this->columnFormatters; | ||
} | ||
|
||
public function mergeColumnFormattersWith($columnFormatters) | ||
{ | ||
$this->columnFormatters = array_merge($this->columnFormatters, $columnFormatters); | ||
} | ||
|
||
/** | ||
* @param \Faker\Generator $generator | ||
* @return array | ||
*/ | ||
public function guessColumnFormatters(\Faker\Generator $generator) | ||
{ | ||
$formatters = array(); | ||
$class = $this->class; | ||
$peerClass = $class::TABLE_MAP; | ||
$tableMap = $peerClass::getTableMap(); | ||
$nameGuesser = new \Faker\Guesser\Name($generator); | ||
$columnTypeGuesser = new \Faker\ORM\Propel2\ColumnTypeGuesser($generator); | ||
foreach ($tableMap->getColumns() as $columnMap) { | ||
// skip behavior columns, handled by modifiers | ||
if ($this->isColumnBehavior($columnMap)) { | ||
continue; | ||
} | ||
if ($columnMap->isForeignKey()) { | ||
$relatedClass = $columnMap->getRelation()->getForeignTable()->getClassname(); | ||
$formatters[$columnMap->getPhpName()] = function ($inserted) use ($relatedClass) { | ||
$relatedClass = trim($relatedClass, "\\"); | ||
return isset($inserted[$relatedClass]) ? $inserted[$relatedClass][mt_rand(0, count($inserted[$relatedClass]) - 1)] : null; | ||
}; | ||
continue; | ||
} | ||
if ($columnMap->isPrimaryKey()) { | ||
continue; | ||
} | ||
if ($formatter = $nameGuesser->guessFormat($columnMap->getPhpName(), $columnMap->getSize())) { | ||
$formatters[$columnMap->getPhpName()] = $formatter; | ||
continue; | ||
} | ||
if ($formatter = $columnTypeGuesser->guessFormat($columnMap)) { | ||
$formatters[$columnMap->getPhpName()] = $formatter; | ||
continue; | ||
} | ||
} | ||
|
||
return $formatters; | ||
} | ||
|
||
/** | ||
* @param ColumnMap $columnMap | ||
* @return bool | ||
*/ | ||
protected function isColumnBehavior(ColumnMap $columnMap) | ||
{ | ||
foreach ($columnMap->getTable()->getBehaviors() as $name => $params) { | ||
$columnName = Base::toLower($columnMap->getName()); | ||
switch ($name) { | ||
case 'nested_set': | ||
$columnNames = array($params['left_column'], $params['right_column'], $params['level_column']); | ||
if (in_array($columnName, $columnNames)) { | ||
return true; | ||
} | ||
break; | ||
case 'timestampable': | ||
$columnNames = array($params['create_column'], $params['update_column']); | ||
if (in_array($columnName, $columnNames)) { | ||
return true; | ||
} | ||
break; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public function setModifiers($modifiers) | ||
{ | ||
$this->modifiers = $modifiers; | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getModifiers() | ||
{ | ||
return $this->modifiers; | ||
} | ||
|
||
public function mergeModifiersWith($modifiers) | ||
{ | ||
$this->modifiers = array_merge($this->modifiers, $modifiers); | ||
} | ||
|
||
/** | ||
* @param \Faker\Generator $generator | ||
* @return array | ||
*/ | ||
public function guessModifiers(\Faker\Generator $generator) | ||
{ | ||
$modifiers = array(); | ||
$class = $this->class; | ||
$peerClass = $class::TABLE_MAP; | ||
$tableMap = $peerClass::getTableMap(); | ||
foreach ($tableMap->getBehaviors() as $name => $params) { | ||
switch ($name) { | ||
case 'nested_set': | ||
$modifiers['nested_set'] = function ($obj, $inserted) use ($class, $generator) { | ||
if (isset($inserted[$class])) { | ||
$queryClass = $class . 'Query'; | ||
$parent = $queryClass::create()->findPk($generator->randomElement($inserted[$class])); | ||
$obj->insertAsLastChildOf($parent); | ||
} else { | ||
$obj->makeRoot(); | ||
} | ||
}; | ||
break; | ||
case 'sortable': | ||
$modifiers['sortable'] = function ($obj, $inserted) use ($class) { | ||
$maxRank = isset($inserted[$class]) ? count($inserted[$class]) : 0; | ||
$obj->insertAtRank(mt_rand(1, $maxRank + 1)); | ||
}; | ||
break; | ||
} | ||
} | ||
|
||
return $modifiers; | ||
} | ||
|
||
/** | ||
* Insert one new record using the Entity class. | ||
*/ | ||
public function execute($con, $insertedEntities) | ||
{ | ||
$obj = new $this->class(); | ||
foreach ($this->getColumnFormatters() as $column => $format) { | ||
if (null !== $format) { | ||
$obj->setByName($column, is_callable($format) ? $format($insertedEntities, $obj) : $format); | ||
} | ||
} | ||
foreach ($this->getModifiers() as $modifier) { | ||
$modifier($obj, $insertedEntities); | ||
} | ||
$obj->save($con); | ||
|
||
return $obj->getPrimaryKey(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
namespace Faker\ORM\Propel2; | ||
|
||
use Propel\Runtime\Propel; | ||
use Propel\Runtime\ServiceContainer\ServiceContainerInterface; | ||
|
||
/** | ||
* Service class for populating a database using the Propel ORM. | ||
* A Populator can populate several tables using ActiveRecord classes. | ||
*/ | ||
class Populator | ||
{ | ||
protected $generator; | ||
protected $entities = array(); | ||
protected $quantities = array(); | ||
|
||
/** | ||
* @param \Faker\Generator $generator | ||
*/ | ||
public function __construct(\Faker\Generator $generator) | ||
{ | ||
$this->generator = $generator; | ||
} | ||
|
||
/** | ||
* Add an order for the generation of $number records for $entity. | ||
* | ||
* @param mixed $entity A Propel ActiveRecord classname, or a \Faker\ORM\Propel2\EntityPopulator instance | ||
* @param int $number The number of entities to populate | ||
*/ | ||
public function addEntity($entity, $number, $customColumnFormatters = array(), $customModifiers = array()) | ||
{ | ||
if (!$entity instanceof \Faker\ORM\Propel2\EntityPopulator) { | ||
$entity = new \Faker\ORM\Propel2\EntityPopulator($entity); | ||
} | ||
$entity->setColumnFormatters($entity->guessColumnFormatters($this->generator)); | ||
if ($customColumnFormatters) { | ||
$entity->mergeColumnFormattersWith($customColumnFormatters); | ||
} | ||
$entity->setModifiers($entity->guessModifiers($this->generator)); | ||
if ($customModifiers) { | ||
$entity->mergeModifiersWith($customModifiers); | ||
} | ||
$class = $entity->getClass(); | ||
$this->entities[$class] = $entity; | ||
$this->quantities[$class] = $number; | ||
} | ||
|
||
/** | ||
* Populate the database using all the Entity classes previously added. | ||
* | ||
* @param PropelPDO $con A Propel connection object | ||
* | ||
* @return array A list of the inserted PKs | ||
*/ | ||
public function execute($con = null) | ||
{ | ||
if (null === $con) { | ||
$con = $this->getConnection(); | ||
} | ||
$isInstancePoolingEnabled = Propel::isInstancePoolingEnabled(); | ||
Propel::disableInstancePooling(); | ||
$insertedEntities = array(); | ||
$con->beginTransaction(); | ||
foreach ($this->quantities as $class => $number) { | ||
for ($i=0; $i < $number; $i++) { | ||
$insertedEntities[$class][]= $this->entities[$class]->execute($con, $insertedEntities); | ||
} | ||
} | ||
$con->commit(); | ||
if ($isInstancePoolingEnabled) { | ||
Propel::enableInstancePooling(); | ||
} | ||
|
||
return $insertedEntities; | ||
} | ||
|
||
protected function getConnection() | ||
{ | ||
// use the first connection available | ||
$class = key($this->entities); | ||
|
||
if (!$class) { | ||
throw new \RuntimeException('No class found from entities. Did you add entities to the Populator ?'); | ||
} | ||
|
||
$peer = $class::TABLE_MAP; | ||
|
||
return Propel::getConnection($peer::DATABASE_NAME, ServiceContainerInterface::CONNECTION_WRITE); | ||
} | ||
} |