ENHANCEMENT: Added documentation for the new ORM.

This commit is contained in:
Sam Minnee 2011-04-04 12:18:50 +12:00
parent 6915e58a39
commit f5d2e43636

View File

@ -28,40 +28,181 @@ Note: You need to be logged in as an administrator to perform this command.
## Querying Data ## Querying Data
There are static methods available for querying data. They automatically compile the necessary SQL to query the database Every query to data starts with a `DataList::create($class)` call. For example, this query would return all of the Member objects:
so they are very helpful. In case you need to fall back to plain-jane SQL, have a look at `[api:SQLQuery]`.
:::php :::php
$records = DataObject::get($obj, $filter, $sort, $join, $limit); $members = DataList::create('Member');
The ORM uses a "fluent" syntax, where you specify a query by chaining together different methods. Two common methods
are filter() and sort():
:::php :::php
$record = DataObject::get_one($obj, $filter); $members = DataList::create('Member')->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 `<% control %>`.
:::php :::php
$record = DataObject::get_by_id($obj, $id); // The SQL query isn't executed here...
$members = DataList::create('Member');
// ...or here
$members = $members->filter(array('FirstName' => 'Sam'));
// ...or even here
$members = $members->sort('Surname');
**CAUTION: Please make sure to properly escape your SQL-snippets (see [security](/topics/security).** // *This* is where the query is executed
foreach($members as $member) {
echo "<p>$member->FirstName $member->Surname</p>";
}
## Joining This also means that getting the count of a list of objects will be done with a single, efficient query.
:::php
$members = DataList::create('Member')->filter(array('FirstName' => 'Sam'))->sort('Surname');
// This will create an single SELECT COUNT query.
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)`:
:::php
$member = DataList::create('Member')->byID(5);
If you have constructed a query that you know should return a single record, you can call `First()`:
:::php
$member = DataList::create('Member')->filter(array('FirstName' => 'Sam', 'Surname' => 'Minnee'))->First();
### Filters
**FUN FACT:** This isn't implemented in the code yet, but will be shortly.
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 = DataList::create('Member')->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:
* 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.
So, this would return only those members called "Sam Minnée".
:::php
$members = DataList::create('Member')->filter(array(
'FirstName' => 'Sam',
'Surname' => 'Minnée',
));
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"`, `":Contains"`, `":GreaterThan"`, `":LessThan"`, `":Not"`,
and `":MatchCase"`. `":Not"` and `":MatchCase"` are special in that you can add it to any of the other filters.
This query will return everyone whose first name doesn't start with S, who have logged on since 1/1/2011.
:::php
$members = DataList::create('Member')->filter(array(
'FirstName:StartsWith:Not' => 'S'
'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'.
:::php
$members = DataList::create('Member')->filter(array(
'FirstName,Surname:Contains' => '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 = DataList::create('Member')->filter(array(
'FirstName' => array('sam', 'ingo'),
));
### Relation filters
So far we have only filtered a data list by fields on the object that you're requesting. For simple cases, this might
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, Sapphire 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.
:::php
$members = DataList::create('Member')->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.
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'.
:::php
$members = DataList::create('Member')->filter(array(
'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.
:::php
$members = DataList::create('Member')->filter(array(
'Groups.Members.FirstName' => 'Sam'
));
### Raw SQL options for advanced users
Occassionally, the system described above won't let you do exactly what you need to do. In these situtations, 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.
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:
:: php
$members = DataList::create('Member')->where("\"FirstName\" = 'Sam'")
#### Joining
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
* An optional alias
For example:
:: php
// Without an alias
$members = DataList::create('Member')->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
$members = DataList::create('Member')->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 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 table. **It will NOT return the additionally joined data.** The returned *$records* will always be a
`[api:DataObject]`. `[api:DataObject]`.
When using *$join* statements be sure the string is in the proper format for the respective database engine. In MySQL
the use of back-ticks may be necessary when referring Table Names and potentially Columns. (see [MySQL
Identifiers](http://dev.mysql.com/doc/refman/5.0/en/identifiers.html)):
:::php
// Example from the forums: http://www.silverstripe.org/archive/show/79865#post79865
// Note the use of backticks on table names
$links = DataObject::get("SiteTree",
"ShowInMenus = 1 AND ParentID = 23",
"",
"LEFT JOIN `ConsultationPaperHolder` ON `ConsultationPaperHolder`.ID = `SiteTree`.ID",
"0, 10");
## Properties ## Properties
@ -312,7 +453,7 @@ Inside sapphire it doesn't matter if you're editing a *has_many*- or a *many_man
} }
### Custom Relation Getters ### Custom Relations
You can use the flexible datamodel to get a filtered result-list without writing any SQL. For example, this snippet gets 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 you the "Players"-relation on a team, but only containing active players. (See `[api:DataObject::$has_many]` for more info on
@ -324,8 +465,8 @@ the described relations).
"Players" => "Player" "Players" => "Player"
); );
// can be accessed by $myTeam->ActivePlayers // can be accessed by $myTeam->ActivePlayers()
function getActivePlayers() { function ActivePlayers() {
return $this->Players("Status='Active'"); return $this->Players("Status='Active'");
} }
} }