diff --git a/docs/en/02_Developer_Guides/00_Model/01_Data_Model_and_ORM.md b/docs/en/02_Developer_Guides/00_Model/01_Data_Model_and_ORM.md index d07b0e282..2c7bc34dc 100644 --- a/docs/en/02_Developer_Guides/00_Model/01_Data_Model_and_ORM.md +++ b/docs/en/02_Developer_Guides/00_Model/01_Data_Model_and_ORM.md @@ -1,14 +1,14 @@ title: Introduction to the Data Model and ORM -summary: Introduction to creating and querying a Data Model through the ORM. +summary: Introduction to creating and querying a database records through the ORM (object-relational model) # Introduction to the Data Model and ORM -SilverStripe uses an [object-relational model](http://en.wikipedia.org/wiki/Object-relational_model) to represent it's +SilverStripe uses an [object-relational model](http://en.wikipedia.org/wiki/Object-relational_model) to represent its information. -* 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. +* 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]. The [api:DataObject] class represents a single row in a database table, following the ["Active Record"](http://en.wikipedia.org/wiki/Active_record_pattern) @@ -38,8 +38,8 @@ so on. After writing this class, we need to regenerate the database schema. ## Generating the Database Schema -After adding, modifying or removing `DataObject` classes make sure to rebuild your SilverStripe database. The -database-schema is generated automatically by visiting the URL http://www.yoursite.com/dev/build. +After adding, modifying or removing `DataObject` subclasses, make sure to rebuild your SilverStripe database. The +database schema is generated automatically by visiting the URL http://www.yoursite.com/dev/build while authenticated as an administrator. This script will analyze the existing schema, compare it to what's required by your data classes, and alter the schema as required. @@ -54,9 +54,9 @@ It will perform the following changes: It **won't** do any of the following - * Deleting tables - * Deleting fields - * Rename any tables that it doesn't recognize - so other applications can co-exist in the same database, as long as + * Delete tables + * Delete fields + * Rename any tables that it doesn't recognize. This allows other applications to coexist in the same database, as long as their table names don't match a SilverStripe data class. @@ -117,6 +117,11 @@ Or, a better way is to use the `create` method. :::php $player = Player::create(); +
+Using the `create()` method provides chainability, which can create add elegance and brevity to your code, e.g. `Player::create()->write()`. More importantly, however, it will look up the class in the [Injector](../extending/injector) so that can the class can be overriden by [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection). +
+ + Database columns and properties can be set as class properties on the object. The SilverStripe ORM handles the saving of the values through a custom `__set()` method. @@ -124,12 +129,18 @@ of the values through a custom `__set()` method. $player->FirstName = "Sam"; $player->PlayerNumber = 07; -To save the `DataObject` to the database use the `write()` method. The first time `write()` is called an `ID` will be +To save the `DataObject` to the database, use the `write()` method. The first time `write()` is called, an `ID` will be set. :::php $player->write(); +For convenience, the `write()` method returns the record's ID. This is particularly useful when creating new records. + + :::php + $player = Player::create(); + $id = $player->write(); + ## Querying Data With the `Player` class defined we can query our data using the `ORM` or Object-Relational Model. The `ORM` provides @@ -166,7 +177,7 @@ Provided `filter` values are automatically escaped and do not require any escapi The `ORM` doesn't actually execute the [api:SQLQuery] until you iterate on the result with a `foreach()` or `<% loop %>`. -It's smart enough to generate a single efficient query at the last moment in time without needing to post process the +It's 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 :::php @@ -202,6 +213,15 @@ This also means that getting the count of a list of objects will be done with a echo $player->FirstName; } +Notice that we can step into the loop safely without having to check if `$players` exists. The `get()` call is robust, and will at worst return an empty `DataList` object. If you do want to check if the query returned any records, you can use the `exists()` method, e.g. + + :::php + $players = Player::get(); + + if($players->exists()) { + // do something here + } + See the [Lists](../lists) documentation for more information on dealing with [api:SS_List] instances. ## Returning a single DataObject @@ -279,7 +299,7 @@ So, this would return only those players called "Sam Minnée". // SELECT * FROM Player WHERE FirstName = 'Sam' AND LastName = 'Minnée' -There is also a short hand way of getting Players with the FirstName of Sam. +There is also a shorthand way of getting Players with the FirstName of Sam. :::php $players = Player::get()->filter('FirstName', 'Sam'); @@ -342,7 +362,9 @@ It is also possible to filter by a PHP callback, this will force the data model PHP, thus `filter()` or `filterAny()` are to be preferred over `filterByCallback()`.
-Because `filterByCallback()` has to run in PHP, it will always return an `ArrayList` +Because `filterByCallback()` has to run in PHP, it has a significant performance tradeoff, and should not be used on large recordsets. + +`filterByCallback()` will always return an `ArrayList`.
The first parameter to the callback is the item, the second parameter is the list itself. The callback will run once @@ -436,8 +458,8 @@ Note that the `limit` argument order is different from a MySQL LIMIT clause. ### Raw SQL 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. +methods that manipulate the SQL query at a lower level. When using these, please ensure that all table and field names +are escaped with double quotes, otherwise some DB backends (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. @@ -548,7 +570,7 @@ special fields. This is called the "base table". In our case, `SiteTree` is the sub-classes of the base class (including the base class itself). * Each sub-class of the base object will also be given its own table, *as long as it has custom fields*. In the -example above, NewsSection didn't have its own data and so an extra table would be redundant. +example above, NewsSection didn't have its own data, so an extra table would be redundant. * In all the tables, ID is the primary key. A matching ID number is used for all parts of a particular record: record #2 in Page refers to the same object as record #2 in `[api:SiteTree]`. diff --git a/docs/en/02_Developer_Guides/00_Model/02_Relations.md b/docs/en/02_Developer_Guides/00_Model/02_Relations.md index 687e99402..e07ec169b 100644 --- a/docs/en/02_Developer_Guides/00_Model/02_Relations.md +++ b/docs/en/02_Developer_Guides/00_Model/02_Relations.md @@ -1,5 +1,5 @@ title: Relations between Records -summary: Relate models together using the ORM. +summary: Relate models together using the ORM using has_one, has_many, and many_many. # Relations between Records @@ -39,6 +39,8 @@ A 1-to-1 relation creates a database-column called "``ID", in This defines a relationship called `Team` which links to a `Team` class. The `ORM` handles navigating the relationship and provides a short syntax for accessing the related object. +At the database level, the `has_one` creates a `TeamID` field on `Player`. A `has_many` field does not impose any database changes. It merely injects a new method into the class to access the related records (in this case, `Players()`) + :::php $player = Player::get()->byId(1); @@ -59,8 +61,7 @@ The relationship can also be navigated in [templates](../templates). ## has_many -Defines 1-to-many joins. A database-column named ""``ID"" will to be created in the child-class. As -you can see from the previous example, `$has_many` goes hand in hand with `$has_one`. +Defines 1-to-many joins. As you can see from the previous example, `$has_many` goes hand in hand with `$has_one`.
Please specify a $has_one-relationship on the related child-class as well, in order to have the necessary accessors @@ -184,7 +185,7 @@ available on both ends. ); } -Much like the `has_one` relationship, `mant_many` can be navigated through the `ORM` as well. The only difference being +Much like the `has_one` relationship, `many_many` can be navigated through the `ORM` as well. The only difference being you will get an instance of [api:ManyManyList] rather than the object. :::php @@ -203,6 +204,11 @@ The relationship can also be navigated in [templates](../templates). <% end_if %> <% end_with %> +## many_many or belongs_many_many? + +If you're unsure about whether an object should take on `many_many` or `belongs_many_many`, the best way to think about it is that the object where the relationship will be edited (i.e. via checkboxes) should contain the `many_many`. For instance, in a `many_many` of Product => Categories, the `Product` should contain the `many_many`, because it is much more likely that the user will select Categories for a Product than vice-versa. + + ## 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 diff --git a/docs/en/02_Developer_Guides/00_Model/03_Lists.md b/docs/en/02_Developer_Guides/00_Model/03_Lists.md index b87a86bce..11f830894 100644 --- a/docs/en/02_Developer_Guides/00_Model/03_Lists.md +++ b/docs/en/02_Developer_Guides/00_Model/03_Lists.md @@ -1,5 +1,5 @@ title: Managing Lists -summary: Learn how to manipulate SS_List objects. +summary: The SS_List interface allows you to iterate through and manipulate a list of objects. # Managing Lists @@ -9,7 +9,7 @@ modify. ## Iterating over the list. -[api:SS_List] implements `IteratorAggregate` allowing you to loop over the instance. +[api:SS_List] implements `IteratorAggregate`, allowing you to loop over the instance. :::php $members = Member::get(); diff --git a/docs/en/02_Developer_Guides/00_Model/04_Data_Types_and_Casting.md b/docs/en/02_Developer_Guides/00_Model/04_Data_Types_and_Casting.md index b077740a4..6a31ea375 100644 --- a/docs/en/02_Developer_Guides/00_Model/04_Data_Types_and_Casting.md +++ b/docs/en/02_Developer_Guides/00_Model/04_Data_Types_and_Casting.md @@ -1,5 +1,5 @@ title: Data Types, Overloading and Casting -summary: Documentation on how data is stored going in, coming out of the ORM and how to modify it. +summary: Learn how how data is stored going in and coming out of the ORM and how to modify it. # Data Types and Casting @@ -7,7 +7,7 @@ Each model in a SilverStripe [api:DataObject] will handle data at some point. Th the ones defined in a `$db` array or simply a method that returns data for the template. A Data Type is represented in SilverStripe by a [api:DBField] subclass. The class is responsible for telling the ORM -about how to store it's data in the database and how to format the information coming out of the database. +about how to store its data in the database and how to format the information coming out of the database, i.e. on a template. In the `Player` example, we have four database columns each with a different data type (Int, Varchar). diff --git a/docs/en/02_Developer_Guides/00_Model/05_Extending_DataObjects.md b/docs/en/02_Developer_Guides/00_Model/05_Extending_DataObjects.md index db8f0f61f..e10ec2267 100644 --- a/docs/en/02_Developer_Guides/00_Model/05_Extending_DataObjects.md +++ b/docs/en/02_Developer_Guides/00_Model/05_Extending_DataObjects.md @@ -12,7 +12,7 @@ data records. ## onBeforeWrite -You can customize saving-behaviour for each DataObject, e.g. for adding workflow or data customization. The function is +You can customize saving-behavior 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. diff --git a/docs/en/02_Developer_Guides/00_Model/06_SearchFilters.md b/docs/en/02_Developer_Guides/00_Model/06_SearchFilters.md index 41724d67e..e0b1f9b4c 100644 --- a/docs/en/02_Developer_Guides/00_Model/06_SearchFilters.md +++ b/docs/en/02_Developer_Guides/00_Model/06_SearchFilters.md @@ -32,11 +32,11 @@ An example of a `SearchFilter` in use: Developers can define their own [api:SearchFilter] 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 +`":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 starts with S either lower or uppercase +The following is a query which will return everyone whose first name starts with "S", either lowercase or uppercase: :::php $players = Player::get()->filter(array( diff --git a/docs/en/02_Developer_Guides/00_Model/10_Versioning.md b/docs/en/02_Developer_Guides/00_Model/10_Versioning.md index db0b43439..9494b6f73 100644 --- a/docs/en/02_Developer_Guides/00_Model/10_Versioning.md +++ b/docs/en/02_Developer_Guides/00_Model/10_Versioning.md @@ -3,7 +3,7 @@ summary: Add versioning to your database content through the Versioned extension # Versioning -Database content in SilverStripe can be "staged" before its publication, as well as tracking all changes through the +Database content in SilverStripe can be "staged" before its publication, as well as track all changes through the lifetime of a database record. It is most commonly applied to pages in the CMS (the `SiteTree` class). Draft content edited in the CMS can be different @@ -148,8 +148,16 @@ helpers. ### Templates Variables In templates, you don't need to worry about this distinction. The `$Content` variable contain the published content by -default, and only preview draft content if explicitly requested (e.g. by the "preview" feature in the CMS). If you want -to force a specific stage, we recommend the `Controller->init()` method for this purpose. +default, and only preview draft content if explicitly requested (e.g. by the "preview" feature in the CMS, or by adding ?stage=Stage to the URL). If you want +to force a specific stage, we recommend the `Controller->init()` method for this purpose, for example: + +**mysite/code/MyController.php** + :::php + public function init() { + parent::init(); + Versioned::set_reading_mode('Stage.Stage'); + } + ### Controllers diff --git a/docs/en/02_Developer_Guides/00_Model/11_Scaffolding.md b/docs/en/02_Developer_Guides/00_Model/11_Scaffolding.md index 2a014169f..3aeddd0f8 100644 --- a/docs/en/02_Developer_Guides/00_Model/11_Scaffolding.md +++ b/docs/en/02_Developer_Guides/00_Model/11_Scaffolding.md @@ -1,5 +1,5 @@ title: Building Model and Search Interfaces around Scaffolding -summary: Model Driven approach to defining your application UI. +summary: A Model-driven approach to defining your application UI. # Scaffolding @@ -24,7 +24,7 @@ An example is `DataObject`, SilverStripe will automatically create your CMS inte ); public function getCMSFields() { - // parent::getCMSFields() does all the hard work and creates the fields for title, isactive and content. + // parent::getCMSFields() does all the hard work and creates the fields for Title, IsActive and Content. $fields = parent::getCMSFields(); $fields->fieldByName('IsActive')->setTitle('Is active?'); @@ -32,6 +32,26 @@ An example is `DataObject`, SilverStripe will automatically create your CMS inte } } +To fully customize your form fields, start with an empty FieldList. + + :::php + setRows(5) + ) + ); + + return $fields; + } + + + You can also alter the fields of built-in and module `DataObject` classes through your own [DataExtension](../extensions), and a call to `DataExtension->updateCMSFields`. @@ -125,7 +145,7 @@ To include relations (`$has_one`, `$has_many` and `$many_many`) in your search, ### Summary Fields -Summary fields can be used to show a quick overview of the data for a specific [api:DataObject] record. Most common use +Summary fields can be used to show a quick overview of the data for a specific [api:DataObject] record. The most common use is their display as table columns, e.g. in the search results of a `[api:ModelAdmin]` CMS interface. :::php