mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Update documentation static declarations to private
Also spelling, grammar and line length clean up.
This commit is contained in:
parent
31cfcdb08e
commit
6d792adab2
@ -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[<extradata-field-name>]`, so for example
|
||||
Example:
|
||||
|
||||
:::php
|
||||
|
||||
class Player extends DataObject {
|
||||
private static $db = array('Name' => 'Text');
|
||||
public static $many_many = array('Teams' => 'Team');
|
||||
|
@ -208,7 +208,7 @@ like this:
|
||||
'Description' => 'Text'
|
||||
);
|
||||
|
||||
public static $belongs_many_many = array(
|
||||
private static $belongs_many_many = array(
|
||||
'GalleryPage' => 'GalleryPage'
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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'
|
||||
);
|
||||
|
||||
|
@ -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.
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
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.
|
||||
</div>
|
||||
|
||||
## 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(<class>)` or `<class>::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 "<p>$member->FirstName $member->Surname</p>";
|
||||
}
|
||||
|
||||
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`<fieldname>`" or "set`<fieldname>`".
|
||||
"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`<fieldname>`" or
|
||||
"set`<fieldname>`".
|
||||
|
||||
:::php
|
||||
class Player extends DataObject {
|
||||
@ -359,17 +405,22 @@ default behaviour by making a function called "get`<fieldname>`" or "set`<fieldn
|
||||
|
||||
### Customizing
|
||||
|
||||
We can create new "virtual properties" which are not actually listed in *static $db* or stored in the database-row.
|
||||
Here we combined a Player's first name and surname, accessible through $myPlayer->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
|
||||
}
|
||||
|
||||
<div class="warning" markdown='1'>
|
||||
**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.
|
||||
</div>
|
||||
|
||||
<div class="warning" markdown='1'>
|
||||
**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.
|
||||
</div>
|
||||
|
||||
### 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',
|
||||
);
|
||||
}
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
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.
|
||||
</div>
|
||||
|
||||
### 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 `<relationship-name> => <classname>`
|
||||
Relations are built through static array definitions on a class, in the format
|
||||
`<relationship-name> => <classname>`.
|
||||
|
||||
### has_one
|
||||
|
||||
A 1-to-1 relation creates a database-column called "`<relationship-name>`ID", in the example below this would be "TeamID"
|
||||
on the "Player"-table.
|
||||
A 1-to-1 relation creates a database-column called "`<relationship-name>`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 ""`<relationship-name>`ID"" will to be created in the child-class.
|
||||
Defines 1-to-many joins. A database-column named ""`<relationship-name>`ID""
|
||||
will to be created in the child-class.
|
||||
|
||||
<div class="warning" markdown='1'>
|
||||
**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.
|
||||
</div>
|
||||
|
||||
:::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.
|
||||
|
||||
<div class="warning" markdown='1'>
|
||||
**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.
|
||||
</div>
|
||||
|
||||
:::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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
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.
|
||||
</div>
|
||||
|
||||
### 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
|
||||
|
@ -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
|
||||
<div>$HelloForm</div>
|
||||
|
||||
<div class="warning" markdown='1'>
|
||||
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.
|
||||
</div>
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
@ -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
|
||||
<form $FormAttributes>
|
||||
@ -314,30 +328,34 @@ Will be rendered as:
|
||||
:::html
|
||||
<input type="text" name="MyText" class="text largeText" id="MyForm_MyCustomForm_MyText" data-validation-regex="[\d]*">
|
||||
|
||||
Each form field is rendered into a form via the `[FormField->FieldHolder()](api:FormField)` method, which includes
|
||||
a container `<div>` as well as a `<label>` element (if applicable).
|
||||
Each form field is rendered into a form via the
|
||||
`[FormField->FieldHolder()](api:FormField)` method, which includes a container
|
||||
`<div>` as well as a `<label>` element (if applicable).
|
||||
|
||||
You can also render each field without these structural elements through the `[FormField->Field()](api:FormField)`
|
||||
method. In order to influence the form rendering, overloading these two methods is a good start.
|
||||
You can also render each field without these structural elements through the
|
||||
`[FormField->Field()](api:FormField)` method. In order to influence the form
|
||||
rendering, overloading these two methods is a good start.
|
||||
|
||||
In addition, most form fields are rendered through SilverStripe templates, e.g. `TextareaField` is rendered via
|
||||
`framework/templates/forms/TextareaField.ss`.
|
||||
In addition, most form fields are rendered through SilverStripe templates, e.g.
|
||||
`TextareaField` is rendered via `framework/templates/forms/TextareaField.ss`.
|
||||
|
||||
These templates can be overwritten globally by placing a template with the same name in your `mysite` directory,
|
||||
or set on a form field instance via anyone of these methods:
|
||||
These templates can be overwritten globally by placing a template with the same
|
||||
name in your `mysite` directory, or set on a form field instance via anyone of
|
||||
these methods:
|
||||
|
||||
- FormField->setTemplate()
|
||||
- FormField->setFieldHolderTemplate()
|
||||
- FormField->getSmallFieldHolderTemplate()
|
||||
|
||||
<div class="hint" markdown='1'>
|
||||
Caution: Not all FormFields consistently uses templates set by the above methods.
|
||||
Caution: Not all FormFields consistently uses templates set by the above methods.
|
||||
</div>
|
||||
|
||||
### Securing forms against Cross-Site Request Forgery (CSRF)
|
||||
|
||||
SilverStripe tries to protect users against *Cross-Site Request Forgery (CSRF)* by adding a hidden *SecurityID*
|
||||
parameter to each form. See [secure-development](/topics/security) for details.
|
||||
SilverStripe tries to protect users against *Cross-Site Request Forgery (CSRF)*
|
||||
by adding a hidden *SecurityID* parameter to each form. See
|
||||
[secure-development](/topics/security) for details.
|
||||
|
||||
In addition, you should limit forms to the intended HTTP verb (mostly `GET` or `POST`)
|
||||
to further reduce attack surface, by using `[api:Form->setStrictFormMethodCheck()]`.
|
||||
@ -353,6 +371,7 @@ If you want to remove certain fields from your subclass:
|
||||
|
||||
:::php
|
||||
class MyCustomForm extends MyForm {
|
||||
|
||||
public function __construct($controller, $name) {
|
||||
parent::__construct($controller, $name);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user