You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

371 lines
9.1 KiB

<?php
/**
* li₃: the most RAD framework for PHP (http://li3.me)
*
* Copyright 2009, Union of RAD. All rights reserved. This source
* code is distributed under the terms of the BSD 3-Clause License.
* The full license text can be found in the LICENSE.txt file.
*/
namespace lithium\test;
use lithium\core\ConfigException;
use lithium\core\Libraries;
class Fixture extends \lithium\data\Schema {
/**
* Classes used by `Fixture`.
*
* @var array
*/
protected $_classes = [
'connections' => 'lithium\data\Connections',
'schema' => 'lithium\data\Schema',
'query' => 'lithium\data\model\Query'
];
/**
* Auto configuration properties.
*
* @var array
*/
protected $_autoConfig = [
'connection', 'source', 'locked', 'model', 'fields' => 'merge', 'meta', 'records'
];
/**
* The connection name
*
* @var string
*/
protected $_connection = null;
/**
* The name of the source. Used if `Fixture::_model` is not set (i.e. `null`).
*
* @var string
*/
protected $_source = null;
/**
* The fully-namespaced attached model class name
*
* @var string
*/
protected $_model = null;
/**
* Fields definition
*
* Example:
* {{{
* protected $_fields = array(
* 'id' => array('type' => 'id'),
* 'firstname' => array('type' => 'string', 'default' => 'foo', 'null' => false),
* 'lastname' => array('type' => 'string', 'default' => 'bar', 'null' => false)
* );
* }}}
*
* @var array
*/
protected $_fields = [];
/**
* Altered fields definition
*
* @var array
*/
protected $_alteredFields = [];
/**
* Metas for the fixture.
*
* Example:
* {{{
* protected $_meta = array(
* 'constraints' => array(
* array(
* 'type' => 'foreign_key',
* 'column' => 'id',
* 'toColumn' => 'id',
* 'to' => 'other_table'
* )
* ),
* 'table' => array('charset' => 'utf8', 'engine' => 'InnoDB')
* );
* }}}
*
* @var array
*/
protected $_meta = [];
/**
* The records should be an array of rows. Each row should have values keyed by
* the column name.
*
* Example:
* {{{
* protected $_records = array(
* array('id' => 1, 'firstname' => 'John', 'lastname' => 'Doe'),
* array('id' => 2, 'firstname' => 'Pamela', 'lastname' => 'A.'),
* array('id' => 6, 'firstname' => 'Jay', 'lastname' => 'Miner'),
* array('id' => 9, 'firstname' => 'Obi-Wan', 'lastname' => 'Kenobi')
* );
* }}}
*
* @var array
*/
protected $_records = [];
/**
* If `true` only fields in `Fixture::_fields` are allowed in records (kind of whitelist).
*
* @var boolean
*/
protected $_locked = null;
/**
* Initializes class configuration (`$_config`), and assigns object properties using the
* `_init()` method, unless otherwise specified by configuration. See below for details.
*
* @see lithium\core\Object::__construct()
* @param array $config The configuration options
*/
public function __construct($config = []) {
parent::__construct($config + ['alters' => []]);
}
/**
* Initializer function called by the constructor unless the constructor
*
* @see lithium\core\Object::_init()
* @throws ConfigException
*/
protected function _init() {
parent::_init();
if (!$this->_connection) {
throw new ConfigException("The `'connection'` option must be set.");
}
if (!$this->_source && !$this->_model) {
throw new ConfigException("The `'model'` or `'source'` option must be set.");
}
$connections = $this->_classes['connections'];
$db = $connections::get($this->_connection);
if ($model = $this->_model) {
$model::config(['meta' => ['connection' => $this->_connection]]);
$this->_source = $this->_source ? : $model::meta('source');
$this->_locked = ($this->_locked === null) ? $model::meta('locked') : $this->_locked;
}
if ($this->_locked === null) {
if ($db::enabled('schema')) {
$this->_locked = true;
} else {
$this->_locked = false;
}
}
foreach ($this->_config['alters'] as $mode => $values) {
foreach ($values as $key => $value) {
$this->alter($mode, $key, $value);
}
}
}
/**
* Create the fixture's schema.
*
* @return boolean Returns `true` on success or if there are no records to import,
* return `false` on failure.
*/
public function create($drop = true) {
return $this->_create($drop, false);
}
/**
* Create the fixture's schema and import records.
*
* @return boolean Returns `true` on success or if there are no records to import,
* return `false` on failure.
*/
public function save($drop = true) {
return $this->_create($drop, true);
}
/**
* Create the fixture's schema and import records.
*
* @param boolean $drop If `true` drop the fixture before creating it
* @param boolean $load If `true` load fixture's records
* @return boolean True on success, false on failure
*/
public function _create($drop = true, $save = true) {
$connections = $this->_classes['connections'];
$db = $connections::get($this->_connection);
if ($drop && !$this->drop()) {
return false;
}
$this->_alteredFields = $this->_alterFields($this->_fields);
$return = true;
if ($db::enabled('schema')) {
$schema = Libraries::instance(null, 'schema', [
'fields' => $this->_alteredFields,
'meta' => $this->_meta,
'locked' => $this->_locked
], $this->_classes);
$return = $db->createSchema($this->_source, $schema);
}
if ($return && $save) {
foreach ($this->_records as $record) {
if (!$this->populate($record, true)) {
return false;
}
}
}
return $return;
}
/**
* Drop table for this fixture.
*
* @param boolean $soft If `true` and there's no existing schema, no drop query is generated.
* @return boolean True on success, false on failure
*/
public function drop($soft = true) {
$connections = $this->_classes['connections'];
$db = $connections::get($this->_connection);
if (!$db::enabled('schema')) {
return $this->truncate();
}
if ($soft && $db::enabled('sources')) {
$sources = $db->sources();
if(!in_array($this->_source, $sources)) {
return true;
}
}
return $db->dropSchema($this->_source);
}
/**
* Populate custom records in the database.
*
* @param array $record The data of the record
* @param boolean $alter If true, the `$record` will be altered according the alter rules.
* @return boolean Returns `true` on success `false` otherwise.
*/
public function populate(array $record = [], $alter = true) {
if (!$record) {
return true;
}
$connections = $this->_classes['connections'];
$db = $connections::get($this->_connection);
$data = $alter ? $this->_alterRecord($record) : $record;
if ($this->_locked) {
$data = array_intersect_key($data, $this->_alteredFields);
}
$options = [
'type' => 'create', 'source' => $this->_source, 'data' => ['data' => $data]
];
$query = Libraries::instance(null, 'query', $options, $this->_classes);
return $db->create($query);
}
/**
* Truncates the current fixture.
*
* @param boolean $soft If `true` and there's no existing schema, no truncate is generated.
* @return boolean
*/
public function truncate($soft = true) {
$connections = $this->_classes['connections'];
$db = $connections::get($this->_connection);
if ($soft && $db::enabled('sources')) {
$sources = $db->sources();
if(!in_array($this->_source, $sources)) {
return true;
}
}
$options = ['source' => $this->_source];
$query = Libraries::instance(null, 'query', $options, $this->_classes);
return $db->delete($query);
}
public function alter($mode = null, $key = null, $value = []) {
if ($mode === null) {
return $this->_config['alters'];
}
if ($key && $mode === 'drop') {
$this->_config['alters']['drop'][] = $key;
return;
}
if ($key && $value) {
$this->_config['alters'][$mode][$key] = $value;
}
}
/**
* Apply the configured value mapping.
*
* @param array $record The record array.
* @return array Returns the modified record.
*/
public function _alterRecord(array $record = []) {
$result = [];
foreach ($record as $name => $value) {
if (isset($this->_config['alters']['change'][$name])) {
$alter = $this->_config['alters']['change'][$name];
if (isset($alter['value'])) {
$function = $alter['value'];
$value = $function($record[$name]);
} else {
$value = $record[$name];
}
if (isset($alter['to'])) {
$result[$alter['to']] = $value;
} else {
$result[$name] = $value;
}
} else {
$result[$name] = $value;
}
}
return $result;
}
public function _alterFields(array $fields = []) {
foreach ($this->_config['alters'] as $mode => $values) {
foreach ($values as $key => $value) {
switch($mode) {
case 'add':
$fields[$key] = $value;
break;
case 'change':
if (isset($fields[$key]) && isset($value['to'])) {
$field = $fields[$key];
unset($fields[$key]);
$to = $value['to'];
unset($value['to']);
unset($value['value']);
$fields[$to] = $value + $field;
}
break;
case 'drop':
unset($fields[$value]);
break;
}
}
}
return $fields;
}
}
?>