diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/07_ReactJS_Redux_and_GraphQL.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/07_ReactJS_Redux_and_GraphQL.md index d83d3da0d..2c34a482e 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/07_ReactJS_Redux_and_GraphQL.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/07_ReactJS_Redux_and_GraphQL.md @@ -1086,7 +1086,61 @@ const transformReadNotes = (manager) => { export default transformReadNotes; ``` -Simple! The transformation passes us a `ApolloGraphQLManager` instance that provides a fluent API for updating a query definition the same way the `FormStateManager` allows us to update Redux form state. In this case, our need is really straightforward. We'l just add a new field and be done with it. +Simple! The transformation passes us a `ApolloGraphQLManager` instance that provides a fluent API for updating a query definition the same way the `FormStateManager` allows us to update Redux form state. + +#### Adding fields + +In the above example, we added a single field to a query. Here's how that works: + + +```js +manager.addField(fieldName, fieldPath = 'root') +``` + +The `fieldPath` argument tells the manager at what level to add the field. In this case, since the `Priority` field is going on the root query (`readNotes`), we'll use `root` as the path. But suppose we had a more complex query like this: + +``` +query readMembers { + FirstName + Surname + Friends { + Email + Company { + Name + } + } +} +``` + +If we wanted to add a field to the nested `Company` query on `Friends`, we would use a path syntax. + +```js +manager.addField('Tagline', 'root/Friends/Company'); +``` + +#### Adding field arguments + +Let's suppose we had the following query: + +``` +query ReadMembers($ImageSize: String!) { + readMembers { + FirstName + Avatar(Size: $ImageSize) + Company { + Name + } + } +} +``` + +Maybe the `Company` type has a `Logo`, and we want to apply the `ImageSize` parameter as an argument to that field. + +```js +manager.addArg('Size', 'ImageSize', 'root/Company/Logo'); +``` + +Where `root/Company/Logo` is the path to the field, `Size` is the name of the argument on that field, and `ImageSize` is the name of the variable. #### Applying the transforms diff --git a/src/Core/Extensible.php b/src/Core/Extensible.php index de40620b6..b29dda32e 100644 --- a/src/Core/Extensible.php +++ b/src/Core/Extensible.php @@ -135,6 +135,10 @@ trait Extensible $this->addCallbackMethod($method, function ($inst, $args) use ($method, $extensionClass) { /** @var Extensible $inst */ $extension = $inst->getExtensionInstance($extensionClass); + if (!$extension) { + return null; + } + try { $extension->setOwner($inst); return call_user_func_array([$extension, $method], $args); diff --git a/src/Dev/State/ExtensionTestState.php b/src/Dev/State/ExtensionTestState.php index ea0859915..2cfedddbd 100644 --- a/src/Dev/State/ExtensionTestState.php +++ b/src/Dev/State/ExtensionTestState.php @@ -85,5 +85,6 @@ class ExtensionTestState implements TestState public function tearDownOnce($class) { + DataObject::flush_extra_methods_cache(); } } diff --git a/src/Forms/TabSet.php b/src/Forms/TabSet.php index 6119fc999..da18e9d48 100644 --- a/src/Forms/TabSet.php +++ b/src/Forms/TabSet.php @@ -43,13 +43,6 @@ class TabSet extends CompositeField */ protected $schemaComponent = 'Tabs'; - /** - * Alter datatype from structural to track the current tab in redux state. - * - * @var string - */ - protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_STRING; - /** * @var TabSet */ diff --git a/src/ORM/ArrayList.php b/src/ORM/ArrayList.php index 9ac0558e2..612347fe2 100644 --- a/src/ORM/ArrayList.php +++ b/src/ORM/ArrayList.php @@ -8,6 +8,7 @@ use SilverStripe\View\ViewableData; use ArrayIterator; use InvalidArgumentException; use LogicException; +use SilverStripe\Dev\Deprecation; /** * A list object that wraps around an array of objects or arrays. @@ -160,7 +161,29 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L */ public function limit($length, $offset = 0) { + // Type checking: designed for consistency with DataList::limit() + if (!is_numeric($length) || !is_numeric($offset)) { + Deprecation::notice( + '4.3', + 'Arguments to ArrayList::limit() should be numeric' + ); + } + + if ($length < 0 || $offset < 0) { + Deprecation::notice( + '4.3', + 'Arguments to ArrayList::limit() should be positive' + ); + } + if (!$length) { + if ($length === 0) { + Deprecation::notice( + '4.3', + "limit(0) is deprecated in SS4. In SS5 a limit of 0 will instead return no records." + ); + } + $length = count($this->items); } diff --git a/src/ORM/Connect/MySQLiConnector.php b/src/ORM/Connect/MySQLiConnector.php index 6b283be7b..43805c705 100644 --- a/src/ORM/Connect/MySQLiConnector.php +++ b/src/ORM/Connect/MySQLiConnector.php @@ -73,8 +73,8 @@ class MySQLiConnector extends DBConnector $selectedDB = ($selectDB && !empty($parameters['database'])) ? $parameters['database'] : null; // Connection charset and collation - $connCharset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'connection_charset'); - $connCollation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'connection_collation'); + $connCharset = Config::inst()->get(MySQLDatabase::class, 'connection_charset'); + $connCollation = Config::inst()->get(MySQLDatabase::class, 'connection_collation'); $this->dbConn = mysqli_init(); @@ -186,7 +186,8 @@ class MySQLiConnector extends DBConnector $types = ''; $values = array(); $blobs = array(); - for ($index = 0; $index < count($parameters); $index++) { + $parametersCount = count($parameters); + for ($index = 0; $index < $parametersCount; $index++) { $value = $parameters[$index]; $phpType = gettype($value); @@ -247,12 +248,13 @@ class MySQLiConnector extends DBConnector // Because mysqli_stmt::bind_param arguments must be passed by reference // we need to do a bit of hackery $boundNames = []; - for ($i = 0; $i < count($parameters); $i++) { + $parametersCount = count($parameters); + for ($i = 0; $i < $parametersCount; $i++) { $boundName = "param$i"; $$boundName = $parameters[$i]; $boundNames[] = &$$boundName; } - call_user_func_array(array($statement, 'bind_param'), $boundNames); + $statement->bind_param(...$boundNames); } public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) @@ -293,10 +295,10 @@ class MySQLiConnector extends DBConnector $metaData = $statement->result_metadata(); if ($metaData) { return new MySQLStatement($statement, $metaData); - } else { - // Replicate normal behaviour of ->query() on non-select calls - return new MySQLQuery($this, true); } + + // Replicate normal behaviour of ->query() on non-select calls + return new MySQLQuery($this, true); } public function selectDatabase($name) @@ -304,9 +306,9 @@ class MySQLiConnector extends DBConnector if ($this->dbConn->select_db($name)) { $this->databaseName = $name; return true; - } else { - return false; } + + return false; } public function getSelectedDatabase() diff --git a/src/ORM/Connect/PDOConnector.php b/src/ORM/Connect/PDOConnector.php index f3fbae164..7d9f1af4a 100644 --- a/src/ORM/Connect/PDOConnector.php +++ b/src/ORM/Connect/PDOConnector.php @@ -106,7 +106,7 @@ class PDOConnector extends DBConnector */ public static function is_emulate_prepare() { - return Config::inst()->get('SilverStripe\ORM\Connect\PDOConnector', 'emulate_prepare'); + return self::config()->get('emulate_prepare'); } public function connect($parameters, $selectDB = false) @@ -159,8 +159,8 @@ class PDOConnector extends DBConnector } // Connection charset and collation - $connCharset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'connection_charset'); - $connCollation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'connection_collation'); + $connCharset = Config::inst()->get(MySQLDatabase::class, 'connection_charset'); + $connCollation = Config::inst()->get(MySQLDatabase::class, 'connection_collation'); // Set charset if given and not null. Can explicitly set to empty string to omit if (!in_array($parameters['driver'], ['sqlsrv', 'pgsql'])) { @@ -325,7 +325,8 @@ class PDOConnector extends DBConnector public function bindParameters(PDOStatement $statement, $parameters) { // Bind all parameters - for ($index = 0; $index < count($parameters); $index++) { + $parameterCount = count($parameters); + for ($index = 0; $index < $parameterCount; $index++) { $value = $parameters[$index]; $phpType = gettype($value); @@ -338,7 +339,7 @@ class PDOConnector extends DBConnector // Check type of parameter $type = $this->getPDOParamType($phpType); if ($type === PDO::PARAM_STR) { - $value = strval($value); + $value = (string) $value; } // Bind this value @@ -388,7 +389,6 @@ class PDOConnector extends DBConnector // Ensure statement is closed if ($statement) { $statement->closeCursor(); - unset($statement); } // Report any errors diff --git a/src/ORM/FieldType/DBEnum.php b/src/ORM/FieldType/DBEnum.php index e2c9d542c..cb3126cec 100644 --- a/src/ORM/FieldType/DBEnum.php +++ b/src/ORM/FieldType/DBEnum.php @@ -145,7 +145,7 @@ class DBEnum extends DBString public function scaffoldSearchField($title = null) { $anyText = _t('SilverStripe\\ORM\\FieldType\\DBEnum.ANY', 'Any'); - return $this->formField($title, null, true, $anyText, "($anyText)"); + return $this->formField($title, null, true, '', "($anyText)"); } /** diff --git a/templates/SilverStripe/Forms/GridField/GridFieldPaginator_Row.ss b/templates/SilverStripe/Forms/GridField/GridFieldPaginator_Row.ss index 8cd7839bf..364256ba6 100644 --- a/templates/SilverStripe/Forms/GridField/GridFieldPaginator_Row.ss +++ b/templates/SilverStripe/Forms/GridField/GridFieldPaginator_Row.ss @@ -6,7 +6,7 @@ $FirstPage $PreviousPage <%t SilverStripe\\Forms\\GridField\\GridFieldPaginator.Page 'Page' %> - + <%t SilverStripe\\Forms\\GridField\\GridFieldPaginator.OF 'of' is 'Example: View 1 of 2' %> $NumPages diff --git a/tests/php/ORM/ArrayListTest.php b/tests/php/ORM/ArrayListTest.php index 513b5d79b..3d6ae6250 100644 --- a/tests/php/ORM/ArrayListTest.php +++ b/tests/php/ORM/ArrayListTest.php @@ -7,6 +7,7 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\Filterable; use SilverStripe\Dev\SapphireTest; use SilverStripe\View\ArrayData; +use SilverStripe\Dev\Deprecation; use stdClass; class ArrayListTest extends SapphireTest @@ -133,6 +134,19 @@ class ArrayListTest extends SapphireTest ); } + /** + * @expectedException PHPUnit_Framework_Error + */ + public function testZeroLimit() + { + Deprecation::notification_version('4.3.0'); + $list = new ArrayList([ + ['Key' => 1], + ['Key' => 2], + ]); + $list->limit(0); + } + public function testAddRemove() { $list = new ArrayList(