diff --git a/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md b/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md index b1be3f83d..cea572907 100644 --- a/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md +++ b/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md @@ -1,7 +1,7 @@ # Nginx These instructions are also covered on the -[Nginx Wiki](http://wiki.nginx.org/SilverStripe). +[Nginx Wiki](https://www.nginx.com/resources/wiki/start/topics/recipes/silverstripe/). The prerequisite is that you have already installed Nginx and you are able to run PHP files via the FastCGI-wrapper from Nginx. diff --git a/docs/en/02_Developer_Guides/00_Model/12_Indexes.md b/docs/en/02_Developer_Guides/00_Model/12_Indexes.md index 6d686a26d..294135646 100644 --- a/docs/en/02_Developer_Guides/00_Model/12_Indexes.md +++ b/docs/en/02_Developer_Guides/00_Model/12_Indexes.md @@ -93,6 +93,9 @@ other columns. If this is indexed, smaller and reasonably unique it might be fas Indexes are generated and removed automatically during a `dev/build`. Caution if you're working with large tables and modify an index as the next `dev/build` will `DROP` the index, and then `ADD` it. +As of 3.7.0 `default_sort` fields will automatically become database indexes as this provides significant performance +benefits. + ## API Documentation * [DataObject](api:SilverStripe\ORM\DataObject) diff --git a/src/ORM/DataObjectSchema.php b/src/ORM/DataObjectSchema.php index 6ad3cfe34..01db82927 100644 --- a/src/ORM/DataObjectSchema.php +++ b/src/ORM/DataObjectSchema.php @@ -219,7 +219,7 @@ class DataObjectSchema // Record specification foreach ($fields as $name => $specification) { $prefix = $includeClass ? "{$tableClass}." : ""; - $db[$name] = $prefix . $specification; + $db[$name] = $prefix . $specification; } } return $db; @@ -524,6 +524,7 @@ class DataObjectSchema { if (!array_key_exists($class, $this->databaseIndexes)) { $this->databaseIndexes[$class] = array_merge( + $this->buildSortDatabaseIndexes($class), $this->cacheDefaultDatabaseIndexes($class), $this->buildCustomDatabaseIndexes($class) ); @@ -604,6 +605,53 @@ class DataObjectSchema return $indexes; } + protected function buildSortDatabaseIndexes($class) + { + $sort = Config::inst()->get($class, 'default_sort', Config::UNINHERITED); + $indexes = []; + + if ($sort && is_string($sort)) { + $sort = preg_split('/,(?![^()]*+\\))/', $sort); + foreach ($sort as $value) { + try { + list ($table, $column) = $this->parseSortColumn(trim($value)); + $table = trim($table, '"'); + $column = trim($column, '"'); + if ($table && strtolower($table) !== strtolower(self::tableName($class))) { + continue; + } + if ($this->databaseField($class, $column, false)) { + $indexes[$column] = [ + 'type' => 'index', + 'columns' => [$column], + ]; + } + } catch (InvalidArgumentException $e) { + } + } + } + return $indexes; + } + + /** + * Parses a specified column into a sort field and direction + * + * @param string $column String to parse containing the column name + * @return array Resolved table and column. + */ + protected function parseSortColumn($column) + { + // Parse column specification, considering possible ansi sql quoting + // Note that table prefix is allowed, but discarded + if (preg_match('/^("?(?[^"\s]+)"?\\.)?"?(?[^"\s]+)"?(\s+(?((asc)|(desc))(ending)?))?$/i', $column, $match)) { + $table = $match['table']; + $column = $match['column']; + } else { + throw new InvalidArgumentException("Invalid sort() column"); + } + return array($table, $column); + } + /** * Returns the table name in the class hierarchy which contains a given * field column for a {@link DataObject}. If the field does not exist, this @@ -714,7 +762,6 @@ class DataObjectSchema } - /** * Parse a belongs_many_many component to extract class and relationship name * diff --git a/tests/php/ORM/DataObjectSchemaGenerationTest.php b/tests/php/ORM/DataObjectSchemaGenerationTest.php index 3f305b824..b366d5d51 100644 --- a/tests/php/ORM/DataObjectSchemaGenerationTest.php +++ b/tests/php/ORM/DataObjectSchemaGenerationTest.php @@ -2,11 +2,13 @@ namespace SilverStripe\ORM\Tests; +use SilverStripe\Core\Config\Config; use SilverStripe\ORM\Connect\MySQLSchemaManager; use SilverStripe\ORM\DB; use SilverStripe\ORM\FieldType\DBClassName; use SilverStripe\ORM\DataObject; use SilverStripe\Dev\SapphireTest; +use SilverStripe\ORM\Tests\DataObjectSchemaGenerationTest\SortedObject; use SilverStripe\ORM\Tests\DataObjectSchemaGenerationTest\TestIndexObject; use SilverStripe\ORM\Tests\DataObjectSchemaGenerationTest\TestObject; @@ -14,7 +16,8 @@ class DataObjectSchemaGenerationTest extends SapphireTest { protected static $extra_dataobjects = array( TestObject::class, - TestIndexObject::class + TestIndexObject::class, + SortedObject::class, ); public static function setUpBeforeClass() @@ -260,4 +263,65 @@ class DataObjectSchemaGenerationTest extends SapphireTest $item1->delete(); $item2->delete(); } + + public function testSortFieldBecomeIndexes() + { + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Sort'], + ], $indexes); + DataObject::getSchema()->reset(); + Config::inst()->update(SortedObject::class, 'default_sort', 'Sort ASC'); + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Sort'], + ], $indexes); + DataObject::getSchema()->reset(); + Config::inst()->update(SortedObject::class, 'default_sort', 'Sort DESC'); + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Sort'], + ], $indexes); + DataObject::getSchema()->reset(); + Config::inst()->update(SortedObject::class, 'default_sort', '"Sort" DESC'); + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Sort'], + ], $indexes); + DataObject::getSchema()->reset(); + Config::inst()->update(SortedObject::class, 'default_sort', '"DataObjectSchemaGenerationTest_SortedObject"."Sort" ASC'); + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Sort'], + ], $indexes); + DataObject::getSchema()->reset(); + Config::inst()->update(SortedObject::class, 'default_sort', '"Sort" DESC, "Title" ASC'); + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Sort'], + ], $indexes); + $this->assertContains([ + 'type' => 'index', + 'columns' => ['Title'], + ], $indexes); + DataObject::getSchema()->reset(); + // make sure that specific indexes aren't overwritten + Config::inst()->update(SortedObject::class, 'indexes', [ + 'Sort' => [ + 'type' => 'unique', + 'columns' => ['Sort'], + ], + ]); + $indexes = DataObject::getSchema()->databaseIndexes(SortedObject::class); + $this->assertContains([ + 'type' => 'unique', + 'columns' => ['Sort'], + ], $indexes); + } } diff --git a/tests/php/ORM/DataObjectSchemaGenerationTest/SortedObject.php b/tests/php/ORM/DataObjectSchemaGenerationTest/SortedObject.php new file mode 100644 index 000000000..51484f72d --- /dev/null +++ b/tests/php/ORM/DataObjectSchemaGenerationTest/SortedObject.php @@ -0,0 +1,18 @@ + 'Varchar', + 'Sort' => 'Int', + ]; + + private static $default_sort = 'Sort'; +}