mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
d8e9af8af8
Database abstraction broken up into controller, connector, query builder, and schema manager, each independently configurable via YAML / Injector Creation of new DBQueryGenerator for database specific generation of SQL Support for parameterised queries, move of code base to use these over escaped conditions Refactor of SQLQuery into separate query classes for each of INSERT UPDATE DELETE and SELECT Support for PDO Installation process upgraded to use new ORM SS_DatabaseException created to handle database errors, maintaining details of raw sql and parameter details for user code designed interested in that data. Renamed DB static methods to conform correctly to naming conventions (e.g. DB::getConn -> DB::get_conn) 3.2 upgrade docs Performance Optimisation and simplification of code to use more concise API API Ability for database adapters to register extensions to ConfigureFromEnv.php
470 lines
14 KiB
PHP
470 lines
14 KiB
PHP
<?php
|
|
|
|
class DataExtensionTest extends SapphireTest {
|
|
protected static $fixture_file = 'DataExtensionTest.yml';
|
|
|
|
protected $extraDataObjects = array(
|
|
'DataExtensionTest_Member',
|
|
'DataExtensionTest_Player',
|
|
'DataExtensionTest_RelatedObject',
|
|
'DataExtensionTest_MyObject',
|
|
'DataExtensionTest_CMSFieldsBase',
|
|
'DataExtensionTest_CMSFieldsChild',
|
|
'DataExtensionTest_CMSFieldsGrandchild'
|
|
);
|
|
|
|
protected $requiredExtensions = array(
|
|
'DataObject' => array( 'DataExtensionTest_AppliedToDO' ),
|
|
);
|
|
|
|
public function testOneToManyAssociationWithExtension() {
|
|
$contact = new DataExtensionTest_Member();
|
|
$contact->Website = "http://www.example.com";
|
|
|
|
$object = new DataExtensionTest_RelatedObject();
|
|
$object->FieldOne = "Lorem ipsum dolor";
|
|
$object->FieldTwo = "Random notes";
|
|
|
|
// The following code doesn't currently work:
|
|
// $contact->RelatedObjects()->add($object);
|
|
// $contact->write();
|
|
|
|
// Instead we have to do the following
|
|
$contact->write();
|
|
$object->ContactID = $contact->ID;
|
|
$object->write();
|
|
|
|
$contactID = $contact->ID;
|
|
unset($contact);
|
|
unset($object);
|
|
|
|
$contact = DataObject::get_one("DataExtensionTest_Member", array(
|
|
'"DataExtensionTest_Member"."Website"' => 'http://www.example.com'
|
|
));
|
|
$object = DataObject::get_one('DataExtensionTest_RelatedObject', array(
|
|
'"DataExtensionTest_RelatedObject"."ContactID"' => $contactID
|
|
));
|
|
|
|
$this->assertNotNull($object, 'Related object not null');
|
|
$this->assertInstanceOf('DataExtensionTest_Member', $object->Contact(),
|
|
'Related contact is a member dataobject');
|
|
$this->assertInstanceOf('DataExtensionTest_Member', $object->getComponent('Contact'),
|
|
'getComponent does the same thing as Contact()');
|
|
|
|
$this->assertInstanceOf('DataExtensionTest_RelatedObject', $contact->RelatedObjects()->First());
|
|
$this->assertEquals("Lorem ipsum dolor", $contact->RelatedObjects()->First()->FieldOne);
|
|
$this->assertEquals("Random notes", $contact->RelatedObjects()->First()->FieldTwo);
|
|
$contact->delete();
|
|
}
|
|
|
|
public function testManyManyAssociationWithExtension() {
|
|
$parent = new DataExtensionTest_MyObject();
|
|
$parent->Title = 'My Title';
|
|
$parent->write();
|
|
|
|
$this->assertEquals(0, $parent->Faves()->Count());
|
|
|
|
$obj1 = $this->objFromFixture('DataExtensionTest_RelatedObject', 'obj1');
|
|
$obj2 = $this->objFromFixture('DataExtensionTest_RelatedObject', 'obj2');
|
|
|
|
$parent->Faves()->add($obj1->ID);
|
|
$this->assertEquals(1, $parent->Faves()->Count());
|
|
|
|
$parent->Faves()->add($obj2->ID);
|
|
$this->assertEquals(2, $parent->Faves()->Count());
|
|
|
|
$parent->Faves()->removeByID($obj2->ID);
|
|
$this->assertEquals(1, $parent->Faves()->Count());
|
|
}
|
|
|
|
/**
|
|
* Test {@link Object::add_extension()} has loaded DataExtension statics correctly.
|
|
*/
|
|
public function testAddExtensionLoadsStatics() {
|
|
// Object::add_extension() will load DOD statics directly, so let's try adding a extension on the fly
|
|
DataExtensionTest_Player::add_extension('DataExtensionTest_PlayerExtension');
|
|
|
|
// Now that we've just added the extension, we need to rebuild the database
|
|
$this->resetDBSchema(true);
|
|
|
|
// Create a test record with extended fields, writing to the DB
|
|
$player = new DataExtensionTest_Player();
|
|
$player->setField('Name', 'Joe');
|
|
$player->setField('DateBirth', '1990-5-10');
|
|
$player->Address = '123 somewhere street';
|
|
$player->write();
|
|
|
|
unset($player);
|
|
|
|
// Pull the record out of the DB and examine the extended fields
|
|
$player = DataObject::get_one('DataExtensionTest_Player', array(
|
|
'"DataExtensionTest_Player"."Name"' => 'Joe'
|
|
));
|
|
$this->assertEquals($player->DateBirth, '1990-05-10');
|
|
$this->assertEquals($player->Address, '123 somewhere street');
|
|
$this->assertEquals($player->Status, 'Goalie');
|
|
}
|
|
|
|
/**
|
|
* Test that DataObject::$api_access can be set to true via a extension
|
|
*/
|
|
public function testApiAccessCanBeExtended() {
|
|
$this->assertTrue(Config::inst()->get('DataExtensionTest_Member', 'api_access', Config::FIRST_SET));
|
|
}
|
|
|
|
public function testPermissionExtension() {
|
|
// testing behaviour in isolation, too many sideeffects and other checks
|
|
// in SiteTree->can*() methods to test one single feature reliably with them
|
|
|
|
$obj = $this->objFromFixture('DataExtensionTest_MyObject', 'object1');
|
|
$websiteuser = $this->objFromFixture('Member', 'websiteuser');
|
|
$admin = $this->objFromFixture('Member', 'admin');
|
|
|
|
$this->assertFalse(
|
|
$obj->canOne($websiteuser),
|
|
'Both extensions return true, but original method returns false'
|
|
);
|
|
|
|
$this->assertFalse(
|
|
$obj->canTwo($websiteuser),
|
|
'One extension returns false, original returns true, but extension takes precedence'
|
|
);
|
|
|
|
$this->assertTrue(
|
|
$obj->canThree($admin),
|
|
'Undefined extension methods returning NULL dont influence the original method'
|
|
);
|
|
|
|
}
|
|
|
|
public function testPopulateDefaults() {
|
|
$obj = new DataExtensionTest_Member();
|
|
$this->assertEquals(
|
|
$obj->Phone,
|
|
'123',
|
|
'Defaults can be populated through extension'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test that DataObject::dbObject() works for fields applied by a extension
|
|
*/
|
|
public function testDbObjectOnExtendedFields() {
|
|
$member = $this->objFromFixture('DataExtensionTest_Member', 'member1');
|
|
$this->assertNotNull($member->dbObject('Website'));
|
|
$this->assertInstanceOf('Varchar', $member->dbObject('Website'));
|
|
}
|
|
|
|
public function testExtensionCanBeAppliedToDataObject() {
|
|
$do = new DataObject();
|
|
$mo = new DataExtensionTest_MyObject();
|
|
|
|
$this->assertTrue($do->hasMethod('testMethodApplied'));
|
|
$this->assertTrue($mo->hasMethod('testMethodApplied'));
|
|
|
|
$this->assertEquals("hello world", $mo->testMethodApplied());
|
|
$this->assertEquals("hello world", $do->testMethodApplied());
|
|
}
|
|
|
|
public function testPageFieldGeneration() {
|
|
$page = new DataExtensionTest_CMSFieldsBase();
|
|
$fields = $page->getCMSFields();
|
|
$this->assertNotEmpty($fields);
|
|
|
|
// Check basic field exists
|
|
$this->assertNotEmpty($fields->dataFieldByName('PageField'));
|
|
}
|
|
|
|
public function testPageExtensionsFieldGeneration() {
|
|
$page = new DataExtensionTest_CMSFieldsBase();
|
|
$fields = $page->getCMSFields();
|
|
$this->assertNotEmpty($fields);
|
|
|
|
// Check extending fields exist
|
|
$this->assertNotEmpty($fields->dataFieldByName('ExtendedFieldRemove')); // Not removed yet!
|
|
$this->assertNotEmpty($fields->dataFieldByName('ExtendedFieldKeep'));
|
|
}
|
|
|
|
public function testSubpageFieldGeneration() {
|
|
$page = new DataExtensionTest_CMSFieldsChild();
|
|
$fields = $page->getCMSFields();
|
|
$this->assertNotEmpty($fields);
|
|
|
|
// Check extending fields exist
|
|
$this->assertEmpty($fields->dataFieldByName('ExtendedFieldRemove')); // Removed by child class
|
|
$this->assertNotEmpty($fields->dataFieldByName('ExtendedFieldKeep'));
|
|
$this->assertNotEmpty($preExtendedField = $fields->dataFieldByName('ChildFieldBeforeExtension'));
|
|
$this->assertEquals($preExtendedField->Title(), 'ChildFieldBeforeExtension: Modified Title');
|
|
|
|
// Post-extension fields
|
|
$this->assertNotEmpty($fields->dataFieldByName('ChildField'));
|
|
}
|
|
|
|
public function testSubSubpageFieldGeneration() {
|
|
$page = new DataExtensionTest_CMSFieldsGrandchild();
|
|
$fields = $page->getCMSFields();
|
|
$this->assertNotEmpty($fields);
|
|
|
|
// Check extending fields exist
|
|
$this->assertEmpty($fields->dataFieldByName('ExtendedFieldRemove')); // Removed by child class
|
|
$this->assertNotEmpty($fields->dataFieldByName('ExtendedFieldKeep'));
|
|
|
|
// Check child fields removed by grandchild in beforeUpdateCMSFields
|
|
$this->assertEmpty($fields->dataFieldByName('ChildFieldBeforeExtension')); // Removed by grandchild class
|
|
|
|
// Check grandchild field modified by extension
|
|
$this->assertNotEmpty($preExtendedField = $fields->dataFieldByName('GrandchildFieldBeforeExtension'));
|
|
$this->assertEquals($preExtendedField->Title(), 'GrandchildFieldBeforeExtension: Modified Title');
|
|
|
|
// Post-extension fields
|
|
$this->assertNotEmpty($fields->dataFieldByName('ChildField'));
|
|
$this->assertNotEmpty($fields->dataFieldByName('GrandchildField'));
|
|
}
|
|
}
|
|
|
|
class DataExtensionTest_Member extends DataObject implements TestOnly {
|
|
|
|
private static $db = array(
|
|
"Name" => "Varchar",
|
|
"Email" => "Varchar"
|
|
);
|
|
|
|
}
|
|
|
|
class DataExtensionTest_Player extends DataObject implements TestOnly {
|
|
|
|
private static $db = array(
|
|
'Name' => 'Varchar'
|
|
);
|
|
|
|
}
|
|
|
|
class DataExtensionTest_PlayerExtension extends DataExtension implements TestOnly {
|
|
|
|
public static function get_extra_config($class = null, $extensionClass = null, $args = null) {
|
|
$config = array();
|
|
|
|
// Only add these extensions if the $class is set to DataExtensionTest_Player, to
|
|
// test that the argument works.
|
|
if($class == 'DataExtensionTest_Player') {
|
|
$config['db'] = array(
|
|
'Address' => 'Text',
|
|
'DateBirth' => 'Date',
|
|
'Status' => "Enum('Shooter,Goalie')"
|
|
);
|
|
$config['defaults'] = array(
|
|
'Status' => 'Goalie'
|
|
);
|
|
}
|
|
|
|
return $config;
|
|
}
|
|
|
|
}
|
|
|
|
class DataExtensionTest_ContactRole extends DataExtension implements TestOnly {
|
|
|
|
private static $db = array(
|
|
'Website' => 'Varchar',
|
|
'Phone' => 'Varchar(255)',
|
|
);
|
|
|
|
private static $has_many = array(
|
|
'RelatedObjects' => 'DataExtensionTest_RelatedObject'
|
|
);
|
|
|
|
private static $defaults = array(
|
|
'Phone' => '123'
|
|
);
|
|
|
|
private static $api_access = true;
|
|
|
|
}
|
|
|
|
class DataExtensionTest_RelatedObject extends DataObject implements TestOnly {
|
|
|
|
private static $db = array(
|
|
"FieldOne" => "Varchar",
|
|
"FieldTwo" => "Varchar"
|
|
);
|
|
|
|
private static $has_one = array(
|
|
"Contact" => "DataExtensionTest_Member"
|
|
);
|
|
|
|
}
|
|
|
|
DataExtensionTest_Member::add_extension('DataExtensionTest_ContactRole');
|
|
|
|
class DataExtensionTest_MyObject extends DataObject implements TestOnly {
|
|
|
|
private static $db = array(
|
|
'Title' => 'Varchar',
|
|
);
|
|
|
|
public function canOne($member = null) {
|
|
// extended access checks
|
|
$results = $this->extend('canOne', $member);
|
|
if($results && is_array($results)) if(!min($results)) return false;
|
|
|
|
return false;
|
|
}
|
|
|
|
public function canTwo($member = null) {
|
|
// extended access checks
|
|
$results = $this->extend('canTwo', $member);
|
|
if($results && is_array($results)) if(!min($results)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public function canThree($member = null) {
|
|
// extended access checks
|
|
$results = $this->extend('canThree', $member);
|
|
if($results && is_array($results)) if(!min($results)) return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class DataExtensionTest_Ext1 extends DataExtension implements TestOnly {
|
|
|
|
public function canOne($member = null) {
|
|
return true;
|
|
}
|
|
|
|
public function canTwo($member = null) {
|
|
return false;
|
|
}
|
|
|
|
public function canThree($member = null) {
|
|
}
|
|
|
|
}
|
|
|
|
class DataExtensionTest_Ext2 extends DataExtension implements TestOnly {
|
|
|
|
public function canOne($member = null) {
|
|
return true;
|
|
}
|
|
|
|
public function canTwo($member = null) {
|
|
return true;
|
|
}
|
|
|
|
public function canThree($member = null) {
|
|
}
|
|
|
|
}
|
|
|
|
class DataExtensionTest_Faves extends DataExtension implements TestOnly {
|
|
|
|
private static $many_many = array(
|
|
'Faves' => 'DataExtensionTest_RelatedObject'
|
|
);
|
|
|
|
}
|
|
|
|
class DataExtensionTest_AppliedToDO extends DataExtension implements TestOnly {
|
|
|
|
public function testMethodApplied() {
|
|
return "hello world";
|
|
}
|
|
|
|
}
|
|
|
|
DataExtensionTest_MyObject::add_extension('DataExtensionTest_Ext1');
|
|
DataExtensionTest_MyObject::add_extension('DataExtensionTest_Ext2');
|
|
DataExtensionTest_MyObject::add_extension('DataExtensionTest_Faves');
|
|
|
|
/**
|
|
* Base class for CMS fields
|
|
*/
|
|
class DataExtensionTest_CMSFieldsBase extends DataObject implements TestOnly {
|
|
|
|
private static $db = array(
|
|
'PageField' => 'Varchar(255)'
|
|
);
|
|
|
|
private static $extensions = array(
|
|
'DataExtensionTest_CMSFieldsBaseExtension'
|
|
);
|
|
|
|
public function getCMSFields() {
|
|
$fields = parent::getCMSFields();
|
|
$fields->addFieldToTab('Root.Test', new TextField('PageField'));
|
|
return $fields;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extension to top level test class, tests that updateCMSFields work
|
|
*/
|
|
class DataExtensionTest_CMSFieldsBaseExtension extends DataExtension implements TestOnly {
|
|
private static $db = array(
|
|
'ExtendedFieldKeep' => 'Varchar(255)',
|
|
'ExtendedFieldRemove' => 'Varchar(255)'
|
|
);
|
|
|
|
public function updateCMSFields(FieldList $fields) {
|
|
$fields->addFieldToTab('Root.Test', new TextField('ExtendedFieldRemove'));
|
|
$fields->addFieldToTab('Root.Test', new TextField('ExtendedFieldKeep'));
|
|
|
|
if($childField = $fields->dataFieldByName('ChildFieldBeforeExtension')) {
|
|
$childField->setTitle('ChildFieldBeforeExtension: Modified Title');
|
|
}
|
|
|
|
if($grandchildField = $fields->dataFieldByName('GrandchildFieldBeforeExtension')) {
|
|
$grandchildField->setTitle('GrandchildFieldBeforeExtension: Modified Title');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Second level test class.
|
|
* Tests usage of beforeExtendingCMSFields
|
|
*/
|
|
class DataExtensionTest_CMSFieldsChild extends DataExtensionTest_CMSFieldsBase implements TestOnly {
|
|
private static $db = array(
|
|
'ChildField' => 'Varchar(255)',
|
|
'ChildFieldBeforeExtension' => 'Varchar(255)'
|
|
);
|
|
|
|
public function getCMSFields() {
|
|
$this->beforeExtending('updateCMSFields', function(FieldList $fields) {
|
|
$fields->addFieldToTab('Root.Test', new TextField('ChildFieldBeforeExtension'));
|
|
});
|
|
|
|
$this->afterExtending('updateCMSFields', function(FieldList $fields){
|
|
$fields->removeByName('ExtendedFieldRemove', true);
|
|
});
|
|
|
|
$fields = parent::getCMSFields();
|
|
$fields->addFieldToTab('Root.Test', new TextField('ChildField'));
|
|
return $fields;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Third level test class, testing that beforeExtendingCMSFields can be nested
|
|
*/
|
|
class DataExtensionTest_CMSFieldsGrandchild extends DataExtensionTest_CMSFieldsChild implements TestOnly {
|
|
private static $db = array(
|
|
'GrandchildField' => 'Varchar(255)'
|
|
);
|
|
|
|
public function getCMSFields() {
|
|
$this->beforeUpdateCMSFields(function(FieldList $fields) {
|
|
// Remove field from parent's beforeExtendingCMSFields
|
|
$fields->removeByName('ChildFieldBeforeExtension', true);
|
|
|
|
// Adds own pre-extension field
|
|
$fields->addFieldToTab('Root.Test', new TextField('GrandchildFieldBeforeExtension'));
|
|
});
|
|
|
|
$fields = parent::getCMSFields();
|
|
$fields->addFieldToTab('Root.Test', new TextField('GrandchildField'));
|
|
return $fields;
|
|
}
|
|
}
|