diff --git a/docs/en/changelogs/3.0.0.md b/docs/en/changelogs/3.0.0.md
index c87047078..b0f824e33 100644
--- a/docs/en/changelogs/3.0.0.md
+++ b/docs/en/changelogs/3.0.0.md
@@ -193,6 +193,15 @@ The abstract `RelationList` class and its implementations `ManyManyList` and `Ha
are replacing the `ComponentSet` API, which is only relevant if you have instanciated these manually.
Relations are retrieved through the same way (e.g. `$myMember->Groups()`).
+### New ORM: DataObjectSet->groupBy() changed to GroupedList decorator
+
+ :::php
+ $members = Member::get();
+ // before
+ $grouped = $members->groupBy('Surname');
+ // after
+ $grouped = GroupedList::create($members)->groupBy('Surname');
+
### Aggregate changes for partial caching in templates ###
`DataObject::Aggregate()` and `DataObject::RelationshipAggregate()` are now deprecated. To replace your deprecated aggregate calls
diff --git a/docs/en/howto/csv-import.md b/docs/en/howto/csv-import.md
index a0aa50ba4..5c8253397 100644
--- a/docs/en/howto/csv-import.md
+++ b/docs/en/howto/csv-import.md
@@ -184,9 +184,7 @@ Sample implementation of a custom loader. Assumes a CSV-file in a certain format
$obj->LastName = $parts[1];
}
public static function getTeamByTitle(&$obj, $val, $record) {
- $SQL_val = Convert::raw2sql($val);
- return DataObject::get_one(
- 'FootballTeam', "Title = '{$SQL_val}'"
+ return FootballTeam::get()->filter('Title', $val)->First();
);
}
}
diff --git a/docs/en/howto/extend-cms-interface.md b/docs/en/howto/extend-cms-interface.md
index 51a3bf8ed..775d94205 100644
--- a/docs/en/howto/extend-cms-interface.md
+++ b/docs/en/howto/extend-cms-interface.md
@@ -115,7 +115,7 @@ Add the following code to a new file `zzz_admin/code/BookmarkedLeftAndMainExtens
where('"IsBookmarked" = 1');
+ return Page::get()->filter("IsBookmarked", 1);
}
}
diff --git a/docs/en/howto/grouping-dataobjectsets.md b/docs/en/howto/grouping-dataobjectsets.md
index 4be1c31eb..4c9498bed 100644
--- a/docs/en/howto/grouping-dataobjectsets.md
+++ b/docs/en/howto/grouping-dataobjectsets.md
@@ -1,15 +1,22 @@
-# Grouping Data Object Sets
+# Grouping lists of records
-The [api:DataObjectSet] class has a number of methods useful for grouping objects by fields. Together with sorting this
-can be used to break up long lists of data into more manageable sub-sections.
+The [api:SS_List] class is designed to return a flat list of records.
+These lists can get quite long, and hard to present on a single list.
+[Pagination](/howto/pagination) is one way to solve this problem,
+by splitting up the list into multiple pages.
-The [api:DataObjectSet->groupBy()] method takes a field name as the single argument, and breaks the set up into a number
-of arrays, where each array contains only objects with the same value of that field. The [api:DataObjectSet->GroupedBy()]
-method builds on this and returns the same data in a template-friendly format.
+In this howto, we present an alternative to pagination:
+Grouping a list by various criteria, through the `[api:GroupedList]` class.
+This class is a `[api:SS_ListDecorator]`, which means it wraps around a list,
+adding new functionality.
+
+It provides a `groupBy()` method, which takes a field name, and breaks up the managed list
+into a number of arrays, where each array contains only objects with the same value of that field.
+Similarly, the `GroupedBy()` method builds on this and returns the same data in a template-friendly format.
## Grouping Sets By First Letter
-This example deals with breaking up a [api:DataObjectSet] into sub-headings by the first letter.
+This example deals with breaking up a [api:SS_List] into sub-headings by the first letter.
Let's say you have a set of Module objects, each representing a SilverStripe module, and you want to output a list of
these in alphabetical order, with each letter as a heading; something like the following list:
@@ -23,31 +30,27 @@ these in alphabetical order, with each letter as a heading; something like the f
* Database Plumber
* ...
-The first step is to set up the basic data model, along with a method that returns the first letter of the title. This
+The first step is to set up the basic data model,
+along with a method that returns the first letter of the title. This
will be used both for grouping and for the title in the template.
:::php
class Module extends DataObject {
-
public static $db = array(
- 'Title' => 'Varchar(255)'
+ 'Title' => 'Text'
);
- // ...
-
/**
* Returns the first letter of the module title, used for grouping.
- *
* @return string
*/
public function getTitleFirstLetter() {
return $this->Title[0];
}
-
}
-The next step is to create a method or variable that will contain/return all the Module objects, sorted by title. For
-this example this will be a method on the Page class.
+The next step is to create a method or variable that will contain/return all the objects,
+sorted by title. For this example this will be a method on the `Page` class.
:::php
class Page extends SiteTree {
@@ -56,23 +59,22 @@ this example this will be a method on the Page class.
/**
* Returns all modules, sorted by their title.
- *
- * @return DataObjectSet
+ * @return GroupedList
*/
- public function getModules() {
- return DataObject::get('Module', null, '"Title"');
+ public function getGroupedModules() {
+ return GroupedList::create(Module::get()->sort('Title'));
}
}
-The final step is to render this into a template. The [api:DataObjectSet->GroupedBy()] method breaks up the set into
-a number of sets, grouped by the field that is passed as the parameter. In this case, the getTitleFirstLetter method
-defined earlier is used to break them up.
+The final step is to render this into a template. The `GroupedBy()` method breaks up the set into
+a number of sets, grouped by the field that is passed as the parameter.
+In this case, the `getTitleFirstLetter()` method defined earlier is used to break them up.
:::ss
- // Modules list grouped by TitleFirstLetter
+ <%-- Modules list grouped by TitleFirstLetter --%>
Modules
- <% control Modules.GroupedBy(TitleFirstLetter) %>
+ <% control GroupedModules.GroupedBy(TitleFirstLetter) %>
$TitleFirstLetter
<% control Children %>
@@ -83,61 +85,63 @@ defined earlier is used to break them up.
## Grouping Sets By Month
-Grouping a set by month is a very similar process. The only difference would be to sort the records by month name, and
-then create a method on the DataObject that returns the month name, and pass that to the [api:DataObjectSet->GroupedBy()]
-call.
+Grouping a set by month is a very similar process.
+The only difference would be to sort the records by month name, and
+then create a method on the DataObject that returns the month name,
+and pass that to the [api:GroupedList->GroupedBy()] call.
-Again, the first step is to create a method on the class in question that will be displayed in a list. For this example,
-a [api:DataObject] called NewsItem will be used. This will have a method which returns the month it was posted in:
+We're reusing our example `Module` object,
+but grouping by its built-in `Created` property instead,
+which is automatically set when the record is first written to the database.
+This will have a method which returns the month it was posted in:
:::php
- class NewsItem extends DataObject {
-
- public static $db = array(
- 'Title' => 'Varchar(255)',
- 'Date' => 'Date'
- );
+ class Module extends DataObject {
// ...
/**
* Returns the month name this news item was posted in.
- *
* @return string
*/
- public function getMonthPosted() {
- return date('F', strtotime($this->Date));
+ public function getMonthCreated() {
+ return date('F', strtotime($this->Created));
}
}
-The next step is to create a method that will return all the News records that exist, sorted by month name from
-January to December. This can be accomplshed by sorting by the Date field:
+The next step is to create a method that will return all records that exist,
+sorted by month name from January to December. This can be accomplshed by sorting by the `Created` field:
:::php
class Page extends SiteTree {
-
+
+ // ...
+
/**
* Returns all news items, sorted by the month they were posted
- *
- * @return DataObjectSet
+ * @return GroupedList
*/
- public function getNewsItems() {
- return DataObject::get('NewsItem', null, '"Date"');
+ public function getGroupedModulesByDate() {
+ return GroupedList::create(Module::get()->sort('Created'));
}
}
-The final step is the render this into the template using the [api:DataObjectSet->GroupedBy()] method.
+The final step is the render this into the template using the [api:GroupedList->GroupedBy()] method.
:::ss
// Modules list grouped by the Month Posted
Modules
- <% control NewsItems.GroupedBy(MonthPosted) %>
- $MonthPosted
+ <% control GroupedModulesByDate.GroupedBy(MonthCreated) %>
+ $MonthCreated
<% control Children %>
- - $Title ($Date.Nice)
+ - $Title ($Created.Nice)
<% end_control %>
- <% end_control %>
\ No newline at end of file
+ <% end_control %>
+
+## Related
+
+ * [Howto: "Pagination"](/howto/pagination)
\ No newline at end of file
diff --git a/docs/en/howto/index.md b/docs/en/howto/index.md
index 6c5e979a1..f57f0ab75 100644
--- a/docs/en/howto/index.md
+++ b/docs/en/howto/index.md
@@ -8,7 +8,7 @@ the language and functions which are used in the guides.
* [Import CSV Data](csv-import). Build a simple CSV importer using either [api:ModelAdmin] or a custom controller
* [Dynamic Default Fields](dynamic-default-fields). Pre populate a [api:DataObject] with data.
-* [Grouping DataObjectSets](grouping-dataobjectsets). Group results in a [api:DataObjectSet] to create sub sections.
+* [Grouping Lists](grouping-dataobjectsets). Group results in a [api:SS_List] to create sub sections.
* [PHPUnit Configuration](phpunit-configuration). How to setup your testing environment with PHPUnit
* [Extend the CMS Interface](extend-cms-interface).
* [How to customize CMS Tree](customize-cms-tree).
diff --git a/docs/en/howto/pagination.md b/docs/en/howto/pagination.md
index cf61691ea..3a7520b7d 100644
--- a/docs/en/howto/pagination.md
+++ b/docs/en/howto/pagination.md
@@ -1,11 +1,11 @@
# Paginating A List
-Adding pagination to a `[api:DataList]` or `[DataObjectSet]` is quite simple. All
+Adding pagination to a `[api:SS_List]` is quite simple. All
you need to do is wrap the object in a `[api:PaginatedList]` decorator, which takes
care of fetching a sub-set of the total list and presenting it to the template.
In order to create a paginated list, you can create a method on your controller
-that first creates a `DataList` that will return all pages, and then wraps it
+that first creates a `SS_List` that will return all pages, and then wraps it
in a `[api:PaginatedList]` object. The `PaginatedList` object is also passed the
HTTP request object so it can read the current page information from the
"?start=" GET var.
@@ -18,8 +18,7 @@ information.
* Returns a paginated list of all pages in the site.
*/
public function PaginatedPages() {
- $pages = DataList::create('Page');
- return new PaginatedList($pages, $this->request);
+ return new PaginatedList(Page::get(), $this->request);
}
## Setting Up The Template
@@ -72,3 +71,7 @@ list will already contain only the items that you wish to display on the current
page. In this situation the automatic limiting done by `[api:PaginatedList]`
will break the pagination. You can disable automatic limiting using the
`[api:PaginatedList->setLimitItems()]` method when using custom lists.
+
+## Related
+
+ * [Howto: "Grouping Lists"](/howto/grouping-dataobjectsets)
\ No newline at end of file
diff --git a/docs/en/misc/coding-conventions.md b/docs/en/misc/coding-conventions.md
index 5b2aa3cfd..e68bacf72 100644
--- a/docs/en/misc/coding-conventions.md
+++ b/docs/en/misc/coding-conventions.md
@@ -403,7 +403,7 @@ Example:
* This method returns something cool. {@link MyParentMethod} has other cool stuff in it.
*
* @param string $colour The colour of cool things that you want
- * @return DataObjectSet A list of everything cool
+ * @return DataList A list of everything cool
*/
public function myMethod($foo) {}
@@ -429,7 +429,7 @@ If you have to use raw SQL, make sure your code works across databases make sure
with the column or table name escaped with double quotes and values with single quotes.
:::php
- DataObject::get("MyClass", "\"Title\" = 'my title'");
+ MyClass::get()->where("\"Title\" = 'my title'");
Use [ANSI SQL](http://en.wikipedia.org/wiki/SQL#Standardization) format where possible.
diff --git a/docs/en/reference/dataobject.md b/docs/en/reference/dataobject.md
index bef4339a2..fbde2b59f 100644
--- a/docs/en/reference/dataobject.md
+++ b/docs/en/reference/dataobject.md
@@ -2,53 +2,67 @@
## Introduction
-A single database record & abstract class for the data-access-model.
+The `[api:DataObject]` class represents a single row in a database table,
+following the ["Active Record"](http://en.wikipedia.org/wiki/Active_record_pattern) design pattern.
-## Usage
+## Defining Properties
-* [datamodel](/topics/datamodel): The basic pricinples
-* [data-types](/topics/data-types): Casting and special property-parsing
-* `[api:DataObject]`: A "container" for DataObjects
+Properties defined through `DataObject::$db` map to table columns,
+and can be declared as different [data-types](/topics/data-types).
-## Basics
+## Loading and Saving Records
-The call to `DataObject->getCMSFields()` is the centerpiece of every data administration interface in SilverStripe,
-which returns a `[api:FieldList]`''.
+The basic principles around data persistence and querying for objects
+is explained in the ["datamodel" topic](/topics/datamodel).
+
+## Defining Form Fields
+
+In addition to defining how data is persisted, the class can also
+help with editing it by providing form fields through `DataObject->getCMSFields()`.
+The resulting `[api:FieldList]` is the centrepiece of many data administration interfaces in SilverStripe.
+Many customizations of the SilverStripe CMS interface start here,
+by adding, removing or configuring fields.
+
+ Example getCMSFields implementation
:::php
- class MyPage extends Page {
+ class MyDataObject extends DataObject {
+ $db = array(
+ 'IsActive' => 'Boolean'
+ );
+ public function getCMSFields() {
+ return new FieldSet(
+ new CheckboxField('IsActive')
+ );
+ }
+ }
+
+There's various [form field types](/references/form-field-types), for editing text, dates,
+restricting input to numbers, and much more.
+
+## Scaffolding Form Fields
+
+The ORM already has a lot of information about the data represented by a `DataObject`
+through its `$db` property, so why not use it to create form fields as well?
+If you call the parent implementation, the class will use `[api:FormScaffolder]`
+to provide reasonable defaults based on the property type (e.g. a checkbox field for booleans).
+You can then further customize those fields as required.
+
+ :::php
+ class MyDataObject extends DataObject {
+ // ...
public function getCMSFields() {
$fields = parent::getCMSFields();
- $fields->addFieldToTab('Root.Content',new CheckboxField('CustomProperty'));
+ $fields->fieldByName('IsActive')->setTitle('Is active?');
return $fields;
}
}
+The `[ModelAdmin](/reference/modeladmin)` class uses this approach to provide
+data management interfaces with very little custom coding.
-## Scaffolding Formfields
-
-These calls retrieve a `[api:FieldList]` for the area where you intend to work with the scaffolded form.
-
-### For the CMS
-
- :::php
- $fields = singleton('MyDataObject')->getCMSFields();
-
-
-### For the Frontend
-
-Used for simple frontend forms without relation editing or `[api:TabSet] behaviour. Uses `scaffoldFormFields()` by
-default. To customize, either overload this method in your subclass, or extend it by `DataExtension->updateFormFields()`.
-
- :::php
- $fields = singleton('MyDataObject')->getFrontEndFields();
-
-
-## Customizing Scaffolded Fields
-
-This section covers how to enhance the default scaffolded form fields from above. It is particularly useful when used
-in conjunction with the `[api:ModelAdmin]` in the CMS to make relevant data administration interfaces.
-
+You can also alter the fields of built-in and module `DataObject` classes through
+your own `[DataExtension](/reference/dataextension)`, and a call to `[api:DataExtension->updateCMSFields()]`.
### Searchable Fields
diff --git a/docs/en/reference/dataobjectset.md b/docs/en/reference/dataobjectset.md
deleted file mode 100644
index 97e45fb4b..000000000
--- a/docs/en/reference/dataobjectset.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# DataObjectSet
-
-## Introduction
-
-This class represents a set of `[api:DataObject]`s, such as the results of a query. It is the base for all
-[datamodel](/topics/datamodel)-related querying. It implements the [Iterator
-interface](http://php.net/manual/en/language.oop5.iterations.php) introduced in PHP5.
-
-Relations (`has_many`/`many_many`) are described in `[api:ComponentSet]`, a subclass of `[api:DataObjectSet]`.
-
-## Usage
-
-### Getting the size
-
- :::php
- $mySet->Count();
-
-### Getting an single element
-
- :::php
- $myFirstDataObject = $mySet->First();
- $myLastDataObject = $mySet->Last();
-
-
-### Getting multiple elements
-
- :::php
- $mySpecialDataObjects = $mySet->find('Status', 'special');
- $startingFromTen = $mySet->getOffset(10);
- $tenToTwenty = $mySet->getRange(10, 10);
-
-
-### Getting one property
-
- :::php
- $myIDArray = $mySet->column('ID');
-
-### Grouping
-
-You can group a set by a specific column. Consider using `[api:SQLQuery]` with a *GROUP BY* statement for enhanced
-performance.
-
- :::php
- $groupedSet = $mySet->groupBy('Lastname');
-
-### Sorting
-
-Sort a set by a specific column.
-
- :::php
- $mySet->sort('Lastname'); //ascending
- $mySet->sort('Lastname', 'DESC'); //descending
-
-This works on the object itself, so do NOT do something like this:
-
- :::php
- $sortedSet = $mySet->sort('Lastname'); //ascending
-
-## Merge with other `[api:DataObjectSet]`s
-
- :::php
- $myFirstSet->merge($mySecondSet);
- // $myFirstSet now contains all combined values
-
-
-### Mapping for Dropdowns
-
-When using `[api:DropdownField]` and its numerous subclasses to select a value from a set, you can easily map
-the records to a compatible array:
-
- :::php
- $map = $mySet->toDropDownMap('ID', 'Title');
- $dropdownField = new DropdownField('myField', 'my label', $map);
-
-
-### Converting to array
-
- :::php
- $myArray = $mySet->toArray();
-
-### Checking for existence
-
-It is good practice to check for empty sets before doing any iteration.
-
- :::php
- $mySet = DataObject::get('Players');
- if($mySet->exists()) foreach($mySet as $player)
-
-### Paging
-
-`[api:DataObject]`s have native support for dealing with **pagination**.
-See *setPageLimits*, *setPageLength*, etc.
-
-FIXME Complete pagination documentation
-
-
-## API Documentation
-`[api:DataObjectSet]`
diff --git a/docs/en/reference/execution-pipeline.md b/docs/en/reference/execution-pipeline.md
index 588a978a2..a196cd9b9 100644
--- a/docs/en/reference/execution-pipeline.md
+++ b/docs/en/reference/execution-pipeline.md
@@ -48,17 +48,14 @@ mod_rewrite works.
## main.php
-All requests go through main.php, which sets up the environment and then hands control over to Director.
+All requests go through `main.`php, which sets up the environment and then hands control over to `Director`.
-**See:** The API documentation of `[api:Main]` for information about how main.php processes requests.
## Director and URL patterns
main.php relies on `[api:Director]` to work out which controller should handle this request. `[api:Director]` will instantiate that
controller object and then call `[api:Controller::run()]`.
-**See:** The API documentation of `[api:Director]` for information about how Director parses URLs and hands control over to a controller object.
-
-In general, the URL is build up as follows: page/action/ID/otherID - e.g. http://www.mysite.com/mypage/addToCart/12.
+In general, the URL is build up as follows: `page/action/ID/otherID` - e.g. http://www.mysite.com/mypage/addToCart/12.
This will add an object with ID 12 to the cart.
When you create a function, you can access the ID like this:
@@ -67,7 +64,7 @@ When you create a function, you can access the ID like this:
public function addToCart ($request) {
$param = $r->allParams();
echo "my ID = ".$param["ID"];
- $obj = DataObject::get("myProduct", $param["ID"]);
+ $obj = MyProduct::get()->byID($param["ID"]);
$obj->addNow();
}
diff --git a/docs/en/reference/index.md b/docs/en/reference/index.md
index 0d04d599e..67be50460 100644
--- a/docs/en/reference/index.md
+++ b/docs/en/reference/index.md
@@ -9,7 +9,6 @@ Reference articles complement our auto-generated [API docs](http://api.silverstr
* [Database Structure](database-structure): Conventions and best practices for database tables and fields
* [DataObject](dataobject): Base class for database records
* [DataExtension](dataextension): A "mixin" system allowing to extend core classes
-* [DataObjectSet](dataobjectset): The base collection of database records in the ORM
* [Director](director): Routes URLs and handles HTTP requests
* [Execution Pipeline](execution-pipeline): Detailed look on the way an HTTP request takes through the system
* [Form Field Types](form-field-types): Highlevel overview of field classes
@@ -32,8 +31,4 @@ Reference articles complement our auto-generated [API docs](http://api.silverstr
* [TableListField](tablelistfield): View and delete records in the CMS
* [Typography](typography): CSS file to enable WYSIWYG previews in the CMS
* [urlvariabletools](urlvariabletools): Debug and maintenance switches
-* [Versioned](versioned): Extension for SiteTree and other classes to store old versions and provide "staging"
-
-## Feedback
-
-If you have a topic you would like covered in these section please ask for it on our [Bug Tracker](http://open.silverstripe.org)
\ No newline at end of file
+* [Versioned](versioned): Extension for SiteTree and other classes to store old versions and provide "staging"
\ No newline at end of file
diff --git a/docs/en/reference/rssfeed.md b/docs/en/reference/rssfeed.md
index 097f06970..ceea4667e 100644
--- a/docs/en/reference/rssfeed.md
+++ b/docs/en/reference/rssfeed.md
@@ -57,7 +57,7 @@ something like this:
public function LatestUpdates() {
// 10 is the number of pages
- return DataObject::get("Page", "", "LastEdited DESC", "", 10);
+ return Page::get()->sort("LastEdited", "DESC")->limit(10);
}
}
diff --git a/docs/en/reference/site-reports.md b/docs/en/reference/site-reports.md
index 60ed878a5..dd59e1686 100644
--- a/docs/en/reference/site-reports.md
+++ b/docs/en/reference/site-reports.md
@@ -64,7 +64,7 @@ CustomSideReport.php
public function records() {
// the data the report returns all the dataobjects of type Page and sorted by title. See datamodel for more info
- return DataObject::get("Page", "", "Title");
+ return Page::get()->sort("Title");
}
public function fieldsToShow() {
diff --git a/docs/en/reference/sitetree.md b/docs/en/reference/sitetree.md
index 051276270..3c3294cc2 100644
--- a/docs/en/reference/sitetree.md
+++ b/docs/en/reference/sitetree.md
@@ -23,7 +23,7 @@ might consist of more than one *URLSegment*).
:::php
// wrong
- $mypage = DataObject::get_one('SiteTree', '"URLSegment" = \'\'');
+ $mypage = SiteTree::get()->filter("URLSegment", '')->First();
// right
$mypage = SiteTree::get_by_link('');
@@ -114,7 +114,8 @@ it is a good starting point, for choosing your customisation.
public function canCreate() {
//here is a trick to only allow one (e.g. holder) of a page
- return !DataObject::get_one($this->class);
+ $class = $this->class;
+ return !$class::get()->Count();
}
public function canDelete() {
diff --git a/docs/en/reference/staticpublisher.md b/docs/en/reference/staticpublisher.md
index 1ecf1190c..00701eeda 100644
--- a/docs/en/reference/staticpublisher.md
+++ b/docs/en/reference/staticpublisher.md
@@ -32,7 +32,7 @@ publisher to generate folders and HTML-files.
$urls = array();
// memory intensive depending on number of pages
- $pages = DataObject::get("SiteTree");
+ $pages = SiteTree::get();
foreach($pages as $page) {
$urls = array_merge($urls, (array)$page->subPagesToCache());
@@ -78,7 +78,7 @@ you can also add an exclusion
:::php
public function allPagesToCache() {
$urls = array();
- $pages = DataObject::get("SiteTree");
+ $pages = SiteTree::get();
// ignored page types
$ignored = array('UserDefinedForm');
@@ -93,12 +93,12 @@ you can also add an exclusion
return $urls;
}
-You can also pass the filtering to the original DataObject::get("SiteTree");
+You can also pass the filtering to the original `SiteTree::get()`;
:::php
public function allPagesToCache() {
$urls = array();
- $pages = DataObject::get("SiteTree", "ClassName != 'UserDefinedForm'");
+ $pages = SiteTree::get()->where("ClassName != 'UserDefinedForm'");
...
## Single server Caching
diff --git a/docs/en/reference/tablelistfield.md b/docs/en/reference/tablelistfield.md
index f3529e370..c756475e4 100644
--- a/docs/en/reference/tablelistfield.md
+++ b/docs/en/reference/tablelistfield.md
@@ -82,7 +82,7 @@ For more information on each of the features used in the example, you can read b
);
// custom DataObjectSet
- $myProducts = DataObject::get('Product','Code = "MyCode"');
+ $myProducts = Product::get()->filter('Code', "MyCode");
$myTableListField->setCustomSourceItems($myProducts);
// custom SQL
diff --git a/docs/en/topics/configuration.md b/docs/en/topics/configuration.md
index 085c86677..4625b8a24 100644
--- a/docs/en/topics/configuration.md
+++ b/docs/en/topics/configuration.md
@@ -54,7 +54,6 @@ incomplete - please add to it** *Try to keep it in alphabetical order too! :)*
| Security::encrypt_passwords($encrypt_passwords); | | Specify if you want store your passwords in clear text or encrypted (for more details see [security](/topics/security)) |
| Security::set_password_encryption_algorithm($algorithm, $use_salt);| | If you choose to encrypt your passwords, you can choose which algorithm is used to and if a salt should be used to increase the security level even more (for more details see [security](/topics/security)). |
| Security::setDefaultAdmin('admin','password'); | | Set default admin email and password, helpful for recovering your password |
- | SSAkismet::setAPIKey(string $key) | | Enables use of the Akismet spam filter. The key must be a valid WordPress API key. |
| SSViewer::set_theme(string $themename) | | Choose the default theme for your site |
## Constants
diff --git a/docs/en/topics/data-types.md b/docs/en/topics/data-types.md
index a999e2a88..ca3b1417c 100644
--- a/docs/en/topics/data-types.md
+++ b/docs/en/topics/data-types.md
@@ -1,26 +1,87 @@
-# Data Types
+# Data Types and Casting
-These are the data-types that you can use when defining your data objects. They are all subclasses of `[api:DBField]`
-for introducing their usage.
-
+Properties on any SilverStripe object can be type casted automatically,
+by transforming its scalar value into an instance of the `[api:DBField]` class,
+providing additional helpers. For example, a string can be cast as
+a `[api:Text]` type, which has a `FirstSentence()` method to retrieve the first
+sentence in a longer piece of text.
-## Types
+## Available Types
-* `[api:Varchar]`: A variable-length string of up to 255 characters, designed to store raw text
-* `[api:Text]`: A variable-length string of up to 2 megabytes, designed to store raw text
-* `[api:HTMLVarchar]`: A variable-length string of up to 255 characters, designed to store HTML
-* `[api:HTMLText]`: A variable-length string of up to 2 megabytes, designed to store HTML
-* `[api:Enum]`: An enumeration of a set of strings
* `[api:Boolean]`: A boolean field.
-* `[api:Int]`: An integer field.
-* `[api:Decimal]`: A decimal number.
* `[api:Currency]`: A number with 2 decimal points of precision, designed to store currency values.
-* `[api:Percentage]`: A decimal number between 0 and 1 that represents a percentage.
* `[api:Date]`: A date field
+* `[api:Decimal]`: A decimal number.
+* `[api:Enum]`: An enumeration of a set of strings
+* `[api:HTMLText]`: A variable-length string of up to 2 megabytes, designed to store HTML
+* `[api:HTMLVarchar]`: A variable-length string of up to 255 characters, designed to store HTML
+* `[api:Int]`: An integer field.
+* `[api:Percentage]`: A decimal number between 0 and 1 that represents a percentage.
* `[api:SS_Datetime]`: A date / time field
+* `[api:Text]`: A variable-length string of up to 2 megabytes, designed to store raw text
* `[api:Time]`: A time field
+* `[api:Varchar]`: A variable-length string of up to 255 characters, designed to store raw text
-## HTMLText vs. Text, and HTMLVarchar vs. Varchar
+## Casting arbitrary values
+
+On the most basic level, the class can be used as simple conversion class
+from one value to another, e.g. to round a number.
+
+ :::php
+ DBField::create_field('Double', 1.23456)->Round(2); // results in 1.23
+
+Of course that's much more verbose than the equivalent PHP call.
+The power of `[api:DBField]` comes with its more sophisticated helpers,
+like showing the time difference to the current date:
+
+ :::php
+ DBField::create_field('Date', '1982-01-01')->TimeDiff(); // shows "30 years ago"
+
+## Casting ViewableData
+
+Most objects in SilverStripe extend from `[api:ViewableData]`,
+which means they know how to present themselves in a view context.
+Through a `$casting` array, arbitrary properties and getters can be casted:
+
+ :::php
+ class MyObject extends ViewableData {
+ static $casting = array(
+ 'MyDate' => 'Date'
+ );
+ function getMyDate() {
+ return '1982-01-01';
+ }
+ }
+ $obj = new MyObject;
+ $obj->getMyDate(); // returns string
+ $obj->MyDate; // returns string
+ $obj->obj('MyDate'); // returns object
+ $obj->obj('MyDate')->InPast(); // returns boolean
+
+## Casting DataObject
+
+The `[api:DataObject]` class uses `DBField` to describe the types of its
+properties which are persisted in database columns, through the `[$db](api:DataObject::$db)` property.
+In addition to type information, the `DBField` class also knows how to
+define itself as a database column. See the ["datamodel" topic](/topics/datamodel#casting) for more details.
+
+
+Since we're dealing with a loosely typed language (PHP)
+as well as varying type support by the different database drivers,
+type conversions between the two systems are not guaranteed to be lossless.
+Please take particular care when casting booleans, null values, and on float precisions.
+
+
+## Casting in templates
+
+In templates, casting helpers are available without the need for an `obj()` call.
+
+Example: Flagging an object of type `MyObject` (see above) if it's date is in the past.
+
+ :::ss
+ <% if MyObjectInstance.MyDate.InPast %>Outdated!<% end_if %>
+
+## Casting HTML Text
The database field types `[api:HTMLVarchar]` and `[api:Varchar]` are exactly the same in the database. However, the
templating engine knows to escape the `[api:Varchar]` field and not the `[api:HTMLVarchar]` field. So, it's important you
@@ -29,6 +90,7 @@ use the right field if you don't want to be putting $FieldType.XML everywhere.
If you're going to put HTML content into the field, please use the field type with the HTML prefix. Otherwise, you're
going to risk double-escaping your data, forgetting to escape your data, and generally creating a confusing situation.
-## Usage
+## Related
-* See [datamodel](/topics/datamodel) for information about **database schemas** implementing these types
+ * ["datamodel" topic](/topics/datamodel)
+ * ["security" topic](/topics/security)
\ No newline at end of file
diff --git a/docs/en/topics/datamodel.md b/docs/en/topics/datamodel.md
index 61d9371d3..6f3292466 100755
--- a/docs/en/topics/datamodel.md
+++ b/docs/en/topics/datamodel.md
@@ -3,21 +3,20 @@
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
+* 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.
-The advanced object-relational layer in SilverStripe is one of the main reasons for requiring PHP5. Most of its
-customizations are possible through [PHP5 Object
+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.
See [database-structure](/reference/database-structure) for in-depth information on the database-schema.
-## Generating the database-schema
+## Generating the Database Schema
The SilverStripe database-schema is generated automatically by visiting the URL.
`http:///dev/build`
@@ -28,14 +27,13 @@ Note: You need to be logged in as an administrator to perform this command.
## Querying Data
-Every query to data starts with a `DataList::create($class)` or `$class::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():
+are `filter()` and `sort()`:
:::php
$members = Member::get()->filter(array('FirstName' => 'Sam'))->sort('Surname');
@@ -83,19 +81,18 @@ If you have constructed a query that you know should return a single record, you
Quiet 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).
+If would like to sort the list by `FirstName` in a ascending way (from A to Z).
:::php
- $member = Member::get()->sort('FirstName');
- // Or the more expressive way
$member = Member::get()->sort('FirstName', 'ASC');
+ $member = Member::get()->sort('FirstName'); // Ascending is implied
To reverse the sort
:::php
$member = 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
+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(
@@ -395,7 +392,7 @@ Note: Alternatively you can set defaults directly in the database-schema (rather
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,
+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
@@ -594,6 +591,9 @@ This functionality is provided by the `SS_Map` class, which can be used to build
$members = Member::get();
$map = new SS_Map($members, 'ID', 'FirstName');
+Note: You can also retrieve a single property from all contained records
+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.
diff --git a/docs/en/topics/grid-field.md b/docs/en/topics/grid-field.md
index b9def9bd9..e049ce961 100644
--- a/docs/en/topics/grid-field.md
+++ b/docs/en/topics/grid-field.md
@@ -166,7 +166,7 @@ Objects can be searched through an input field (partially matching one or more f
Selecting from the results will add the object to the relation.
:::php
- $group = DataObject::get_one('Group');
+ $group = Group::get()->First();
$config = GridFieldConfig::create()->addComponent(new GridFieldAddExistingAutocompleter(array('FirstName', 'Surname', 'Email'));
$gridField = new GridField('Members', 'Members', $group->Members(), $config);
diff --git a/docs/en/topics/javascript.md b/docs/en/topics/javascript.md
index 2429bc0ca..2c7d2e96a 100644
--- a/docs/en/topics/javascript.md
+++ b/docs/en/topics/javascript.md
@@ -386,8 +386,7 @@ PHP:
:::php
class MyController {
function autocomplete($request) {
- $SQL_title = Convert::raw2sql($request->getVar('title'));
- $results = DataObject::get("Page", "Title = '$SQL_title'");
+ $results = Page::get()->filter("Title", $request->getVar('title'));
if(!$results) return new HTTPResponse("Not found", 404);
// Use HTTPResponse to pass custom status messages
diff --git a/docs/en/topics/page-types.md b/docs/en/topics/page-types.md
index 58065a837..3c29cc065 100644
--- a/docs/en/topics/page-types.md
+++ b/docs/en/topics/page-types.md
@@ -150,7 +150,7 @@ and [tutorial:3-forms](/tutorials/3-forms).
## Updating a page:
:::php
- $page = DataObject::get_one("Page", "ParentID = 18");
+ $page = Page::get()->filter("ParentID", 18)->First();
$page->Title = "More Serious";
$page->writeToStage('Stage');
$page->Publish('Stage', 'Live');
diff --git a/docs/en/topics/security.md b/docs/en/topics/security.md
index b64c0e282..5d20481af 100644
--- a/docs/en/topics/security.md
+++ b/docs/en/topics/security.md
@@ -24,6 +24,7 @@ For `[api:MySQLDatabase]`, this will be `[mysql_real_escape_string()](http://de3
* DataObject::castedUpdate()
* DataObject->Property = 'val', DataObject->setField('Property','val')
* DataObject::write()
+* DataList->byID()
* Form->saveInto()
* FormField->saveInto()
* DBField->saveInto()
@@ -65,7 +66,7 @@ Example:
class MyForm extends Form {
public function save($RAW_data, $form) {
$SQL_data = Convert::raw2sql($RAW_data); // works recursively on an array
- $objs = DataObject::get('Player', "Name = '{$SQL_data[name]}'");
+ $objs = Player::get()->where("Name = '{$SQL_data[name]}'");
// ...
}
}
@@ -80,7 +81,7 @@ Example:
class MyController extends Controller {
public function myurlaction($RAW_urlParams) {
$SQL_urlParams = Convert::raw2sql($RAW_urlParams); // works recursively on an array
- $objs = DataObject::get('Player', "Name = '{$SQL_data[OtherID]}'");
+ $objs = Player::get()->where("Name = '{$SQL_data[OtherID]}'");
// ...
}
}
@@ -314,8 +315,8 @@ Below is an example with different ways you would use this casting technique:
// cast the 'category' GET variable as an integer
$categoryID = (int)$_GET['category'];
- // perform a get_by_id, ensure the ID is an integer before querying
- return DataObject::get_by_id('CaseStudy', $categoryID);
+ // perform a byID(), which ensures the ID is an integer before querying
+ return CaseStudy::get()->byID($categoryID);
}
diff --git a/docs/en/topics/testing/testing-guide-troubleshooting.md b/docs/en/topics/testing/testing-guide-troubleshooting.md
index 812aae5cf..4c8ce11f1 100644
--- a/docs/en/topics/testing/testing-guide-troubleshooting.md
+++ b/docs/en/topics/testing/testing-guide-troubleshooting.md
@@ -30,7 +30,7 @@ but rather just the returned collection,
:::php
$myPage = $this->objFromFixture('Page', 'mypage');
$myOtherPage = $this->objFromFixture('Page', 'myotherpage');
- $pages = DataObject::get('Page');
+ $pages = Page::get();
// Bad: Assumptions about IDs and their order
$this->assertEquals(array(1,2), $pages->column('ID'));
// Good: Uses actually created IDs, independent of their order
diff --git a/docs/en/tutorials/2-extending-a-basic-site.md b/docs/en/tutorials/2-extending-a-basic-site.md
index 918e18e82..78cff2ee1 100644
--- a/docs/en/tutorials/2-extending-a-basic-site.md
+++ b/docs/en/tutorials/2-extending-a-basic-site.md
@@ -406,8 +406,8 @@ control. We can get the data for the news articles by implementing our own funct
:::php
...
public function LatestNews($num=5) {
- $holder = DataObject::get_one("ArticleHolder");
- return ($holder) ? DataList::create('ArticlePage')->where('"ParentID" = '.$holder->ID)->sort('Date DESC')->limit($num) : false;
+ $holder = ArticleHolder::get()->First();
+ return ($holder) ? ArticlePage::get()->filter('ParentID', $holder->ID)->sort('Date DESC')->limit($num) : false;
}
...
diff --git a/docs/en/tutorials/3-forms.md b/docs/en/tutorials/3-forms.md
index 5aa8a3ab4..b269ace97 100644
--- a/docs/en/tutorials/3-forms.md
+++ b/docs/en/tutorials/3-forms.md
@@ -321,7 +321,7 @@ then create our graph using a page control in the template. Create the function
:::php
public function BrowserPollResults() {
- $submissions = DataObject::get('BrowserPollSubmission');
+ $submissions = BrowserPollSubmission::get();
$total = $submissions->Count();
$doSet = new DataObjectSet();
@@ -341,7 +341,7 @@ then create our graph using a page control in the template. Create the function
This introduces a few new concepts, so let's step through it.
:::php
- $submissions = DataObject::get('BrowserPollSubmission');
+ $submissions = BrowserPollSubmission::get();
First we get all of the *BrowserPollSubmission*s from the database. This returns the submissions as a
diff --git a/docs/en/tutorials/5-dataobject-relationship-management.md b/docs/en/tutorials/5-dataobject-relationship-management.md
index 0b7776e93..aaefe8d34 100644
--- a/docs/en/tutorials/5-dataobject-relationship-management.md
+++ b/docs/en/tutorials/5-dataobject-relationship-management.md
@@ -703,7 +703,7 @@ it *MyProject* for instance.
...
public function MyProject() {
- return DataObject::get( 'Project', "`MyStudentID` = '{$this->ID}'" );
+ return Project::get()->filter("MyStudentID", $this->ID);
}
}
diff --git a/model/GroupedList.php b/model/GroupedList.php
index 5686dd57b..84dc39b74 100644
--- a/model/GroupedList.php
+++ b/model/GroupedList.php
@@ -10,7 +10,7 @@ class GroupedList extends SS_ListDecorator {
/**
* @param string $index
- * @return ArrayList
+ * @return array
*/
public function groupBy($index) {
$result = array();
@@ -29,8 +29,11 @@ class GroupedList extends SS_ListDecorator {
}
/**
+ * Similar to {@link groupBy()}, but returns
+ * the data in a format which is suitable for usage in templates.
+ *
* @param string $index
- * @param string $children
+ * @param string $children Name of the control under which children can be iterated on
* @return ArrayList
*/
public function GroupedBy($index, $children = 'Children') {