mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '4.0' of https://github.com/silverstripe/silverstripe-framework into 4
This commit is contained in:
commit
2b6b877327
@ -165,21 +165,22 @@ around 2000px on the longest edge.
|
||||
|
||||
#### Forced resampling
|
||||
|
||||
Since the 'master' images in your asset store may have a large file size, by
|
||||
default SilverStripe will always apply compression to your images to save
|
||||
bandwidth - even if no other manipulation (such as a crop or resize) is taking
|
||||
place. If you expect the images in your asset store to already have
|
||||
compression applied and want to serve up the original when no resampling is
|
||||
necessary, you can add this to your mysite/config/config.yml file:
|
||||
Since the 'master' images in your asset store may have a large file size, SilverStripe
|
||||
can apply compression to your images to save bandwidth - even if no other manipulation
|
||||
(such as a crop or resize) is taking place. In many cases this can result in a smaller
|
||||
overall file size, which may be appropriate for streaming to web users.
|
||||
|
||||
Please note that turning this feature on can increase the server memory requirements,
|
||||
and is off by default to conserve resources.
|
||||
|
||||
You can turn this on with the below config:
|
||||
|
||||
```yml
|
||||
# Configure resampling for File dataobject
|
||||
---
|
||||
Name: resamplefiles
|
||||
---
|
||||
SilverStripe\Assets\File:
|
||||
force_resample: false
|
||||
# DBFile can be configured independently
|
||||
SilverStripe\Assets\Storage\DBFile:
|
||||
force_resample: false
|
||||
```
|
||||
|
||||
#### Resampled image quality
|
||||
|
@ -1350,18 +1350,26 @@ These methods are deprecated:
|
||||
### New Ownership API {#ownership}
|
||||
|
||||
In order to support the recursive publishing of dataobjects, a new API has been developed to allow
|
||||
developers to declare dependencies between objects. This is done to ensure that the published state
|
||||
of linked components are consistent with their "owner." Without the concept of ownership, these linked
|
||||
components could be implicitly exposed on the frontend, which may not align with the intent of the
|
||||
content author.
|
||||
developers to declare dependencies between versioned objects.
|
||||
|
||||
For instance, on a products page which has a list of products, the products should not be published unless the products page is, too. The ownership API solves this by allowing you to declare
|
||||
a two-way relationship between objects, typically, but not necessarily, linked by a database relationship
|
||||
(`has_many`, `many_many`, etc.).
|
||||
```php
|
||||
private static $owns = [
|
||||
'Banners', // This page depends on banners, declared as a separate 'has_many'
|
||||
'FooterImage', // This page depends on the footer image, declared as a separate 'has_one'
|
||||
];
|
||||
```
|
||||
|
||||
This is done to ensure that the published state of linked components are consistent with their "owner."
|
||||
Without the concept of ownership, these linked components could be implicitly exposed on the frontend,
|
||||
which may not align with the intent of the content author.
|
||||
|
||||
For instance, on a products page which has a list of products, the products should not be published unless
|
||||
the products page is, too. The ownership API solves this by allowing you to declare a two-way relationship
|
||||
between objects, typically, but not necessarily, linked by a database relationship
|
||||
(`has_many`, `many_many`, etc.).
|
||||
|
||||
```php
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use Page;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class ProductPage extends Page
|
||||
@ -1386,6 +1394,7 @@ class Product extends DataObject
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
If your objects are linked by something other than a database relationship, for instance, a custom
|
||||
getter that is computed at runtime, the same rules can be applied, as long as you provide an `$owned_by`
|
||||
setting on the child object.
|
||||
|
@ -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;
|
||||
|
@ -67,6 +67,9 @@ class DBDate extends DBField
|
||||
} else {
|
||||
// Convert US date -> iso, fix y2k, etc
|
||||
$value = $this->fixInputDate($value);
|
||||
if (is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
$source = strtotime($value); // convert string to timestamp
|
||||
}
|
||||
if ($value === false) {
|
||||
@ -529,6 +532,9 @@ class DBDate extends DBField
|
||||
// split
|
||||
list($year, $month, $day, $time) = $this->explodeDateString($value);
|
||||
|
||||
if ((int)$year === 0 && (int)$month === 0 && (int)$day === 0) {
|
||||
return null;
|
||||
}
|
||||
// Validate date
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
throw new InvalidArgumentException(
|
||||
@ -568,7 +574,7 @@ class DBDate extends DBField
|
||||
if ($parts[0] < 1000 && $parts[2] > 1000) {
|
||||
$parts = array_reverse($parts);
|
||||
}
|
||||
if ($parts[0] < 1000) {
|
||||
if ($parts[0] < 1000 && (int)$parts[0] !== 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"Invalid date: '$value'. Use " . self::ISO_DATE . " to prevent this error."
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +218,12 @@ class DBDateTest extends SapphireTest
|
||||
|
||||
$date = DBField::create_field('Date', 0);
|
||||
$this->assertEquals('1970-01-01', $date->getValue(), 'Zero is UNIX epoch date');
|
||||
|
||||
$date = DBField::create_field('Date', '0000-00-00 00:00:00');
|
||||
$this->assertNull($date->getValue(), '0000-00-00 00:00:00 is set as NULL');
|
||||
|
||||
$date = DBField::create_field('Date', '00/00/0000');
|
||||
$this->assertNull($date->getValue(), '00/00/0000 is set as NULL');
|
||||
}
|
||||
|
||||
public function testDayOfMonth()
|
||||
|
31
tests/php/ORM/DBEnumTest.php
Normal file
31
tests/php/ORM/DBEnumTest.php
Normal 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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user