From 6d792adab2f1804b9e0297df4bdf6aba0260c7f7 Mon Sep 17 00:00:00 2001 From: Will Rossiter Date: Sat, 8 Jun 2013 15:14:53 +1200 Subject: [PATCH] Update documentation static declarations to private Also spelling, grammar and line length clean up. --- docs/en/reference/grid-field.md | 5 +- docs/en/reference/uploadfield.md | 2 +- docs/en/topics/commandline.md | 37 +-- docs/en/topics/controller.md | 8 +- docs/en/topics/datamodel.md | 446 +++++++++++++++++++------------ docs/en/topics/forms.md | 107 +++++--- 6 files changed, 367 insertions(+), 238 deletions(-) diff --git a/docs/en/reference/grid-field.md b/docs/en/reference/grid-field.md index a97f920d8..3b30f6bc8 100644 --- a/docs/en/reference/grid-field.md +++ b/docs/en/reference/grid-field.md @@ -1,6 +1,6 @@ -# Gridfield +# GridField -Gridfield is SilverStripe's implementation of data grids. Its main purpose is to display tabular data +GridField is SilverStripe's implementation of data grids. Its main purpose is to display tabular data in a format that is easy to view and modify. It's a can be thought of as a HTML table with some tricks. It's built in a way that provides developers with an extensible way to display tabular data in a @@ -170,6 +170,7 @@ The namespace notation is `ManyMany[]`, so for example Example: :::php + class Player extends DataObject { private static $db = array('Name' => 'Text'); public static $many_many = array('Teams' => 'Team'); diff --git a/docs/en/reference/uploadfield.md b/docs/en/reference/uploadfield.md index 497e45890..aec8b5a53 100644 --- a/docs/en/reference/uploadfield.md +++ b/docs/en/reference/uploadfield.md @@ -208,7 +208,7 @@ like this: 'Description' => 'Text' ); - public static $belongs_many_many = array( + private static $belongs_many_many = array( 'GalleryPage' => 'GalleryPage' ); } diff --git a/docs/en/topics/commandline.md b/docs/en/topics/commandline.md index b6bebb73f..e69de8a7e 100644 --- a/docs/en/topics/commandline.md +++ b/docs/en/topics/commandline.md @@ -98,22 +98,25 @@ This code provides a good template: :::php class MyProcess extends Controller { - public static $allowed_actions = array('index'); - function index() { - set_time_limit(0); - while(memory_get_usage() < 32*1024*1024) { - if($this->somethingToDo()) { - $this->doSomething(); - sleep(1) - } else { - sleep(300); - } - } - } + + private static $allowed_actions = array( + 'index' + ); + + function index() { + set_time_limit(0); + + while(memory_get_usage() < 32*1024*1024) { + if($this->somethingToDo()) { + $this->doSomething(); + sleep(1) + } else { + sleep(300); + } + } + } } - - Step 2: Install the "daemon" command-line tool on your server. Step 3: Use sake to start and stop your process @@ -122,8 +125,9 @@ Step 3: Use sake to start and stop your process sake -stop MyProcess -Note that sake processes are currently a little brittle, in that the pid and log files are placed in the site root -directory, rather than somewhere sensible like /var/log or /var/run. +Note that sake processes are currently a little brittle, in that the pid and log +files are placed in the site root directory, rather than somewhere sensible like +/var/log or /var/run. ### Running Regular Tasks With Cron @@ -137,6 +141,7 @@ php /path/to/site_root/framework/cli-script.php dev/tasks/MyTask If you find that your cron job appears to be retrieving the login screen, then you may need to use `php-cli` instead. This is typical of a cPanel-based setup. + ``` php-cli /path/to/site_root/framework/cli-script.php dev/tasks/MyTask ``` diff --git a/docs/en/topics/controller.md b/docs/en/topics/controller.md index d03254af4..81880c948 100644 --- a/docs/en/topics/controller.md +++ b/docs/en/topics/controller.md @@ -164,8 +164,12 @@ through `/fastfood/drivethrough/` to use the same order function. :::php class FastFood_Controller extends Controller { - private static $allowed_actions = array('drivethrough'); - public static $url_handlers = array( + + private static $allowed_actions = array( + 'drivethrough' + ); + + private static $url_handlers = array( 'drivethrough/$Action/$ID/$Name' => 'order' ); diff --git a/docs/en/topics/datamodel.md b/docs/en/topics/datamodel.md index c67032a54..67494760c 100755 --- a/docs/en/topics/datamodel.md +++ b/docs/en/topics/datamodel.md @@ -1,21 +1,26 @@ # Datamodel -SilverStripe uses an [object-relational model](http://en.wikipedia.org/wiki/Object-relational_model) that assumes the -following connections: +SilverStripe uses an [object-relational model](http://en.wikipedia.org/wiki/Object-relational_model) +that assumes the following connections: * Each database-table maps to a PHP class * Each database-row maps to a PHP object * Each database-column maps to a property on a PHP object -All data tables in SilverStripe are defined as subclasses of `[api:DataObject]`. Inheritance is supported in the data -model: seperate tables will be linked together, the data spread across these tables. The mapping and saving/loading -logic is handled by SilverStripe, you don't need to worry about writing SQL most of the time. +All data tables in SilverStripe are defined as subclasses of `[api:DataObject]`. + +Inheritance is supported in the data model: separate tables will be linked +together, the data spread across these tables. The mapping and saving/loading +logic is handled by SilverStripe, you don't need to worry about writing SQL most +of the time. Most of the ORM customizations are possible through [PHP5 Object -Overloading](http://www.onlamp.com/pub/a/php/2005/06/16/overloading.html) handled in the `[api:Object]`-class. +Overloading](http://www.onlamp.com/pub/a/php/2005/06/16/overloading.html) +handled in the `[api:Object]`-class. -See [database-structure](/reference/database-structure) for in-depth information on the database-schema, -and the ["sql queries" topic](/reference/sqlquery) in case you need to drop down to the bare metal. +See [database-structure](/reference/database-structure) for in-depth information +on the database-schema and the ["sql queries" topic](/reference/sqlquery) in +case you need to drop down to the bare metal. ## Generating the Database Schema @@ -24,27 +29,34 @@ The SilverStripe database-schema is generated automatically by visiting the URL.
Note: You need to be logged in as an administrator to perform this command, -unless your site is in "[dev mode](/topics/debugging)", or the command is run through CLI. +unless your site is in "[dev mode](/topics/debugging)", or the command is run +through CLI.
## Querying Data -Every query to data starts with a `DataList::create()` or `::get()` call. For example, this query would return all of the `Member` objects: +Every query to data starts with a `DataList::create()` or `::get()` +call. For example, this query would return all of the `Member` objects: :::php $members = Member::get(); -The ORM uses a "fluent" syntax, where you specify a query by chaining together different methods. Two common methods -are `filter()` and `sort()`: +The ORM uses a "fluent" syntax, where you specify a query by chaining together +different methods. Two common methods are `filter()` and `sort()`: :::php - $members = Member::get()->filter(array('FirstName' => 'Sam'))->sort('Surname'); + $members = Member::get()->filter(array( + 'FirstName' => 'Sam' + ))->sort('Surname'); -Those of you who know a bit about SQL might be thinking "it looks like you're querying all members, and then filtering -to those with a first name of 'Sam'. Isn't this very slow?" Is isn't, because the ORM doesn't actually execute the SQL -query until you iterate on the result with a `foreach()` or `<% loop %>`. The ORM is smart enough to generate a single -efficient query at the last moment in time without needing to post process the result set in PHP. In MySQL the query -generated by the ORM may look something like this for the previous query. +Those of you who know a bit about SQL might be thinking "it looks like you're +querying all members, and then filtering to those with a first name of 'Sam'. + +Isn't this very slow?" Is isn't, because the ORM doesn't actually execute the +SQL query until you iterate on the result with a `foreach()` or `<% loop %>`. +The ORM is smart enough to generate a single efficient query at the last moment +in time without needing to post process the result set in PHP. In MySQL the +query generated by the ORM may look something like this for the previous query. ::: SELECT * FROM Member WHERE FirstName = 'Sam' ORDER BY Surname @@ -64,10 +76,13 @@ An example of the query process in action: echo "

