Merge pull request #7605 from open-sausages/pulls/4.0/fix-enum-defaults

BUG Fix DBEnum ignoring empty defaults
This commit is contained in:
Chris Joe 2017-11-20 11:32:45 +13:00 committed by GitHub
commit d8ca223e15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 35 deletions

View File

@ -163,7 +163,7 @@ class PDOConnector extends DBConnector
$connCollation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'connection_collation');
// Set charset if given and not null. Can explicitly set to empty string to omit
if ($parameters['driver'] !== 'sqlsrv') {
if (!in_array($parameters['driver'], ['sqlsrv', 'pgsql'])) {
$charset = isset($parameters['charset'])
? $parameters['charset']
: $connCharset;

View File

@ -32,45 +32,46 @@ class DBEnum extends DBString
private static $default_search_filter_class = 'ExactMatchFilter';
/**
* Create a new Enum field.
* Create a new Enum field, which is a value within a defined set, with an optional default.
*
* Example usage in {@link DataObject::$db} with comma-separated string
* notation ('Val1' is default)
* Example field specification strings:
*
* <code>
* "MyField" => "Enum('Val1, Val2, Val3', 'Val1')"
* </code>
*
* Example usage in in {@link DataObject::$db} with array notation
* ('Val1' is default)
*
* <code>
* "MyField" => "Enum(array('Val1', 'Val2', 'Val3'), 'Val1')"
* "MyField" => "Enum('Val1, Val2, Val3')" // First item 'Val1' is default implicitly
* "MyField" => "Enum('Val1, Val2, Val3', 'Val2')" // 'Val2' is default explicitly
* "MyField" => "Enum('Val1, Val2, Val3', null)" // Force empty (no) default
* "MyField" => "Enum(array('Val1', 'Val2', 'Val3'), 'Val1')" // Supports array notation as well
* </code>
*
* @param string $name
* @param string|array $enum A string containing a comma separated list of options or an array of Vals.
* @param string $default The default option, which is either NULL or one of the items in the enumeration.
* @param string|int|null $default The default option, which is either NULL or one of the items in the enumeration.
* If passing in an integer (non-string) it will default to the index of that item in the list.
* Set to null or empty string to allow empty values
* @param array $options Optional parameters for this DB field
*/
public function __construct($name = null, $enum = null, $default = null, $options = [])
public function __construct($name = null, $enum = null, $default = 0, $options = [])
{
if ($enum) {
$this->setEnum($enum);
$enum = $this->getEnum();
// If there's a default, then
if ($default) {
if (in_array($default, $this->getEnum())) {
// If there's a default, then use this
if ($default && !is_int($default)) {
if (in_array($default, $enum)) {
$this->setDefault($default);
} else {
user_error("Enum::__construct() The default value '$default' does not match any item in the"
. " enumeration", E_USER_ERROR);
user_error(
"Enum::__construct() The default value '$default' does not match any item in the enumeration",
E_USER_ERROR
);
}
// By default, set the default value to the first item
} elseif (is_int($default) && $default < count($enum)) {
// Set to specified index if given
$this->setDefault($enum[$default]);
} else {
$enum = $this->getEnum();
$this->setDefault(reset($enum));
// Set to null if specified
$this->setDefault(null);
}
}
@ -187,7 +188,7 @@ class DBEnum extends DBString
ltrim(preg_replace('/,\s*\n\s*$/', '', $enum))
);
}
$this->enum = $enum;
$this->enum = array_values($enum);
return $this;
}
@ -210,6 +211,7 @@ class DBEnum extends DBString
public function setDefault($default)
{
$this->default = $default;
$this->setDefaultValue($default);
return $this;
}
}

View File

@ -2,8 +2,9 @@
namespace SilverStripe\ORM\FieldType;
use SilverStripe\Core\Injector\Injector;
use InvalidArgumentException;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\FormField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataObject;
@ -139,7 +140,7 @@ abstract class DBField extends ViewableData implements DBIndexable
if ($options) {
if (!is_array($options)) {
throw new \InvalidArgumentException("Invalid options $options");
throw new InvalidArgumentException("Invalid options $options");
}
$this->setOptions($options);
}
@ -152,16 +153,26 @@ abstract class DBField extends ViewableData implements DBIndexable
*
* Useful for accessing the classes behaviour for other parts of your code.
*
* @param string $className class of field to construct
* @param string $spec Class specification to construct. May include both service name and additional
* constructor arguments in the same format as DataObject.db config.
* @param mixed $value value of field
* @param string $name Name of field
* @param mixed $object Additional parameter to pass to field constructor
* @param mixed $args Additional arguments to pass to constructor if not using args in service $spec
* Note: Will raise a warning if using both
* @return static
*/
public static function create_field($className, $value, $name = null, $object = null)
public static function create_field($spec, $value, $name = null, ...$args)
{
// Raise warning if inconsistent with DataObject::dbObject() behaviour
// This will cause spec args to be shifted down by the number of provided $args
if ($args && strpos($spec, '(') !== false) {
trigger_error('Additional args provided in both $spec and $args', E_USER_WARNING);
}
// Ensure name is always first argument
array_unshift($args, $name);
/** @var DBField $dbField */
$dbField = Injector::inst()->create($className, $name, $object);
$dbField = Injector::inst()->createWithArgs($spec, $args);
$dbField->setValue($value, null, false);
return $dbField;
}
@ -633,11 +644,13 @@ DBG;
public function getIndexSpecs()
{
if ($type = $this->getIndexType()) {
$type = $this->getIndexType();
if ($type) {
return [
'type' => $type,
'columns' => [$this->getName()],
];
}
return null;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace SilverStripe\ORM\Tests;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\FieldType\DBEnum;
use SilverStripe\ORM\FieldType\DBField;
class DBEnumTest extends SapphireTest
{
public function testDefault()
{
/** @var DBEnum $enum1 */
$enum1 = DBField::create_field('Enum("A, B, C, D")', null);
/** @var DBEnum $enum2 */
$enum2 = DBField::create_field('Enum("A, B, C, D", "")', null);
/** @var DBEnum $enum3 */
$enum3 = DBField::create_field('Enum("A, B, C, D", null)', null);
/** @var DBEnum $enum4 */
$enum4 = DBField::create_field('Enum("A, B, C, D", 1)', null);
$this->assertEquals('A', $enum1->getDefaultValue());
$this->assertEquals('A', $enum1->getDefault());
$this->assertEquals(null, $enum2->getDefaultValue());
$this->assertEquals(null, $enum2->getDefault());
$this->assertEquals(null, $enum3->getDefaultValue());
$this->assertEquals(null, $enum3->getDefault());
$this->assertEquals('B', $enum4->getDefaultValue());
$this->assertEquals('B', $enum4->getDefault());
}
}

View File

@ -104,17 +104,17 @@ class PDODatabaseTest extends SapphireTest
$this->markTestSkipped('This test requires the current DB connector is PDO');
}
$query = new SQLUpdate('MySQLDatabaseTest_Data');
$query->setAssignments(array('Title' => 'New Title'));
$query = new SQLUpdate('"MySQLDatabaseTest_Data"');
$query->setAssignments(array('"Title"' => 'New Title'));
// Test update which affects no rows
$query->setWhere(array('Title' => 'Bob'));
$query->setWhere(array('"Title"' => 'Bob'));
$result = $query->execute();
$this->assertInstanceOf(PDOQuery::class, $result);
$this->assertEquals(0, DB::affected_rows());
// Test update which affects some rows
$query->setWhere(array('Title' => 'First Item'));
$query->setWhere(array('"Title"' => 'First Item'));
$result = $query->execute();
$this->assertInstanceOf(PDOQuery::class, $result);
$this->assertEquals(1, DB::affected_rows());