2012-12-07 18:44:00 +01:00
|
|
|
<?php
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
namespace SilverStripe\Dev;
|
|
|
|
|
2016-06-15 06:03:16 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLInsert;
|
|
|
|
use SilverStripe\ORM\DB;
|
|
|
|
use SilverStripe\ORM\DataObject;
|
|
|
|
use SilverStripe\ORM\Queries\SQLDelete;
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Core\Injector\Injector;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
|
2012-12-07 18:44:00 +01:00
|
|
|
/**
|
|
|
|
* Manages a set of database fixtures for {@link DataObject} records
|
|
|
|
* as well as raw database table rows.
|
|
|
|
*
|
|
|
|
* Delegates creation of objects to {@link FixtureBlueprint},
|
|
|
|
* which can implement class- and use-case specific fixture setup.
|
|
|
|
*
|
|
|
|
* Supports referencing model relations through a specialized syntax:
|
|
|
|
* <code>
|
|
|
|
* $factory = new FixtureFactory();
|
|
|
|
* $relatedObj = $factory->createObject(
|
2016-01-06 00:34:58 +01:00
|
|
|
* 'MyRelatedClass',
|
2012-12-07 18:44:00 +01:00
|
|
|
* 'relation1'
|
|
|
|
* );
|
|
|
|
* $obj = $factory->createObject(
|
2016-01-06 00:34:58 +01:00
|
|
|
* 'MyClass',
|
2012-12-07 18:44:00 +01:00
|
|
|
* 'object1'
|
|
|
|
* array('MyRelationName' => '=>MyRelatedClass.relation1')
|
|
|
|
* );
|
|
|
|
* </code>
|
|
|
|
* Relation loading is order dependant.
|
|
|
|
*/
|
|
|
|
class FixtureFactory {
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2012-12-07 18:44:00 +01:00
|
|
|
/**
|
|
|
|
* @var array Array of fixture items, keyed by class and unique identifier,
|
|
|
|
* with values being the generated database ID. Does not store object instances.
|
|
|
|
*/
|
|
|
|
protected $fixtures = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array Callbacks
|
|
|
|
*/
|
|
|
|
protected $blueprints = array();
|
|
|
|
|
|
|
|
/**
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $name Unique name for this blueprint
|
2012-12-07 18:44:00 +01:00
|
|
|
* @param array|FixtureBlueprint $defaults Array of default values, or a blueprint instance
|
2016-08-19 00:51:35 +02:00
|
|
|
* @return $this
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function define($name, $defaults = array()) {
|
|
|
|
if($defaults instanceof FixtureBlueprint) {
|
|
|
|
$this->blueprints[$name] = $defaults;
|
|
|
|
} else {
|
|
|
|
$class = $name;
|
|
|
|
$this->blueprints[$name] = Injector::inst()->create(
|
2016-08-19 00:51:35 +02:00
|
|
|
'SilverStripe\\Dev\\FixtureBlueprint', $name, $class, $defaults
|
2012-12-07 18:44:00 +01:00
|
|
|
);
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2012-12-07 18:44:00 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes the fixture into the database using DataObjects
|
|
|
|
*
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $name Name of the {@link FixtureBlueprint} to use,
|
2012-12-07 18:44:00 +01:00
|
|
|
* usually a DataObject subclass.
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $identifier Unique identifier for this fixture type
|
|
|
|
* @param array $data Map of properties. Overrides default data.
|
2012-12-07 18:44:00 +01:00
|
|
|
* @return DataObject
|
|
|
|
*/
|
|
|
|
public function createObject($name, $identifier, $data = null) {
|
|
|
|
if(!isset($this->blueprints[$name])) {
|
|
|
|
$this->blueprints[$name] = new FixtureBlueprint($name);
|
|
|
|
}
|
|
|
|
$blueprint = $this->blueprints[$name];
|
|
|
|
$obj = $blueprint->createObject($identifier, $data, $this->fixtures);
|
|
|
|
$class = $blueprint->getClass();
|
|
|
|
|
|
|
|
if(!isset($this->fixtures[$class])) {
|
|
|
|
$this->fixtures[$class] = array();
|
|
|
|
}
|
|
|
|
$this->fixtures[$class][$identifier] = $obj->ID;
|
|
|
|
|
|
|
|
return $obj;
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2012-12-07 18:44:00 +01:00
|
|
|
/**
|
|
|
|
* Writes the fixture into the database directly using a database manipulation.
|
|
|
|
* Does not use blueprints. Only supports tables with a primary key.
|
|
|
|
*
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $table Existing database table name
|
|
|
|
* @param string $identifier Unique identifier for this fixture type
|
|
|
|
* @param array $data Map of properties
|
|
|
|
* @return int Database identifier
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function createRaw($table, $identifier, $data) {
|
2013-06-21 00:32:08 +02:00
|
|
|
$fields = array();
|
|
|
|
foreach($data as $fieldName => $fieldVal) {
|
2014-07-14 23:31:03 +02:00
|
|
|
$fields["\"{$fieldName}\""] = $this->parseValue($fieldVal);
|
2012-12-07 18:44:00 +01:00
|
|
|
}
|
2014-07-14 23:31:03 +02:00
|
|
|
$insert = new SQLInsert("\"{$table}\"", $fields);
|
2013-06-21 00:32:08 +02:00
|
|
|
$insert->execute();
|
|
|
|
$id = DB::get_generated_id($table);
|
2012-12-07 18:44:00 +01:00
|
|
|
$this->fixtures[$table][$identifier] = $id;
|
|
|
|
|
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the ID of an object from the fixture.
|
2016-08-19 00:51:35 +02:00
|
|
|
*
|
|
|
|
* @param string $class The data class, as specified in your fixture file. Parent classes won't work
|
|
|
|
* @param string $identifier The identifier string, as provided in your fixture file
|
|
|
|
* @return int
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function getId($class, $identifier) {
|
|
|
|
if(isset($this->fixtures[$class][$identifier])) {
|
|
|
|
return $this->fixtures[$class][$identifier];
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2012-12-07 18:44:00 +01:00
|
|
|
/**
|
|
|
|
* Return all of the IDs in the fixture of a particular class name.
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $class
|
|
|
|
* @return array|false A map of fixture-identifier => object-id
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function getIds($class) {
|
|
|
|
if(isset($this->fixtures[$class])) {
|
|
|
|
return $this->fixtures[$class];
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $class
|
|
|
|
* @param string $identifier
|
|
|
|
* @param int $databaseId
|
|
|
|
* @return $this
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function setId($class, $identifier, $databaseId) {
|
|
|
|
$this->fixtures[$class][$identifier] = $databaseId;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an object from the fixture.
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $class The data class, as specified in your fixture file. Parent classes won't work
|
|
|
|
* @param string $identifier The identifier string, as provided in your fixture file
|
|
|
|
* @return DataObject
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function get($class, $identifier) {
|
|
|
|
$id = $this->getId($class, $identifier);
|
2016-08-19 00:51:35 +02:00
|
|
|
if(!$id) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return DataObject::get_by_id($class, $id);
|
2012-12-07 18:44:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-08-19 00:51:35 +02:00
|
|
|
* @return array Map of class names, containing a map of in-memory identifiers
|
2012-12-07 18:44:00 +01:00
|
|
|
* mapped to database identifiers.
|
|
|
|
*/
|
|
|
|
public function getFixtures() {
|
|
|
|
return $this->fixtures;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove all fixtures previously defined through {@link createObject()}
|
|
|
|
* or {@link createRaw()}, both from the internal fixture mapping and the database.
|
|
|
|
* If the $class argument is set, limit clearing to items of this class.
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $limitToClass
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function clear($limitToClass = null) {
|
|
|
|
$classes = ($limitToClass) ? array($limitToClass) : array_keys($this->fixtures);
|
|
|
|
foreach($classes as $class) {
|
|
|
|
$ids = $this->fixtures[$class];
|
|
|
|
foreach($ids as $id => $dbId) {
|
|
|
|
if(class_exists($class)) {
|
|
|
|
$class::get()->byId($dbId)->delete();
|
|
|
|
} else {
|
|
|
|
$table = $class;
|
2013-06-21 00:32:08 +02:00
|
|
|
$delete = new SQLDelete("\"$table\"", array(
|
|
|
|
"\"$table\".\"ID\"" => $dbId
|
2012-12-07 18:44:00 +01:00
|
|
|
));
|
2013-06-21 00:32:08 +02:00
|
|
|
$delete->execute();
|
2012-12-07 18:44:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
unset($this->fixtures[$class][$id]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-08-19 00:51:35 +02:00
|
|
|
* @return array Of {@link FixtureBlueprint} instances
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function getBlueprints() {
|
|
|
|
return $this->blueprints;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param String $name
|
2016-08-19 00:51:35 +02:00
|
|
|
* @return FixtureBlueprint|false
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
public function getBlueprint($name) {
|
|
|
|
return (isset($this->blueprints[$name])) ? $this->blueprints[$name] : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-06 00:34:58 +01:00
|
|
|
* Parse a value from a fixture file. If it starts with =>
|
2012-12-07 18:44:00 +01:00
|
|
|
* it will get an ID from the fixture dictionary
|
|
|
|
*
|
2016-08-19 00:51:35 +02:00
|
|
|
* @param string $value
|
|
|
|
* @return string Fixture database ID, or the original value
|
2012-12-07 18:44:00 +01:00
|
|
|
*/
|
|
|
|
protected function parseValue($value) {
|
|
|
|
if(substr($value,0,2) == '=>') {
|
|
|
|
// Parse a dictionary reference - used to set foreign keys
|
|
|
|
list($class, $identifier) = explode('.', substr($value,2), 2);
|
|
|
|
|
|
|
|
if($this->fixtures && !isset($this->fixtures[$class][$identifier])) {
|
|
|
|
throw new InvalidArgumentException(sprintf(
|
|
|
|
'No fixture definitions found for "%s"',
|
|
|
|
$value
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->fixtures[$class][$identifier];
|
|
|
|
} else {
|
|
|
|
// Regular field value setting
|
|
|
|
return $value;
|
|
|
|
}
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2014-08-18 05:49:32 +02:00
|
|
|
}
|