Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jails committed Dec 14, 2012
0 parents commit e02f83d
Show file tree
Hide file tree
Showing 9 changed files with 1,354 additions and 0 deletions.
25 changes: 25 additions & 0 deletions LICENSE.txt
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright (c) 2012, Union of RAD http://union-of-rad.org
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Lithium, Union of Rad, nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
185 changes: 185 additions & 0 deletions README.md
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,185 @@
# Database schema managment

## Requirement

PHP 5.4

## Installation

Checkout the code to either of your library directories:

cd libraries
git clone git@github.com:UnionOfRAD/li3_fixtures.git

Include the library in in your `/app/config/bootstrap/libraries.php`

Libraries::add('li3_fixtures');

## Presentation

Thin plugin provide fixtures managment over connections. Should work with any kind of `Source` adapaters.

## Dependencies

This plugin needs [li3_sqltools](https://github.com/UnionOfRAD/li3_sqltools) if you want to make it work with li3's `Database` adapters. For schema less datasources, adapters must return false on `::enabled('schema')` call.

## API

### The Fixture class

Methods:
--------

- Fixture::create($safe); //Create the source only
- Fixture::save($safe); //Create the source + save the fixture's records in.
- Fixture::drop($safe); //Drop the source.
- Fixture::populate($records); //Insert a record in the database
- Fixture::alter($mode, $fieldname, $value); //Altering the schema before `::create()/::save()`.

Simple example of unit test:

//app/tests/cases/models/SampleTest.php
namespace app\tests\cases\models;

use li3_fixtures\test\Fixture;

class SampleTest extends \lithium\test\Unit {

public function testFixture() {
$fixture = new Fixture(array(
'connection' => 'lithium_mysql_test',
'source' => 'contacts',
'fields' => array(
'id' => array('type' => 'id'),
'name' => array('type' => 'string')
),
'records' => array(
array('id' => 1, 'name' => 'Nate'),
array('id' => 2, 'name' => 'Gwoo')
)
));

$fixture->save();

$fixture->populate(array('id' => 3, 'name' => 'Mehlah'));

$fixture->drop();
}
}

### The Fixtures class

`Fixture` is a kind of `Schema` which contain records and a source name or a reference to a model.
So let save the above fixture in a class.

//app/tests/fixture/ContactsFixture.php
namespace app\tests\fixture;

class ContactsFixture extends \li3_fixtures\test\Fixture {

protected $_model = 'app\models\Contacts';

protected $_fields = array(
'id' => array('type' => 'id'),
'name' => array('type' => 'string')
);

protected $_records = array(
array('id' => 1, 'name' => 'Nate'),
array('id' => 2, 'name' => 'Gwoo')
);
}

//app/models/Contact.php
namespace app\models;

class Contacts extends \lithium\data\Model {
}

If you have numbers of fixtures, it will be interesting to use the `Fixtures` class.

Example of use case:

//app/tests/integration/Sample2Test.php
namespace app\tests\integration;

use li3_fixtures\test\Fixtures;
use app\models\Contacts;
use app\models\Images;
// and so on...

class Sample2Test extends \lithium\test\Unit {

public function setUp() {
Fixtures::config(array(
'db' => array(
'adapter' => 'Connection',
'connection' => 'lithium_mysql_test',
'fixtures' => array(
'contacts' => 'app\tests\fixture\ContactsFixture',
'images' => 'app\tests\fixture\ImagesFixture'
// and so on...
)
)
));
Fixtures::save('db');
}

public function tearDown() {
Fixtures::reset('db');
}

public function testFixture() {
var_export(Contacts::find('all')->data());
var_export(Images::find('all')->data());
}
}

Ok so why it's better to set the `Fixture::_model` instead of `Fixture::_source` ? Long story short,
models had their own meta `'connection'` value. If a fixture is "linked" with a model, it will
automagically configure its meta `'connection'` to the fixture's connection when is created or saved.

Example:

Fixtures::save('db', array('contacts'));
Contacts::config(array('meta' => array('connection' => 'test'))); //This is not needed

### Advanced use case

For interoperability, sometimes it's usefull to adjust fixtures according a datasources.

You can alter `Fixture`'s instance before creating it like the following use case:

$fixture->alter('add', array(
'name' => 'enabled',
'type' => 'boolean'
); //Add a field

$fixture->alter('change', array(
'name' => 'published',
'value' => function ($val) {
return new MongoDate(strtotime($val));
}
); //Simple cast for fixture's values according the closure

$fixture->alter('change', array(
'name' => 'id',
'to' => '_id',
'value' => function ($val) {
return new MongoId('4c3628558ead0e594' . (string) ($val + 1000000));
}
); //Renaming the field 'id' to '_id' + cast fixture's values according the closure

$fixture->alter('change', array(
'name' => 'bigintger',
'type' => 'integer',
'use' => 'bigint' //use db specific type
); //Modifing a field type

$fixture->alter('drop', 'bigintger'); //Simply dropping a field

Note :

You can recover a specific fixture's instance from `Fixtures` using:

$fixture = Fixtures::get('db', 'contacts');
184 changes: 184 additions & 0 deletions extensions/adapter/test/fixtures/Connection.php
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,184 @@
<?php
/**
* Lithium: the most rad php framework
*
* @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
* @license http://opensource.org/licenses/bsd-license.php The BSD License
*/

namespace li3_fixtures\extensions\adapter\test\fixtures;

use lithium\core\ConfigException;
use UnexpectedValueException;

class Connection extends \lithium\core\Object {

/**
* Auto configuration properties.
*
* @var array
*/
protected $_autoConfig = array('connection', 'fixtures');

/**
* Connection name to use.
*
* @var string
*/
protected $_connection = null;

/**
* Holds the fixture classes that where instantiated.
*
* @var array
*/
protected $_loaded = array();

/**
* Holds the fixture classes to be instantiated indexed by a name.
*
* @var array
*/
protected $_fixtures = array();

/**
* 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(array $config = array()) {
$defaults = array('alters' => array());
parent::__construct($config + $defaults);
}

/**
* 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.");
}
}

/**
* Instantiate a fixture
*
* @param array $name The fixture name to instanciate
* @return boolean Returns `true` on success
*/
protected function _instantiate($name) {
if (isset($this->_fixtures[$name])) {
$options = array(
'connection' => $this->_connection,
'alters' => $this->_config['alters']
);
$this->_loaded[$name] = new $this->_fixtures[$name]($options);
return true;
} else {
throw new UnexpectedValueException("Undefined fixture named: `{$name}`.");
}
}

/**
* Getting a fixture.
*
* @param mixed $name The fixture name to get.
* @return mixed Returns a fixture object or `null` if doesn't exists.
*/
public function get($name) {
if (isset($this->_loaded[$name]) || $this->_instantiate($name)) {
return $this->_loaded[$name];
}
}

/**
* Creates the schema of fixtures
*
* @param mixed $names An array of fixture name.
* @param boolean $drop If `true` drop the fixture before creating it
*/
public function create(array $names = array(), $drop = true) {
$this->_create($names, $drop, false);
}

/**
* Creates the fixtures tables and inserts data on them
*
* @param mixed $names An array of fixture name.
* @param boolean $drop If `true` drop the fixture before loading it
*/
public function save(array $names = array(), $drop = true) {
$this->_create($names, $drop, true);
}

/**
* Build fixtures
*
* @param mixed $names An array of fixture name.
* @param boolean $drop If `true` drop the fixture before creating it
* @param boolean $save If `true` save fixture's records in the database
*/
protected function _create($names = array(), $drop = true, $save = true) {
$names = $names ?: array_keys($this->_fixtures);

foreach ((array) $names as $name) {
if (isset($this->_loaded[$name]) || $this->_instantiate($name)) {
$fixture = $this->_loaded[$name];
if ($save) {
$fixture->save($drop);
} else {
$fixture->create($drop);
}
}
}
}

/**
* Trucantes the fixtures tables
*
* @param mixed $names The fixtures name to truncate.
*/
public function truncate(array $names = array()) {
$names = $names ?: array_keys($this->_loaded);
foreach ($names as $name) {
$fixture = $this->get($name);
$fixture->truncate();
}
}

/**
* Drop all fixture tables loaded by this class.
*
* @param array $names The fixtures name to drop.
* @param boolean $safe If `true` drop the fixture only if exists
*/
public function drop(array $names = array(), $safe = true) {
$names = $names ?: array_keys($this->_loaded);
foreach ($names as $name) {
$fixture = $this->get($name);
$fixture->drop($safe);
}
}

/**
* Drop all fixture tables loaded by this class.
*
* @param array $names The fixtures name to drop.
*/
public function reset() {
foreach ($this->_loaded as $name => $fixture) {
$fixture->drop();
}
$this->_loaded = array();
$this->_config = array();
}
}

?>
Loading

0 comments on commit e02f83d

Please sign in to comment.