Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Datamodel documentation fixes

Fixes reported issues and other related typos, inconsistent language from the datamodel documentation. Thanks to Nimo.
  • Loading branch information...
commit eb82094c4505e457cdec7e874032633e5b40cd32 1 parent d774cb5
@wilr wilr authored
Showing with 92 additions and 73 deletions.
  1. +92 −73 docs/en/topics/datamodel.md
View
165 docs/en/topics/datamodel.md
@@ -40,8 +40,15 @@ are `filter()` and `sort()`:
$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
-query until you iterate on the result with a `foreach()` or `<% loop %>`.
+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
+
+An example of the query process in action:
:::php
// The SQL query isn't executed here...
@@ -60,14 +67,16 @@ This also means that getting the count of a list of objects will be done with a
:::php
$members = Member::get()->filter(array('FirstName' => 'Sam'))->sort('Surname');
- // This will create an single SELECT COUNT query.
+
+ // This will create an single SELECT COUNT query similar to -
+ // SELECT COUNT(*) FROM Members WHERE FirstName = 'Sam'
echo $members->Count();
-All of this lets you focus on writing your application, and not worrying too much about whether or not your queries are efficient.
### 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);
@@ -80,12 +89,12 @@ If you have constructed a query that you know should return a single record, you
### Sort
-Quiet 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');
+ $member = Member::get()->sort('FirstName', 'ASC'); // ASC or DESC
$member = Member::get()->sort('FirstName'); // Ascending is implied
To reverse the sort
@@ -93,7 +102,8 @@ 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(
@@ -101,9 +111,14 @@ However you might have several entries with the same `FirstName` and would like
'LastName'=>'ASC'
));
+You can also sort randomly
+
+ :::php
+ $member = Member::get()->sort('RAND()')
+
### Filter
-As you might expect, the `filter()` method filters the list of objects that gets returned. The previous example
+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
@@ -113,8 +128,8 @@ In SilverStripe 2, we would have passed `"\"FirstName\" = 'Sam'` to make this qu
`array('FirstName' => 'Sam')`, to minimise 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.
@@ -126,7 +141,7 @@ So, this would return only those members called "Sam Minnée".
'Surname' => 'Minnée',
));
-There are also a short hand way of getting Members with the FirstName of Sam.
+There is also a short hand way of getting Members with the FirstName of Sam.
:::php
$members = Member::get()->filter('FirstName', 'Sam');
@@ -188,10 +203,17 @@ This would be equivalent to a SQL query of
:::
... WHERE ("FirstName" NOT IN ('Sam','Sig) OR "Age" NOT IN ('17', '74));
-By default, these filters specify case-insensitive exact matches. There are a number of suffixes that you can put on
-field names to change this: `":StartsWith"`, `":EndsWith"`, `":PartialMatch"`, `":GreaterThan"`, `":LessThan"`, `":Negation"`.
+### Search Filter Modifiers
+
+The where clauses showcased in the previous two sections (filter and exclude) specify case-insensitive 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"`.
+
+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.
-This query will return everyone whose first name doesn't start with S, who have logged on 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(
@@ -199,21 +221,14 @@ This query will return everyone whose first name doesn't start with S, who have
'LastVisited:GreaterThan' => '2011-01-01'
));
-If you wish to match against any of a number of columns, you can list several field names, separated by commas. This
-will return all members whose first name or surname contain the string 'sam'.
+If you wish to match against any of a number of columns, you can list several field names, separated by commas.
+This will return all members whose first name or surname contain the string 'sam'.
:::php
$members = Member::get()->filter(array(
'FirstName,Surname:PartialMatch' => 'sam'
));
-If you wish to match against any of a number of values, you can pass an array as the value. This will return all
-members whose first name is either Sam or Ingo.
-
- :::php
- $members = Member::get()->filter(array(
- 'FirstName' => array('sam', 'ingo'),
- ));
### Subtract
@@ -238,18 +253,20 @@ So far we have only filtered a data list by fields on the object that you're req
be okay, but often, a data model is made up of a number of related objects. For example, in SilverStripe each member
can be placed in a number of groups, and each group has a number of permissions.
-For this, the SilverStripe ORM supports **Relation Filters**. Any ORM request can be filtered by fields on a related object by
-specifying the filter key as `<relation-name>.<field-in-related-object>`. You can chain relations together as many
-times as is necessary.
+For this, the SilverStripe ORM supports **Relation Filters**. Any ORM request can be filtered by fields on a related
+object by specifying the filter key as `<relation-name>.<field-in-related-object>`. You can chain relations together
+as many times as is necessary.
-For example, this will return all members assigned ot a group that has a permission record with the code "ADMIN". In other words, it will return all administrators.
+For example, this will return all members assigned to a group that has a permission record with the code "ADMIN". In
+other words, it will return all administrators.
:::php
$members = Member::get()->filter(array(
'Groups.Permissions.Code' => 'ADMIN',
));
-Note that we are just joining to these tables to filter the records. Even if a member is in more than 1 administrator group, unique members will still be returned by this query.
+Note that we are just joining these tables to filter the records. Even if a member is in more than 1 administrator
+group, unique members will still be returned by this query.
The other features of filters can be applied to relation filters as well. This will return all members in groups whose
names start with 'A' or 'B'.
@@ -259,7 +276,8 @@ names start with 'A' or 'B'.
'Groups.Title:StartsWith' => array('A', 'B'),
));
-You can even follow a relation back to the original model class! This will return all members are in at least 1 group that also has a member called Sam.
+You can even follow a relation back to the original model class! This will return all members are in at least 1 group
+that also has a member called Sam.
:::php
$members = Member::get()->filter(array(
@@ -273,13 +291,14 @@ methods that manipulate the SQL query at a lower level. When using these, pleas
are escaped with double quotes, otherwise some DB back-ends (e.g. PostgreSQL) won't work.
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)
+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:
- :: php
+ :::php
$members = Member::get()->where("\"FirstName\" = 'Sam'")
#### Joining
@@ -292,7 +311,7 @@ You can specify a join with the innerJoin and leftJoin methods. Both of these m
For example:
- :: php
+ :::php
// Without an alias
$members = Member::get()->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
@@ -312,7 +331,7 @@ Data is defined in the static variable $db on each class, in the format:
:::php
class Player extends DataObject {
- static $db = array(
+ public static $db = array(
"FirstName" => "Varchar",
"Surname" => "Varchar",
"Description" => "Text",
@@ -331,7 +350,7 @@ default behaviour by making a function called "get`<fieldname>`" or "set`<fieldn
:::php
class Player extends DataObject {
- static $db = array(
+ public static $db = array(
"Status" => "Enum('Active, Injured, Retired')"
);
@@ -379,7 +398,7 @@ new object is created.
:::php
class Player extends DataObject {
- static $defaults = array(
+ public static $defaults = array(
"Status" => 'Active',
);
}
@@ -398,7 +417,7 @@ but using the *obj()*-method or accessing through a template will cast the value
:::php
class Player extends DataObject {
- static $casting = array(
+ public static $casting = array(
"MembershipFee" => 'Currency',
);
@@ -423,7 +442,7 @@ on the "Player"-table.
:::php
// access with $myPlayer->Team()
class Player extends DataObject {
- static $has_one = array(
+ public static $has_one = array(
"Team" => "Team",
);
}
@@ -434,7 +453,7 @@ parent element in the tree:
:::php
// access with $mySiteTree->Parent()
class SiteTree extends DataObject {
- static $has_one = array(
+ public static $has_one = array(
"Parent" => "SiteTree",
);
}
@@ -451,12 +470,12 @@ accessors available on both ends.
:::php
// access with $myTeam->Players() or $player->Team()
class Team extends DataObject {
- static $has_many = array(
+ public static $has_many = array(
"Players" => "Player",
);
}
class Player extends DataObject {
- static $has_one = array(
+ public static $has_one = array(
"Team" => "Team",
);
}
@@ -465,15 +484,15 @@ accessors available on both ends.
To specify multiple $has_manys to the same object you can use dot notation to distinguish them like below
:::php
- class Person {
- static $has_many = array(
+ class Person extends DataObject {
+ public static $has_many = array(
"Managing" => "Company.Manager",
"Cleaning" => "Company.Cleaner",
);
}
- class Company {
- static $has_one = array(
+ class Company extends DataObject {
+ public static $has_one = array(
"Manager" => "Person",
"Cleaner" => "Person"
);
@@ -487,12 +506,12 @@ Multiple $has_one relationships are okay if they aren't linking to the same obje
* THIS IS BAD
*/
class Team extends DataObject {
- static $has_many = array(
+ public static $has_many = array(
"Players" => "Player",
);
}
class Player extends DataObject {
- static $has_one = array(
+ public static $has_one = array(
"Team" => "Team",
"AnotherTeam" => "Team",
);
@@ -511,12 +530,12 @@ accessors available on both ends.
:::php
// access with $myTeam->Categories() or $myCategory->Teams()
class Team extends DataObject {
- static $many_many = array(
+ public static $many_many = array(
"Categories" => "Category",
);
}
class Category extends DataObject {
- static $belongs_many_many = array(
+ public static $belongs_many_many = array(
"Teams" => "Team",
);
}
@@ -524,15 +543,14 @@ 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"
- static $many_many = array(
+ public static $many_many = array(
"Categories" => "Category",
);
@@ -544,13 +562,14 @@ both of which provide very similar APIs, 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 {
- static $has_many = array(
+ public static $has_many = array(
"Players" => "Player"
);
@@ -560,8 +579,8 @@ 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.
## Validation and Constraints
@@ -589,10 +608,11 @@ Example: Validate postcodes based on the selected country
:::php
class MyObject extends DataObject {
- static $db = array(
+ public static $db = array(
'Country' => 'Varchar',
'Postcode' => 'Varchar'
);
+
public function validate() {
$result = parent::validate();
if($this->Country == 'DE' && $this->Postcode && strlen($this->Postcode) != 5) {
@@ -643,7 +663,7 @@ You have to make sure though that certain properties are not overwritten, e.g. *
### Update
:::php
- $myPlayer = DataObject::get_by_id('Player',99);
+ $myPlayer = Player::get()->byID(99);
if($myPlayer) {
$myPlayer->Firstname = "John"; // sets property on object
$myPlayer->write(); // writes row to database
@@ -675,14 +695,15 @@ casting data before saving.
### onBeforeWrite
-You can customize saving-behaviour for each DataObject, e.g. for adding security. These functions are private, obviously
-it wouldn't make sense to call them externally on the object. They are triggered when calling *write()*.
+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.
:::php
class Player extends DataObject {
- static $has_many = array(
+ public static $has_many = array(
"Teams"=>"Team"
);
@@ -722,7 +743,7 @@ It checks if a member is logged in who belongs to a group containing the permiss
:::php
class Player extends DataObject {
- static $has_many = array(
+ public static $has_many = array(
"Teams"=>"Team"
);
@@ -746,19 +767,17 @@ See the ["sql queries" topic](/reference/sqlquery) for custom *INSERT*, *UPDATE*
## 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.
-Please see `[api:DataExtension]` for a general description, and `[api:Hierarchy]` for our 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
-### Whats the difference between DataObject::get() and a relation-getter?
+### 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:
+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
- $myTeam->add($myOtherPlayer);
Please sign in to comment.
Something went wrong with that request. Please try again.