$member->FirstName $member->Surname

"; } -This also means that getting the count of a list of objects will be done with a single, efficient query. +This also means that getting the count of a list of objects will be done with a +single, efficient query. :::php - $members = Member::get()->filter(array('FirstName' => 'Sam'))->sort('Surname'); + $members = Member::get()->filter(array( + 'FirstName' => 'Sam' + ))->sort('Surname'); // This will create an single SELECT COUNT query similar to - // SELECT COUNT(*) FROM Members WHERE FirstName = 'Sam' @@ -76,35 +91,42 @@ This also means that getting the count of a list of objects will be done with a ### Returning a single DataObject -There are a couple of ways of getting a single DataObject from the ORM. If you know the ID number of the object, -you can use `byID($id)`: +There are a couple of ways of getting a single DataObject from the ORM. If you +know the ID number of the object, you can use `byID($id)`: :::php $member = Member::get()->byID(5); -If you have constructed a query that you know should return a single record, you can call `First()`: +If you have constructed a query that you know should return a single record, you +can call `First()`: :::php - $member = Member::get()->filter(array('FirstName' => 'Sam', 'Surname' => 'Minnee'))->First(); + $member = Member::get()->filter(array( + 'FirstName' => 'Sam', 'Surname' => 'Minnee' + ))->First(); ### Sort -Quite often you would like to sort a list. Doing this on a list could be done in a few ways. +Quite often you would like to sort a list. Doing this on a list could be done in +a few ways. If would like to sort the list by `FirstName` in a ascending way (from A to Z). :::php - $member = Member::get()->sort('FirstName', 'ASC'); // ASC or DESC - $member = Member::get()->sort('FirstName'); // Ascending is implied + $members = Member::get()->sort('FirstName', 'ASC'); // ASC or DESC + $members = Member::get()->sort('FirstName'); // Ascending is implied To reverse the sort :::php - $member = Member::get()->sort('FirstName', 'DESC'); + $members = Member::get()->sort('FirstName', 'DESC'); -However you might have several entries with the same `FirstName` and would like to sort them by `FirstName` -and `LastName` + // or.. + $members = Member::get()->sort('FirstName', 'ASC')->reverse(); + +However you might have several entries with the same `FirstName` and would like +to sort them by `FirstName` and `LastName` :::php $member = Member::get()->sort(array( @@ -119,18 +141,19 @@ You can also sort randomly ### Filter -As you might expect, the `filter()` method filters the list of objects that gets returned. The previous example -included this filter, which returns all Members with a first name of "Sam". +As you might expect, the `filter()` method filters the list of objects that gets +returned. The previous example included this filter, which returns all Members +with a first name of "Sam". :::php $members = Member::get()->filter(array('FirstName' => 'Sam')); -In SilverStripe 2, we would have passed `"\"FirstName\" = 'Sam'` to make this query. Now, we pass an array, -`array('FirstName' => 'Sam')`, to minimise the risk of SQL injection bugs. The format of this array follows a few -rules: +In SilverStripe 2, we would have passed `"\"FirstName\" = 'Sam'` to make this +query. Now, we pass an array, `array('FirstName' => 'Sam')`, to minimize the +risk of SQL injection bugs. The format of this array follows a few rules: - * Each element of the array specifies a filter. You can specify as many filters as you like, and they **all** must - be true. + * Each element of the array specifies a filter. You can specify as many + filters as you like, and they **all** must be true. * The key in the filter corresponds to the field that you want to filter by. * The value in the filter corresponds to the value that you want to filter to. @@ -154,18 +177,19 @@ Or if you want to find both Sam and Sig. 'FirstName', array('Sam', 'Sig') ); - -Then there is the most complex task when you want to find Sam and Sig that has either Age 17 or 74. +Then there is the most complex task when you want to find Sam and Sig that has +either Age 17 or 74. :::php $members = Member::get()->filter(array( 'FirstName' => array('Sam', 'Sig'), 'Age' => array(17, 74) )); + // SQL: WHERE ("FirstName" IN ('Sam', 'Sig) AND "Age" IN ('17', '74)) -In case you want to match multiple criteria non-exclusively (with an "OR" disjunctive), -use the `filterAny()` method instead: +In case you want to match multiple criteria non-exclusively (with an "OR" +disjunctive),use the `filterAny()` method instead: :::php $members = Member::get()->filterAny(array( @@ -185,11 +209,12 @@ You can also combine both conjunctive ("AND") and disjunctive ("OR") statements. 'FirstName' => 'Sam', 'Age' => 17, )); - // SQL: WHERE ("LastName" = 'Minnée' AND ("FirstName" = 'Sam' OR "Age" = '17')) + // WHERE ("LastName" = 'Minnée' AND ("FirstName" = 'Sam' OR "Age" = '17')) ### Exclude -The `exclude()` method is the opposite to the filter in that it removes entries from a list. +The `exclude()` method is the opposite to the filter in that it removes entries +from a list. If we would like to remove all members from the list with the FirstName of Sam. @@ -201,7 +226,8 @@ Remove both Sam and Sig is as easy as. :::php $members = Member::get()->exclude('FirstName', array('Sam','Sig')); -As you can see it follows the same pattern as filter, so for removing only Sam Minnée from the list +As you can see it follows the same pattern as filter, so for removing only Sam +Minnée from the list: :::php $members = Member::get()->exclude(array( @@ -224,20 +250,24 @@ This would be equivalent to a SQL query of ### Search Filter Modifiers -The where clauses showcased in the previous two sections (filter and exclude) specify exact -matches by default. However, there are a number of suffixes that you can put on field names to change this -behaviour `":StartsWith"`, `":EndsWith"`, `":PartialMatch"`, `":GreaterThan"`, `":LessThan"`, `":Negation"`. +The where clauses showcased in the previous two sections (filter and exclude) +specify exact matches by default. However, there are a number of suffixes that +you can put on field names to change this behavior such as `":StartsWith"`, +`":EndsWith"`, `":PartialMatch"`, `":GreaterThan"`, `":LessThan"`, +`":Negation"`. -Each of these suffixes is represented in the ORM as a subclass of `[api:SearchFilter]`. Developers can define -their own SearchFilters if needing to extend the ORM filter and exclude behaviours. +Each of these suffixes is represented in the ORM as a subclass of +`[api:SearchFilter]`. Developers can define their own SearchFilters if needing +to extend the ORM filter and exclude behaviors. -These suffixes can also take modifiers themselves. The modifiers currently supported are `":not"`, `":nocase"` -and `":case"`. These negate the filter, make it case-insensitive and make it case-sensitive respectively. The -default comparison uses the database's default. For MySQL and MSSQL, this is case-insensitive. For PostgreSQL, -this is case-sensitive. +These suffixes can also take modifiers themselves. The modifiers currently +supported are `":not"`, `":nocase"` and `":case"`. These negate the filter, +make it case-insensitive and make it case-sensitive respectively. The default +comparison uses the database's default. For MySQL and MSSQL, this is +case-insensitive. For PostgreSQL, this is case-sensitive. -The following is a query which will return everyone whose first name doesn't start with S, who has logged in -since 1/1/2011. +The following is a query which will return everyone whose first name doesn't +start with S, who has logged in since 1/1/2011. :::php $members = Member::get()->filter(array( @@ -247,15 +277,17 @@ since 1/1/2011. ### Subtract -You can subtract entries from a DataList by passing in another DataList to `subtract()` +You can subtract entries from a DataList by passing in another DataList to +`subtract()` :::php $allSams = Member::get()->filter('FirstName', 'Sam'); $allMembers = Member::get(); $noSams = $allMembers->subtract($allSams); -Though for the above example it would probably be easier to use `filter()` and `exclude()`. A better -use case could be when you want to find all the members that does not exist in a Group. +Though for the above example it would probably be easier to use `filter()` and +`exclude()`. A better use case could be when you want to find all the members +that does not exist in a Group. :::php // ... Finding all members that does not belong to $group. @@ -263,15 +295,17 @@ use case could be when you want to find all the members that does not exist in a ### Limit -You can limit the amount of records returned in a DataList by using the `limit()` method. +You can limit the amount of records returned in a DataList by using the +`limit()` method. :::php // Returning the first 5 members, sorted alphabetically by Surname $members = Member::get()->sort('Surname')->limit(5); -`limit()` accepts two arguments, the first being the amount of results you want returned, with an optional second -parameter to specify the offset, which allows you to tell the system where to start getting the results from. The -offset, if not provided as an argument, will default to 0. +`limit()` accepts two arguments, the first being the amount of results you want +returned, with an optional second parameter to specify the offset, which allows +you to tell the system where to start getting the results from. The offset, if +not provided as an argument, will default to 0. :::php // Return 10 members with an offset of 4 (starting from the 5th result). @@ -280,27 +314,33 @@ offset, if not provided as an argument, will default to 0. ### Raw SQL options for advanced users -Occasionally, the system described above won't let you do exactly what you need to do. In these situations, we have -methods that manipulate the SQL query at a lower level. When using these, please ensure that all table & field names -are escaped with double quotes, otherwise some DB back-ends (e.g. PostgreSQL) won't work. +Occasionally, the system described above won't let you do exactly what you need +to do. In these situations, we have methods that manipulate the SQL query at a +lower level. When using these, please ensure that all table & field names are +escaped with double quotes, otherwise some DB back-ends (e.g. PostgreSQL) won't +work. -Under the hood, query generation is handled by the `[api:DataQuery]` class. This class does provide more direct -access to certain SQL features that `DataList` abstracts away from you. +Under the hood, query generation is handled by the `[api:DataQuery]` class. This +class does provide more direct access to certain SQL features that `DataList` +abstracts away from you. -In general, we advise against using these methods unless it's absolutely necessary. If the ORM doesn't do quite what -you need it to, you may also consider extending the ORM with new data types or filter modifiers (that documentation -still needs to be written) +In general, we advise against using these methods unless it's absolutely +necessary. If the ORM doesn't do quite what you need it to, you may also +consider extending the ORM with new data types or filter modifiers (that +documentation still needs to be written) #### Where clauses -You can specify a WHERE clause fragment (that will be combined with other filters using AND) with the `where()` method: +You can specify a WHERE clause fragment (that will be combined with other +filters using AND) with the `where()` method: :::php $members = Member::get()->where("\"FirstName\" = 'Sam'") #### Joining -You can specify a join with the innerJoin and leftJoin methods. Both of these methods have the same arguments: +You can specify a join with the innerJoin and leftJoin methods. Both of these +methods have the same arguments: * The name of the table to join to * The filter clause for the join @@ -310,11 +350,15 @@ For example: :::php // Without an alias - $members = Member::get()->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\""); - $members = Member::get()->innerJoin("Group_Members", "\"Rel\".\"MemberID\" = \"Member\".\"ID\"", "REl"); + $members = Member::get() + ->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\""); + + $members = Member::get() + ->innerJoin("Group_Members", "\"Rel\".\"MemberID\" = \"Member\".\"ID\"", "Rel"); -Passing a *$join* statement to DataObject::get will filter results further by the JOINs performed against the foreign -table. **It will NOT return the additionally joined data.** The returned *$records* will always be a +Passing a *$join* statement to DataObject::get will filter results further by +the JOINs performed against the foreign table. **It will NOT return the +additionally joined data.** The returned *$records* will always be a `[api:DataObject]`. ## Properties @@ -340,9 +384,11 @@ See [data-types](data-types) for all available types. ### Overloading -"Getters" and "Setters" are functions that help us save fields to our data objects. By default, the methods getField() -and setField() are used to set data object fields. They save to the protected array, $obj->record. We can overload the -default behaviour by making a function called "get``" or "set``". +"Getters" and "Setters" are functions that help us save fields to our data +objects. By default, the methods getField() and setField() are used to set data +object fields. They save to the protected array, $obj->record. We can overload +the default behavior by making a function called "get``" or +"set``". :::php class Player extends DataObject { @@ -359,17 +405,22 @@ default behaviour by making a function called "get``" or "set`Title. +We can create new "virtual properties" which are not actually listed in +`private static $db` or stored in the database-row. + +Here we combined a Player's first name and surname, accessible through +$myPlayer->Title. :::php class Player extends DataObject { + public function getTitle() { return "{$this->FirstName} {$this->Surname}"; } // access through $myPlayer->Title = "John Doe"; - // just saves data on the object, please use $myPlayer->write() to save the database-row + // just saves data on the object, please use $myPlayer->write() to save + // the database-row public function setTitle($title) { list($firstName, $surName) = explode(' ', $title); $this->FirstName = $firstName; @@ -378,48 +429,55 @@ Here we combined a Player's first name and surname, accessible through $myPlayer }
-**CAUTION:** It is common practice to make sure that pairs of custom getters/setter deal with the same data, in a consistent -format. +**CAUTION:** It is common practice to make sure that pairs of custom +getters/setter deal with the same data, in a consistent format.
-**CAUTION:** Custom setters can be hard to debug: Please double check if you could transform your data in more -straight-forward logic embedded to your custom controller or form-saving. +**CAUTION:** Custom setters can be hard to debug: Please double check if you +could transform your data in more straight-forward logic embedded to your custom +controller or form-saving.
### Default Values -Define the default values for all the $db fields. This example sets the "Status"-column on Player to "Active" whenever a -new object is created. +Define the default values for all the $db fields. This example sets the +"Status"-column on Player to "Active" whenever a new object is created. :::php class Player extends DataObject { - public static $defaults = array( + + private static $defaults = array( "Status" => 'Active', ); }
-Note: Alternatively you can set defaults directly in the database-schema (rather than the object-model). See -[data-types](data-types) for details. +Note: Alternatively you can set defaults directly in the database-schema (rather +than the object-model). See [data-types](data-types) for details.
### Casting -Properties defined in *static $db* are automatically casted to their [data-types](data-types) when used in templates. -You can also cast the return-values of your custom functions (e.g. your "virtual properties"). -Calling those functions directly will still return whatever type your PHP code generates, -but using the *obj()*-method or accessing through a template will cast the value according to the $casting-definition. +Properties defined in *static $db* are automatically casted to their +[data-types](data-types) when used in templates. + +You can also cast the return-values of your custom functions (e.g. your "virtual +properties"). Calling those functions directly will still return whatever type +your PHP code generates, but using the *obj()*-method or accessing through a +template will cast the value according to the $casting-definition. :::php class Player extends DataObject { - public static $casting = array( + + private static $casting = array( "MembershipFee" => 'Currency', ); // $myPlayer->MembershipFee() returns a float (e.g. 123.45) // $myPlayer->obj('MembershipFee') returns a object of type Currency - // In a template: <% loop $MyPlayer %>MembershipFee.Nice<% end_loop %> returns a casted string (e.g. "$123.45") + // In a template: <% loop $MyPlayer %>MembershipFee.Nice<% end_loop %> + // returns a casted string (e.g. "$123.45") public function getMembershipFee() { return $this->Team()->BaseFee * $this->MembershipYears; } @@ -428,86 +486,98 @@ but using the *obj()*-method or accessing through a template will cast the value ## Relations -Relations are built through static array definitions on a class, in the format ` => ` +Relations are built through static array definitions on a class, in the format +` => `. ### has_one -A 1-to-1 relation creates a database-column called "``ID", in the example below this would be "TeamID" -on the "Player"-table. +A 1-to-1 relation creates a database-column called "``ID", in +the example below this would be "TeamID" on the "Player"-table. :::php // access with $myPlayer->Team() class Player extends DataObject { - public static $has_one = array( + + private static $has_one = array( "Team" => "Team", ); } -SilverStripe's `[api:SiteTree]` base-class for content-pages uses a 1-to-1 relationship to link to its -parent element in the tree: +SilverStripe's `[api:SiteTree]` base-class for content-pages uses a 1-to-1 +relationship to link to its parent element in the tree: :::php // access with $mySiteTree->Parent() class SiteTree extends DataObject { - public static $has_one = array( + private static $has_one = array( "Parent" => "SiteTree", ); } ### has_many -Defines 1-to-many joins. A database-column named ""``ID"" will to be created in the child-class. +Defines 1-to-many joins. A database-column named ""``ID"" +will to be created in the child-class.
-**CAUTION:** Please specify a $has_one-relationship on the related child-class as well, in order to have the necessary -accessors available on both ends. +**CAUTION:** Please specify a $has_one-relationship on the related child-class +as well, in order to have the necessary accessors available on both ends.
:::php // access with $myTeam->Players() or $player->Team() class Team extends DataObject { - public static $has_many = array( + + private static $has_many = array( "Players" => "Player", ); } + class Player extends DataObject { - public static $has_one = array( + + private static $has_one = array( "Team" => "Team", ); } -To specify multiple $has_manys to the same object you can use dot notation to distinguish them like below +To specify multiple $has_manys to the same object you can use dot notation to +distinguish them like below :::php class Person extends DataObject { - public static $has_many = array( + + private static $has_many = array( "Managing" => "Company.Manager", "Cleaning" => "Company.Cleaner", ); } class Company extends DataObject { - public static $has_one = array( + + private static $has_one = array( "Manager" => "Person", "Cleaner" => "Person" ); } -Multiple $has_one relationships are okay if they aren't linking to the same object type. +Multiple $has_one relationships are okay if they aren't linking to the same +object type. :::php /** * THIS IS BAD */ class Team extends DataObject { - public static $has_many = array( + + private static $has_many = array( "Players" => "Player", ); } + class Player extends DataObject { - public static $has_one = array( + private static $has_one = array( "Team" => "Team", "AnotherTeam" => "Team", ); @@ -516,22 +586,26 @@ Multiple $has_one relationships are okay if they aren't linking to the same obje ### many_many -Defines many-to-many joins. A new table, (this-class)_(relationship-name), will be created with a pair of ID fields. +Defines many-to-many joins. A new table, (this-class)_(relationship-name), will +be created with a pair of ID fields.
-**CAUTION:** Please specify a $belongs_many_many-relationship on the related class as well, in order to have the necessary -accessors available on both ends. +**CAUTION:** Please specify a $belongs_many_many-relationship on the related +class as well, in order to have the necessary accessors available on both ends.
:::php // access with $myTeam->Categories() or $myCategory->Teams() class Team extends DataObject { - public static $many_many = array( + + private static $many_many = array( "Categories" => "Category", ); } + class Category extends DataObject { - public static $belongs_many_many = array( + + private static $belongs_many_many = array( "Teams" => "Team", ); } @@ -539,14 +613,16 @@ accessors available on both ends. ### Adding relations -Adding new items to a relations works the same, regardless if you're editing a *has_many*- or a *many_many*. -They are encapsulated by `[api:HasManyList]` and `[api:ManyManyList]`, both of which provide very similar APIs, -e.g. an `add()` and `remove()` method. +Adding new items to a relations works the same, regardless if you're editing a +*has_many*- or a *many_many*. They are encapsulated by `[api:HasManyList]` and +`[api:ManyManyList]`, both of which provide very similar APIs, e.g. an `add()` +and `remove()` method. :::php class Team extends DataObject { + // see "many_many"-description for a sample definition of class "Category" - public static $many_many = array( + private static $many_many = array( "Categories" => "Category", ); @@ -558,14 +634,15 @@ e.g. an `add()` and `remove()` method. ### Custom Relations -You can use the flexible datamodel to get a filtered result-list without writing any SQL. For example, this snippet -gets you the "Players"-relation on a team, but only containing active players. +You can use the flexible datamodel to get a filtered result-list without writing +any SQL. For example, this snippet gets you the "Players"-relation on a team, +but only containing active players. See `[api:DataObject::$has_many]` for more info on the described relations. :::php class Team extends DataObject { - public static $has_many = array( + private static $has_many = array( "Players" => "Player" ); @@ -575,39 +652,45 @@ See `[api:DataObject::$has_many]` for more info on the described relations. } } -Note: Adding new records to a filtered `RelationList` like in the example above doesn't automatically set the -filtered criteria on the added record. +Note: Adding new records to a filtered `RelationList` like in the example above +doesn't automatically set the filtered criteria on the added record. ### Relations on Unsaved Objects -You can also set *has_many* and *many_many* relations before the `DataObject` is saved. This behaviour uses the -`[api:UnsavedRelationList]` and converts it into the correct `RelationList` when saving the `DataObject` for the -first time. +You can also set *has_many* and *many_many* relations before the `DataObject` is +saved. This behaviour uses the `[api:UnsavedRelationList]` and converts it into +the correct `RelationList` when saving the `DataObject` for the first time. -This unsaved lists will also recursively save any unsaved objects that they contain. +This unsaved lists will also recursively save any unsaved objects that they +contain. -As these lists are not backed by the database, most of the filtering methods on `DataList` cannot be used on a -list of this type. As such, an `UnsavedRelationList` should only be used for setting a relation before saving an -object, not for displaying the objects contained in the relation. +As these lists are not backed by the database, most of the filtering methods on +`DataList` cannot be used on a list of this type. As such, an +`UnsavedRelationList` should only be used for setting a relation before saving +an object, not for displaying the objects contained in the relation. ## Validation and Constraints -Traditionally, validation in SilverStripe has been mostly handled on the controller -through [form validation](/topics/form-validation). +Traditionally, validation in SilverStripe has been mostly handled on the +controller through [form validation](/topics/form-validation). + While this is a useful approach, it can lead to data inconsistencies if the record is modified outside of the controller and form context. -Most validation constraints are actually data constraints which belong on the model. -SilverStripe provides the `[api:DataObject->validate()]` method for this purpose. + +Most validation constraints are actually data constraints which belong on the +model. SilverStripe provides the `[api:DataObject->validate()]` method for this +purpose. By default, there is no validation - objects are always valid! However, you can overload this method in your DataObject sub-classes to specify custom validation, or use the hook through `[api:DataExtension]`. -Invalid objects won't be able to be written - a [api:ValidationException]` -will be thrown and no write will occur. -It is expected that you call validate() in your own application to test that an object -is valid before attempting a write, and respond appropriately if it isn't. +Invalid objects won't be able to be written - a [api:ValidationException]` will +be thrown and no write will occur. + +It is expected that you call validate() in your own application to test that an +object is valid before attempting a write, and respond appropriately if it isn't. The return value of `validate()` is a `[api:ValidationResult]` object. You can append your own errors in there. @@ -616,6 +699,7 @@ Example: Validate postcodes based on the selected country :::php class MyObject extends DataObject { + private static $db = array( 'Country' => 'Varchar', 'Postcode' => 'Varchar' @@ -632,21 +716,23 @@ Example: Validate postcodes based on the selected country ## Maps -A map is an array where the array indexes contain data as well as the values. You can build a map -from any DataList like this: +A map is an array where the array indexes contain data as well as the values. +You can build a map from any DataList like this: :::php $members = Member::get()->map('ID', 'FirstName'); -This will return a map where the keys are Member IDs, and the values are the corresponding FirstName -values. Like everything else in the ORM, these maps are lazy loaded, so the following code will only -query a single record from the database: +This will return a map where the keys are Member IDs, and the values are the +corresponding FirstName values. Like everything else in the ORM, these maps are +lazy loaded, so the following code will only query a single record from the +database: :::php $members = Member::get()->map('ID', 'FirstName'); echo $member[5]; -This functionality is provided by the `SS_Map` class, which can be used to build a map around any `SS_List`. +This functionality is provided by the `SS_Map` class, which can be used to build +a map around any `SS_List`. :::php $members = Member::get(); @@ -657,8 +743,9 @@ through `[api:SS_List->column()]`. ## Data Handling -When saving data through the object model, you don't have to manually escape strings to create SQL-safe commands. -You have to make sure though that certain properties are not overwritten, e.g. *ID* or *ClassName*. +When saving data through the object model, you don't have to manually escape +strings to create SQL-safe commands. You have to make sure though that certain +properties are not overwritten, e.g. *ID* or *ClassName*. ### Creation @@ -689,8 +776,9 @@ You have to make sure though that certain properties are not overwritten, e.g. * ); -Alternatively you can use *castedUpdate()* to respect the [data-types](/topics/data-types). This is preferred to manually -casting data before saving. +Alternatively you can use *castedUpdate()* to respect the +[data-types](/topics/data-types). This is preferred to manually casting data +before saving. :::php $myPlayer->castedUpdate( @@ -703,20 +791,24 @@ casting data before saving. ### onBeforeWrite -You can customize saving-behaviour for each DataObject, e.g. for adding workflow or data customization. The function is -triggered when calling *write()* to save the object to the database. This includes saving a page in the CMS or altering -a ModelAdmin record. +You can customize saving-behaviour for each DataObject, e.g. for adding workflow +or data customization. The function is triggered when calling *write()* to save +the object to the database. This includes saving a page in the CMS or altering a +ModelAdmin record. -Example: Disallow creation of new players if the currently logged-in player is not a team-manager. +Example: Disallow creation of new players if the currently logged-in player is +not a team-manager. :::php class Player extends DataObject { - public static $has_many = array( + + private static $has_many = array( "Teams"=>"Team" ); public function onBeforeWrite() { - // check on first write action, aka "database row creation" (ID-property is not set) + // check on first write action, aka "database row creation" + // (ID-property is not set) if(!$this->ID) { $currentPlayer = Member::currentUser(); if(!$currentPlayer->IsTeamManager()) { @@ -727,31 +819,34 @@ Example: Disallow creation of new players if the currently logged-in player is n // check on every write action if(!$this->record['TeamID']) { - user_error('Cannot save player without a valid team-connection', E_USER_ERROR); + user_error('Cannot save player without a valid team', E_USER_ERROR); exit(); } - // CAUTION: You are required to call the parent-function, otherwise SilverStripe will not execute the request. + // CAUTION: You are required to call the parent-function, otherwise + // SilverStripe will not execute the request. parent::onBeforeWrite(); } }
-Note: There are no separate methods for *onBeforeCreate* and *onBeforeUpdate*. Please check for the existence of -$this->ID to toggle these two modes, as shown in the example above. +Note: There are no separate methods for *onBeforeCreate* and *onBeforeUpdate*. +Please check for the existence of $this->ID to toggle these two modes, as shown +in the example above.
### onBeforeDelete Triggered before executing *delete()* on an existing object. -Example: Checking for a specific [permission](/reference/permission) to delete this type of object. -It checks if a member is logged in who belongs to a group containing the permission "PLAYER_DELETE". +Example: Checking for a specific [permission](/reference/permission) to delete +this type of object. It checks if a member is logged in who belongs to a group +containing the permission "PLAYER_DELETE". :::php class Player extends DataObject { - public static $has_many = array( + private static $has_many = array( "Teams"=>"Team" ); @@ -771,20 +866,25 @@ See [forms](/topics/forms). ### Saving data with custom SQL -See the ["sql queries" topic](/reference/sqlquery) for custom *INSERT*, *UPDATE*, *DELETE* queries. +See the ["sql queries" topic](/reference/sqlquery) for custom *INSERT*, +*UPDATE*, *DELETE* queries. ## Extending DataObjects -You can add properties and methods to existing `[api:DataObjects]`s like `[api:Member]` (a core class) without -hacking core code or subclassing. See `[api:DataExtension]` for a general description, and `[api:Hierarchy]` for -the most popular examples. +You can add properties and methods to existing `[api:DataObjects]`s like +`[api:Member]` (a core class) without hacking core code or subclassing. See +`[api:DataExtension]` for a general description, and `[api:Hierarchy]` for the +most popular examples. ## FAQ ### What's the difference between DataObject::get() and a relation-getter? -You can work with both in pretty much the same way, but relationship-getters return a special type of collection: -A `[api:HasManyList]` or a `[api:ManyManyList]` with relation-specific functionality. +You can work with both in pretty much the same way, but relationship-getters +return a special type of collection: + +A `[api:HasManyList]` or a `[api:ManyManyList]` with relation-specific +functionality. :::php $myTeams = $myPlayer->Team(); // returns HasManyList diff --git a/docs/en/topics/forms.md b/docs/en/topics/forms.md index f2a50195f..e48587d34 100644 --- a/docs/en/topics/forms.md +++ b/docs/en/topics/forms.md @@ -1,11 +1,13 @@ # Forms -HTML forms are in practice the most used way to communicate with a browser. SilverStripe provides classes to generate -and handle the actions and data from a form. +HTML forms are in practice the most used way to communicate with a browser. +SilverStripe provides classes to generate and handle the actions and data from a +form. ## Overview -A fully implemented form in SilverStripe includes a couple of classes that individually have separate concerns. +A fully implemented form in SilverStripe includes a couple of classes that +individually have separate concerns. * Controller - Takes care of assembling the form and receiving data from it. * Form - Holds sets of fields, actions and validators. @@ -13,19 +15,22 @@ A fully implemented form in SilverStripe includes a couple of classes that indiv * FormActions - Often submit buttons that executes actions. * Validators - Validate the whole form, see [Form validation](form-validation.md) topic for more information. -Depending on your needs you can customize and override any of the above classes, however the defaults are often -sufficient. +Depending on your needs you can customize and override any of the above classes, +however the defaults are often sufficient. ## The Controller -Forms start at the controller. Here is a simple example on how to set up a form in a controller. +Forms start at the controller. Here is a simple example on how to set up a form +in a controller. **Page.php** :::php class Page_Controller extends ContentController { - public static $allowed_actions = array('HelloForm'); + private static $allowed_actions = array( + 'HelloForm' + ); // Template method public function HelloForm() { @@ -45,18 +50,20 @@ Forms start at the controller. Here is a simple example on how to set up a form } } -The name of the form ("HelloForm") is passed into the `Form` -constructor as a second argument. It needs to match the method name. +The name of the form ("HelloForm") is passed into the `Form` constructor as a +second argument. It needs to match the method name. -Since forms need a URL, the `HelloForm()` method needs to be handled -like any other controller action. In order to whitelist its access through -URLs, we add it to the `$allowed_actions` array. -Form actions ("doSayHello") on the other hand should NOT be included here, -these are handled separately through `Form->httpSubmission()`. -You can control access on form actions either by conditionally removing -a `FormAction` from the form construction, -or by defining `$allowed_actions` in your own `Form` class -(more information in the ["controllers" topic](/topics/controllers)). +Since forms need a URL, the `HelloForm()` method needs to be handled like any +other controller action. In order to whitelist its access through URLs, we add +it to the `$allowed_actions` array. + +Form actions ("doSayHello") on the other hand should NOT be included here, these +are handled separately through `Form->httpSubmission()`. + +You can control access on form actions either by conditionally removing a +`FormAction` from the form construction, or by defining `$allowed_actions` in +your own `Form` class (more information in the +["controllers" topic](/topics/controllers)). **Page.ss** @@ -65,8 +72,8 @@ or by defining `$allowed_actions` in your own `Form` class
$HelloForm
- Be sure to add the Form name 'HelloForm' to the Controller::$allowed_actions() to be sure that form submissions - get through to the correct action. +Be sure to add the Form name 'HelloForm' to the Controller::$allowed_actions() +to be sure that form submissions get through to the correct action.
@@ -78,13 +85,15 @@ documentation or the API documentation for `[api:Object]`::create(). ## The Form -Form is the base class of all forms in a SilverStripe application. Forms in your application can be created either by -instantiating the Form class itself, or by subclassing it. +Form is the base class of all forms in a SilverStripe application. Forms in your +application can be created either by instantiating the Form class itself, or by +subclassing it. ### Instantiating a form -Creating a form is a matter of defining a method to represent that form. This method should return a form object. The -constructor takes the following arguments: +Creating a form is a matter of defining a method to represent that form. This +method should return a form object. The constructor takes the following +arguments: * `$controller`: This must be and instance of the controller that contains the form, often `$this`. * `$name`: This must be the name of the method on that controller that is called to return the form. The first two @@ -141,7 +150,7 @@ data. :::php class Page_Controller extends ContentController { - public static $allowed_actions = array( + private static $allowed_actions = array( 'HelloForm', ); @@ -161,6 +170,7 @@ data. EmailField::create("Email"), PasswordField::create("Password") ); + $actions = new FieldList(FormAction::create("login")->setTitle("Log in")); parent::__construct($controller, $name, $fields, $actions); @@ -180,8 +190,9 @@ There are many classes extending `[api:FormField]`. There is a full overview at ### Using Form Fields -To get these fields automatically rendered into a form element, all you need to do is create a new instance of the -class, and add it to the fieldlist of the form. +To get these fields automatically rendered into a form element, all you need to +do is create a new instance of the class, and add it to the `FieldList` of the +form. :::php $form = new Form( @@ -202,8 +213,9 @@ class, and add it to the fieldlist of the form. ## Readonly -You can turn a form or individual fields into a readonly version. This is handy in the case of confirmation pages or -when certain fields can be edited due to permissions. +You can turn a form or individual fields into a readonly version. This is handy +in the case of confirmation pages or when certain fields can be edited due to +permissions. Readonly on a Form @@ -242,6 +254,7 @@ First of all, you need to create your form on it's own class, that way you can d EmailField::create("Email"), PasswordField::create("Password") ); + $actions = new FieldList(FormAction::create("login")->setTitle("Log in")); parent::__construct($controller, $name, $fields, $actions); } @@ -256,11 +269,12 @@ First of all, you need to create your form on it's own class, that way you can d } } -`MyForm->forTemplate()` tells the `[api:Form]` class to render with a template of return value of `$this->class`, which in this case -is *MyForm*. If the template doesn't exist, then it falls back to using Form.ss. +`MyForm->forTemplate()` tells the `[api:Form]` class to render with a template +of return value of `$this->class`, which in this case is *MyForm*. If the +template doesn't exist, then it falls back to using Form.ss. -*MyForm.ss* should then be placed into your *templates/Includes* directory for your project. Here is an example of -basic customisation: +*MyForm.ss* should then be placed into your *templates/Includes* directory for +your project. Here is an example of basic customization: :::ss
@@ -314,30 +328,34 @@ Will be rendered as: :::html -Each form field is rendered into a form via the `[FormField->FieldHolder()](api:FormField)` method, which includes -a container `
` as well as a `