mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #6446 from robbieaverill/feature/controllers-without-underscores
API Allow controller discovery without underscores (PSR-2 compliance)
This commit is contained in:
commit
747c0770e7
@ -1087,6 +1087,7 @@ mappings:
|
|||||||
ConfigTest_TestNest: SilverStripe\Core\Tests\Config\ConfigTest\TestNest
|
ConfigTest_TestNest: SilverStripe\Core\Tests\Config\ConfigTest\TestNest
|
||||||
ConfigTest: SilverStripe\Core\Tests\Config\ConfigTest
|
ConfigTest: SilverStripe\Core\Tests\Config\ConfigTest
|
||||||
ConfigTest_Config_MemCache: SilverStripe\Core\Tests\Config\ConfigTest\ConfigTestMemCache
|
ConfigTest_Config_MemCache: SilverStripe\Core\Tests\Config\ConfigTest\ConfigTestMemCache
|
||||||
|
Page_Controller: PageController
|
||||||
skipConfigs:
|
skipConfigs:
|
||||||
- db
|
- db
|
||||||
- casting
|
- casting
|
||||||
|
@ -335,13 +335,22 @@ types right now, we will go into much more detail in the [next tutorial](/tutori
|
|||||||
|
|
||||||
Create a new file *HomePage.php* in *mysite/code*. Copy the following code into it:
|
Create a new file *HomePage.php* in *mysite/code*. Copy the following code into it:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
class HomePage extends Page {
|
|
||||||
}
|
|
||||||
class HomePage_Controller extends Page_Controller {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
use Page;
|
||||||
|
use PageController;
|
||||||
|
|
||||||
|
class HomePage extends Page
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class HomePageController extends PageController
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Every page type also has a database table corresponding to it. Every time we modify the database, we need to rebuild it.
|
Every page type also has a database table corresponding to it. Every time we modify the database, we need to rebuild it.
|
||||||
We can do this by going to `http://localhost/your_site_name/dev/build`.
|
We can do this by going to `http://localhost/your_site_name/dev/build`.
|
||||||
|
@ -18,59 +18,67 @@ We will create a poll on the home page that asks the user their favourite web br
|
|||||||
|
|
||||||
## Creating the form
|
## Creating the form
|
||||||
|
|
||||||
The poll we will be creating on our homepage will ask the user for their name and favourite web browser. It will then collate the results into a bar graph. We create the form in a method on *HomePage_Controller*.
|
The poll we will be creating on our homepage will ask the user for their name and favourite web browser. It will then collate the results into a bar graph. We create the form in a method on *HomePageController*.
|
||||||
|
|
||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePageController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class HomePage_Controller extends Page_Controller {
|
use PageController;
|
||||||
private static $allowed_actions = array('BrowserPollForm');
|
use SilverStripe\Forms\FieldList;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\FormAction;
|
||||||
|
use SilverStripe\Forms\OptionSetField;
|
||||||
|
use SilverStripe\Forms\TextField;
|
||||||
|
|
||||||
// ...
|
class HomePageController extends PageController
|
||||||
|
{
|
||||||
|
private static $allowed_actions = array('BrowserPollForm');
|
||||||
|
|
||||||
public function BrowserPollForm() {
|
// ...
|
||||||
// Create fields
|
|
||||||
$fields = new FieldList(
|
|
||||||
new TextField('Name'),
|
|
||||||
new OptionsetField('Browser', 'Your Favourite Browser', array(
|
|
||||||
'Firefox' => 'Firefox',
|
|
||||||
'Chrome' => 'Chrome',
|
|
||||||
'Internet Explorer' => 'Internet Explorer',
|
|
||||||
'Safari' => 'Safari',
|
|
||||||
'Opera' => 'Opera',
|
|
||||||
'Lynx' => 'Lynx'
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create actions
|
public function BrowserPollForm()
|
||||||
$actions = new FieldList(
|
{
|
||||||
new FormAction('doBrowserPoll', 'Submit')
|
// Create fields
|
||||||
);
|
$fields = new FieldList(
|
||||||
|
new TextField('Name'),
|
||||||
|
new OptionsetField('Browser', 'Your Favourite Browser', array(
|
||||||
|
'Firefox' => 'Firefox',
|
||||||
|
'Chrome' => 'Chrome',
|
||||||
|
'Internet Explorer' => 'Internet Explorer',
|
||||||
|
'Safari' => 'Safari',
|
||||||
|
'Opera' => 'Opera',
|
||||||
|
'Lynx' => 'Lynx'
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
return new Form($this, 'BrowserPollForm', $fields, $actions);
|
// Create actions
|
||||||
}
|
$actions = new FieldList(
|
||||||
|
new FormAction('doBrowserPoll', 'Submit')
|
||||||
|
);
|
||||||
|
|
||||||
...
|
return new Form($this, 'BrowserPollForm', $fields, $actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
// ...
|
||||||
|
}
|
||||||
|
// ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's step through this code.
|
Let's step through this code.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Create fields
|
// Create fields
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
new TextField('Name'),
|
new TextField('Name'),
|
||||||
new OptionsetField('Browser', 'Your Favourite Browser', array(
|
new OptionsetField('Browser', 'Your Favourite Browser', array(
|
||||||
'Firefox' => 'Firefox',
|
'Firefox' => 'Firefox',
|
||||||
'Chrome' => 'Chrome',
|
'Chrome' => 'Chrome',
|
||||||
'Internet Explorer' => 'Internet Explorer',
|
'Internet Explorer' => 'Internet Explorer',
|
||||||
'Safari' => 'Safari',
|
'Safari' => 'Safari',
|
||||||
'Opera' => 'Opera',
|
'Opera' => 'Opera',
|
||||||
'Lynx' => 'Lynx'
|
'Lynx' => 'Lynx'
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
First we create our form fields.
|
First we create our form fields.
|
||||||
@ -81,10 +89,10 @@ argument is passed, as in this case, it is assumed the label is the same as the
|
|||||||
The second field we create is an [api:OptionsetField]. This is a dropdown, and takes a third argument - an
|
The second field we create is an [api:OptionsetField]. This is a dropdown, and takes a third argument - an
|
||||||
array mapping the values to the options listed in the dropdown.
|
array mapping the values to the options listed in the dropdown.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
new FormAction('doBrowserPoll', 'Submit');
|
new FormAction('doBrowserPoll', 'Submit');
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
After creating the fields, we create the form actions. Form actions appear as buttons at the bottom of the form.
|
After creating the fields, we create the form actions. Form actions appear as buttons at the bottom of the form.
|
||||||
@ -94,7 +102,7 @@ All the form actions (in this case only one) are collected into a [api:FieldList
|
|||||||
the fields.
|
the fields.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
return new Form($this, 'BrowserPollForm', $fields, $actions);
|
return new Form($this, 'BrowserPollForm', $fields, $actions);
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally we create the [api:Form] object and return it.
|
Finally we create the [api:Form] object and return it.
|
||||||
@ -107,14 +115,14 @@ Add the following code to the top of your home page template, just before `<div
|
|||||||
|
|
||||||
**themes/simple/templates/Layout/HomePage.ss**
|
**themes/simple/templates/Layout/HomePage.ss**
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
...
|
...
|
||||||
<div id="BrowserPoll">
|
<div id="BrowserPoll">
|
||||||
<h2>Browser Poll</h2>
|
<h2>Browser Poll</h2>
|
||||||
$BrowserPollForm
|
$BrowserPollForm
|
||||||
</div>
|
</div>
|
||||||
<div class="Content">
|
<div class="Content">
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to make the graphs render correctly,
|
In order to make the graphs render correctly,
|
||||||
@ -123,49 +131,49 @@ Add the following code to the existing `form.css` file:
|
|||||||
|
|
||||||
**themes/simple/css/form.css**
|
**themes/simple/css/form.css**
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* BROWSER POLL */
|
/* BROWSER POLL */
|
||||||
#BrowserPoll {
|
#BrowserPoll {
|
||||||
float: right;
|
float: right;
|
||||||
margin: 20px 10px 0 0;
|
margin: 20px 10px 0 0;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
form FieldList {
|
form FieldList {
|
||||||
border:0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#BrowserPoll .message {
|
#BrowserPoll .message {
|
||||||
float:left;
|
float: left;
|
||||||
display: block;
|
display: block;
|
||||||
color:red;
|
color: red;
|
||||||
background:#efefef;
|
background: #efefef;
|
||||||
border:1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding:5px;
|
padding: 5px;
|
||||||
margin:5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#BrowserPoll h2 {
|
#BrowserPoll h2 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
line-height:2em;
|
line-height:2em;
|
||||||
color: #0083C8;
|
color: #0083C8;
|
||||||
}
|
}
|
||||||
|
|
||||||
#BrowserPoll .field {
|
#BrowserPoll .field {
|
||||||
padding:3px 0;
|
padding:3px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#BrowserPoll input.text {
|
#BrowserPoll input.text {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size:1em;
|
font-size:1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#BrowserPoll .btn-toolbar {
|
#BrowserPoll .btn-toolbar {
|
||||||
padding:5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#BrowserPoll .bar {
|
#BrowserPoll .bar {
|
||||||
background-color: #015581;
|
background-color: #015581;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -183,29 +191,37 @@ If you recall, in the [second tutorial](/tutorials/extending_a_basic_site) we sa
|
|||||||
|
|
||||||
**mysite/code/BrowserPollSubmission.php**
|
**mysite/code/BrowserPollSubmission.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
class BrowserPollSubmission extends DataObject {
|
|
||||||
private static $db = array(
|
use SilverStripe\ORM\DataObject;
|
||||||
'Name' => 'Text',
|
|
||||||
'Browser' => 'Text'
|
class BrowserPollSubmission extends DataObject
|
||||||
);
|
{
|
||||||
}
|
private static $db = array(
|
||||||
|
'Name' => 'Text',
|
||||||
|
'Browser' => 'Text'
|
||||||
|
);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
If we then rebuild the database ([http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build)), we will see that the *BrowserPollSubmission* table is created. Now we just need to define 'doBrowserPoll' on *HomePage_Controller*:
|
If we then rebuild the database ([http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build)), we will see that the *BrowserPollSubmission* table is created. Now we just need to define 'doBrowserPoll' on *HomePageController*:
|
||||||
|
|
||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePageController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class HomePage_Controller extends Page_Controller {
|
use BrowserPollSubmission;
|
||||||
// ...
|
use PageController;
|
||||||
public function doBrowserPoll($data, $form) {
|
|
||||||
$submission = new BrowserPollSubmission();
|
class HomePageController extends PageController
|
||||||
$form->saveInto($submission);
|
{
|
||||||
$submission->write();
|
// ...
|
||||||
return $this->redirectBack();
|
public function doBrowserPoll($data, $form) {
|
||||||
}
|
$submission = new BrowserPollSubmission();
|
||||||
}
|
$form->saveInto($submission);
|
||||||
|
$submission->write();
|
||||||
|
return $this->redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A function that processes a form submission takes two arguments - the first is the data in the form, the second is the [api:Form] object.
|
A function that processes a form submission takes two arguments - the first is the data in the form, the second is the [api:Form] object.
|
||||||
@ -218,16 +234,17 @@ SilverStripe forms all have automatic validation on fields where it is logical.
|
|||||||
|
|
||||||
SilverStripe provides the *RequiredFields* validator, which ensures that the fields specified are filled in before the form is submitted. To use it we create a new *RequiredFields* object with the name of the fields we wish to be required as the arguments, then pass this as a fifth argument to the Form constructor.
|
SilverStripe provides the *RequiredFields* validator, which ensures that the fields specified are filled in before the form is submitted. To use it we create a new *RequiredFields* object with the name of the fields we wish to be required as the arguments, then pass this as a fifth argument to the Form constructor.
|
||||||
|
|
||||||
Change the end of the 'BrowserPollForm' function so it looks like this:
|
Add a namespace import for `SilverStripe\Forms\RequiredFields`, then change the end of the 'BrowserPollForm' function so it looks like this:
|
||||||
|
|
||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePage.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function BrowserPollForm() {
|
public function BrowserPollForm()
|
||||||
// ...
|
{
|
||||||
$validator = new RequiredFields('Name', 'Browser');
|
// ...
|
||||||
return new Form($this, 'BrowserPollForm', $fields, $actions, $validator);
|
$validator = new RequiredFields('Name', 'Browser');
|
||||||
}
|
return new Form($this, 'BrowserPollForm', $fields, $actions, $validator);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If we then open the homepage and attempt to submit the form without filling in the required fields errors should appear.
|
If we then open the homepage and attempt to submit the form without filling in the required fields errors should appear.
|
||||||
@ -242,34 +259,40 @@ The first thing to do is make it so a user can only vote once per session. If th
|
|||||||
|
|
||||||
We can do this using a session variable. The [api:Session] class handles all session variables in SilverStripe. First modify the 'doBrowserPoll' to set the session variable 'BrowserPollVoted' when a user votes.
|
We can do this using a session variable. The [api:Session] class handles all session variables in SilverStripe. First modify the 'doBrowserPoll' to set the session variable 'BrowserPollVoted' when a user votes.
|
||||||
|
|
||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePageController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// ...
|
// ...
|
||||||
class HomePage_Controller extends Page_Controller {
|
class HomePageController extends PageController
|
||||||
// ...
|
{
|
||||||
public function doBrowserPoll($data, $form) {
|
// ...
|
||||||
$submission = new BrowserPollSubmission();
|
public function doBrowserPoll($data, $form)
|
||||||
$form->saveInto($submission);
|
{
|
||||||
$submission->write();
|
$submission = new BrowserPollSubmission();
|
||||||
Session::set('BrowserPollVoted', true);
|
$form->saveInto($submission);
|
||||||
return $this->redirectBack();
|
$submission->write();
|
||||||
}
|
Session::set('BrowserPollVoted', true);
|
||||||
}
|
return $this->redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we simply need to check if the session variable has been set in 'BrowserPollForm()', and to not return the form if
|
Then we simply need to check if the session variable has been set in 'BrowserPollForm()', and to not return the form if
|
||||||
it is.
|
it is.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// ...
|
// ...
|
||||||
class HomePage_Controller extends Page_Controller {
|
class HomePageController extends PageController
|
||||||
// ...
|
{
|
||||||
public function BrowserPollForm() {
|
// ...
|
||||||
if(Session::get('BrowserPollVoted')) return false;
|
public function BrowserPollForm()
|
||||||
// ...
|
{
|
||||||
}
|
if (Session::get('BrowserPollVoted')) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you visit the home page now you will see you can only vote once per session; after that the form won't be shown. You can start a new session by closing and reopening your browser,
|
If you visit the home page now you will see you can only vote once per session; after that the form won't be shown. You can start a new session by closing and reopening your browser,
|
||||||
@ -281,47 +304,49 @@ Now that we're collecting data, it would be nice to show the results on the webs
|
|||||||
|
|
||||||
In the [second tutorial](/tutorials/extending_a_basic_site), we got a collection of news articles for the home page by using the 'ArticleHolder::get()' function, which returns a [api:DataList]. We can get all submissions in the same fashion, through `BrowserPollSubmission::get()`. This list will be the starting point for our result aggregation.
|
In the [second tutorial](/tutorials/extending_a_basic_site), we got a collection of news articles for the home page by using the 'ArticleHolder::get()' function, which returns a [api:DataList]. We can get all submissions in the same fashion, through `BrowserPollSubmission::get()`. This list will be the starting point for our result aggregation.
|
||||||
|
|
||||||
Create the function 'BrowserPollResults' on the *HomePage_Controller* class.
|
Add the appropriate namespace imports, then create the function 'BrowserPollResults' on the *HomePageController* class.
|
||||||
|
|
||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePageController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function BrowserPollResults() {
|
public function BrowserPollResults()
|
||||||
$submissions = new GroupedList(BrowserPollSubmission::get());
|
{
|
||||||
$total = $submissions->Count();
|
$submissions = new GroupedList(BrowserPollSubmission::get());
|
||||||
|
$total = $submissions->Count();
|
||||||
|
|
||||||
$list = new ArrayList();
|
$list = new ArrayList();
|
||||||
foreach($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
|
foreach($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
|
||||||
$percentage = (int) ($browserSubmissions->Count() / $total * 100);
|
$percentage = (int) ($browserSubmissions->Count() / $total * 100);
|
||||||
$list->push(new ArrayData(array(
|
$list->push(new ArrayData(array(
|
||||||
'Browser' => $browserName,
|
'Browser' => $browserName,
|
||||||
'Percentage' => $percentage
|
'Percentage' => $percentage
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
This code introduces a few new concepts, so let's step through it.
|
This code introduces a few new concepts, so let's step through it.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$submissions = new GroupedList(BrowserPollSubmission::get());
|
$submissions = new GroupedList(BrowserPollSubmission::get());
|
||||||
```
|
```
|
||||||
|
|
||||||
First we get all of the `BrowserPollSubmission` records from the database. This returns the submissions as a [api:DataList]. Then we wrap it inside a [api:GroupedList], which adds the ability to group those records. The resulting object will behave just like the original `DataList`, though (with the addition of a `groupBy()` method).
|
First we get all of the `BrowserPollSubmission` records from the database. This returns the submissions as a [api:DataList]. Then we wrap it inside a [api:GroupedList], which adds the ability to group those records. The resulting object will behave just like the original `DataList`, though (with the addition of a `groupBy()` method).
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$total = $submissions->Count();
|
$total = $submissions->Count();
|
||||||
```
|
```
|
||||||
We get the total number of submissions, which is needed to calculate the percentages.
|
We get the total number of submissions, which is needed to calculate the percentages.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$list = new ArrayList();
|
$list = new ArrayList();
|
||||||
foreach($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
|
foreach ($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
|
||||||
$percentage = (int) ($browserSubmissions->Count() / $total * 100);
|
$percentage = (int) ($browserSubmissions->Count() / $total * 100);
|
||||||
$list->push(new ArrayData(array(
|
$list->push(new ArrayData(array(
|
||||||
'Browser' => $browserName,
|
'Browser' => $browserName,
|
||||||
'Percentage' => $percentage
|
'Percentage' => $percentage
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we create an empty [api:ArrayList] to hold the data we'll pass to the template. Its similar to [api:DataList], but can hold arbitrary objects rather than just DataObject` instances. Then we iterate over the 'Browser' submissions field.
|
Now we create an empty [api:ArrayList] to hold the data we'll pass to the template. Its similar to [api:DataList], but can hold arbitrary objects rather than just DataObject` instances. Then we iterate over the 'Browser' submissions field.
|
||||||
@ -334,21 +359,21 @@ The final step is to create the template to display our data. Change the 'Browse
|
|||||||
**themes/simple/templates/Layout/HomePage.ss**
|
**themes/simple/templates/Layout/HomePage.ss**
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<div id="BrowserPoll">
|
<div id="BrowserPoll">
|
||||||
<h2>Browser Poll</h2>
|
<h2>Browser Poll</h2>
|
||||||
<% if $BrowserPollForm %>
|
<% if $BrowserPollForm %>
|
||||||
$BrowserPollForm
|
$BrowserPollForm
|
||||||
<% else %>
|
<% else %>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $BrowserPollResults %>
|
<% loop $BrowserPollResults %>
|
||||||
<li>
|
<li>
|
||||||
<div class="browser">$Browser: $Percentage%</div>
|
<div class="browser">$Browser: $Percentage%</div>
|
||||||
<div class="bar" style="width:$Percentage%"> </div>
|
<div class="bar" style="width:$Percentage%"> </div>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we first check if the *BrowserPollForm* is returned, and if it is display it. Otherwise the user has already voted,
|
Here we first check if the *BrowserPollForm* is returned, and if it is display it. Otherwise the user has already voted,
|
||||||
|
@ -22,11 +22,11 @@ make them editable in the CMS, and render them on the website.
|
|||||||
|
|
||||||
This table shows some example data we'll be using:
|
This table shows some example data we'll be using:
|
||||||
|
|
||||||
| Project | Student | Mentor |
|
| Project | Student | Mentor |
|
||||||
| ------- | ------- | ------- |
|
| ------- | ------- | ------- |
|
||||||
| Developer Toolbar | Jakob,Ofir | Mark,Sean |
|
| Developer Toolbar | Jakob,Ofir | Mark,Sean |
|
||||||
| Behaviour Testing | Michal,Wojtek | Ingo, Sean |
|
| Behaviour Testing | Michal,Wojtek | Ingo, Sean |
|
||||||
| Content Personalization | Yuki | Philipp |
|
| Content Personalization | Yuki | Philipp |
|
||||||
| Module Management | Andrew | Marcus,Sam |
|
| Module Management | Andrew | Marcus,Sam |
|
||||||
|
|
||||||
### Has-One and Has-Many Relationships: Project and Student
|
### Has-One and Has-Many Relationships: Project and Student
|
||||||
@ -38,29 +38,49 @@ Let's create the `Student` and `Project` objects.
|
|||||||
|
|
||||||
**mysite/code/Student.php**
|
**mysite/code/Student.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
class Student extends DataObject {
|
|
||||||
private static $db = array(
|
use SilverStripe\ORM\DataObject;
|
||||||
'Name' => 'Varchar',
|
|
||||||
'University' => 'Varchar',
|
class Student extends DataObject
|
||||||
);
|
{
|
||||||
private static $has_one = array(
|
private static $db = array(
|
||||||
'Project' => 'Project'
|
'Name' => 'Varchar',
|
||||||
);
|
'University' => 'Varchar',
|
||||||
}
|
);
|
||||||
|
private static $has_one = array(
|
||||||
|
'Project' => 'Project'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**mysite/code/Project.php**
|
**mysite/code/Project.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
class Project extends Page {
|
|
||||||
private static $has_many = array(
|
use Page;
|
||||||
'Students' => 'Student'
|
|
||||||
);
|
class Project extends Page
|
||||||
}
|
{
|
||||||
class Project_Controller extends Page_Controller {
|
private static $has_many = array(
|
||||||
}
|
'Students' => 'Student'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**mysite/code/ProjectController.php**
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
use PageController;
|
||||||
|
|
||||||
|
class ProjectController extends PageController
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The relationships are defined through the `$has_one`
|
The relationships are defined through the `$has_one`
|
||||||
and `$has_many` properties on the objects.
|
and `$has_many` properties on the objects.
|
||||||
@ -95,15 +115,30 @@ The restriction is enforced through the `$allowed_children` directive.
|
|||||||
|
|
||||||
**mysite/code/ProjectsHolder.php**
|
**mysite/code/ProjectsHolder.php**
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
<?php
|
<?php
|
||||||
class ProjectsHolder extends Page {
|
|
||||||
private static $allowed_children = array(
|
use Page;
|
||||||
'Project'
|
|
||||||
);
|
class ProjectsHolder extends Page {
|
||||||
}
|
private static $allowed_children = array(
|
||||||
class ProjectsHolder_Controller extends Page_Controller {
|
'Project'
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**mysite/code/ProjectsHolderController.php
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PageController;
|
||||||
|
|
||||||
|
class ProjectsHolderController extends PageController
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
You might have noticed that we don't specify the relationship
|
You might have noticed that we don't specify the relationship
|
||||||
to a project. That's because it's already inherited from the parent implementation,
|
to a project. That's because it's already inherited from the parent implementation,
|
||||||
@ -128,32 +163,42 @@ All customization to fields for a page type are managed through a method called
|
|||||||
|
|
||||||
**mysite/code/Project.php**
|
**mysite/code/Project.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
class Project extends Page {
|
|
||||||
// ...
|
use Page;
|
||||||
public function getCMSFields() {
|
use SilverStripe\Forms\GridField;
|
||||||
// Get the fields from the parent implementation
|
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||||
$fields = parent::getCMSFields();
|
|
||||||
// Create a default configuration for the new GridField, allowing record editing
|
class Project extends Page
|
||||||
$config = GridFieldConfig_RelationEditor::create();
|
{
|
||||||
// Set the names and data for our gridfield columns
|
// ...
|
||||||
$config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
|
public function getCMSFields()
|
||||||
'Name' => 'Name',
|
{
|
||||||
'Project.Title'=> 'Project' // Retrieve from a has-one relationship
|
// Get the fields from the parent implementation
|
||||||
));
|
$fields = parent::getCMSFields();
|
||||||
// Create a gridfield to hold the student relationship
|
// Create a default configuration for the new GridField, allowing record editing
|
||||||
$studentsField = new GridField(
|
$config = GridFieldConfig_RelationEditor::create();
|
||||||
'Students', // Field name
|
// Set the names and data for our gridfield columns
|
||||||
'Student', // Field title
|
$config
|
||||||
$this->Students(), // List of all related students
|
->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns')
|
||||||
$config
|
->setDisplayFields(array(
|
||||||
);
|
'Name' => 'Name',
|
||||||
// Create a tab named "Students" and add our field to it
|
'Project.Title'=> 'Project' // Retrieve from a has-one relationship
|
||||||
$fields->addFieldToTab('Root.Students', $studentsField);
|
));
|
||||||
return $fields;
|
// Create a gridfield to hold the student relationship
|
||||||
}
|
$studentsField = new GridField(
|
||||||
}
|
'Students', // Field name
|
||||||
|
'Student', // Field title
|
||||||
|
$this->Students(), // List of all related students
|
||||||
|
$config
|
||||||
|
);
|
||||||
|
// Create a tab named "Students" and add our field to it
|
||||||
|
$fields->addFieldToTab('Root.Students', $studentsField);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This creates a tabular field, which lists related student records, one row at a time.
|
This creates a tabular field, which lists related student records, one row at a time.
|
||||||
It's empty by default, but you can add new students as required,
|
It's empty by default, but you can add new students as required,
|
||||||
@ -169,11 +214,11 @@ One example of this is the configuration of column names on our table:
|
|||||||
We call `setDisplayFields()` directly on the component responsible for their rendering.
|
We call `setDisplayFields()` directly on the component responsible for their rendering.
|
||||||
|
|
||||||
<div class="note" markdown="1">
|
<div class="note" markdown="1">
|
||||||
Adding a `GridField` to a page type is a popular way to manage data,
|
Adding a `GridField` to a page type is a popular way to manage data,
|
||||||
but not the only one. If your data requires a dedicated interface
|
but not the only one. If your data requires a dedicated interface
|
||||||
with more sophisticated search and management logic, consider
|
with more sophisticated search and management logic, consider
|
||||||
using the [ModelAdmin](/developer_guides/customising_the_admin_interface/modeladmin)
|
using the [ModelAdmin](/developer_guides/customising_the_admin_interface/modeladmin)
|
||||||
interface instead.
|
interface instead.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
![tutorial:tutorial5_project_creation.jpg](../_images/tutorial5_project_creation.jpg)
|
![tutorial:tutorial5_project_creation.jpg](../_images/tutorial5_project_creation.jpg)
|
||||||
@ -200,26 +245,37 @@ The first step is to create the `Mentor` object and set the relation with the `P
|
|||||||
|
|
||||||
**mysite/code/Mentor.php**
|
**mysite/code/Mentor.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
class Mentor extends DataObject {
|
|
||||||
private static $db = array(
|
use SilverStripe\ORM\DataObject;
|
||||||
'Name' => 'Varchar',
|
|
||||||
);
|
class Mentor extends DataObject
|
||||||
private static $belongs_many_many = array(
|
{
|
||||||
'Projects' => 'Project'
|
private static $db = array(
|
||||||
);
|
'Name' => 'Varchar',
|
||||||
}
|
);
|
||||||
|
private static $belongs_many_many = array(
|
||||||
|
'Projects' => 'Project'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**mysite/code/Project.php**
|
**mysite/code/Project.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class Project extends Page {
|
<?php
|
||||||
// ...
|
|
||||||
private static $many_many = array(
|
use Page;
|
||||||
'Mentors' => 'Mentor'
|
|
||||||
);
|
class Project extends Page
|
||||||
}
|
{
|
||||||
|
// ...
|
||||||
|
private static $many_many = array(
|
||||||
|
'Mentors' => 'Mentor'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This code will create a relationship between the `Project` table and the `Mentor` table by storing the ids of the respective `Project` and `Mentor` in a another table named "Project_Mentors"
|
This code will create a relationship between the `Project` table and the `Mentor` table by storing the ids of the respective `Project` and `Mentor` in a another table named "Project_Mentors"
|
||||||
(after you've performed a `dev/build` command, of course).
|
(after you've performed a `dev/build` command, of course).
|
||||||
@ -231,22 +287,31 @@ to configure it a bit differently.
|
|||||||
|
|
||||||
**mysite/code/Project.php**
|
**mysite/code/Project.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class Project extends Page {
|
<?php
|
||||||
// ...
|
|
||||||
public function getCMSFields() {
|
use Page;
|
||||||
// ...
|
use SilverStripe\Forms\GridField;
|
||||||
// Same setup, but for mentors
|
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||||
$mentorsField = new GridField(
|
|
||||||
'Mentors',
|
class Project extends Page
|
||||||
'Mentors',
|
{
|
||||||
$this->Mentors(),
|
// ...
|
||||||
GridFieldConfig_RelationEditor::create()
|
public function getCMSFields()
|
||||||
);
|
{
|
||||||
$fields->addFieldToTab('Root.Mentors', $mentorsField);
|
// ...
|
||||||
return $fields;
|
// Same setup, but for mentors
|
||||||
}
|
$mentorsField = new GridField(
|
||||||
}
|
'Mentors',
|
||||||
|
'Mentors',
|
||||||
|
$this->Mentors(),
|
||||||
|
GridFieldConfig_RelationEditor::create()
|
||||||
|
);
|
||||||
|
$fields->addFieldToTab('Root.Mentors', $mentorsField);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The important difference to our student management UI is the usage
|
The important difference to our student management UI is the usage
|
||||||
of `$this->Mentor()` (rather than `Mentor::get()`). It will limit
|
of `$this->Mentor()` (rather than `Mentor::get()`). It will limit
|
||||||
@ -286,44 +351,45 @@ a named list of object.
|
|||||||
|
|
||||||
**themes/simple/templates/Layout/ProjectsHolder.ss**
|
**themes/simple/templates/Layout/ProjectsHolder.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
<div class="content-container unit size3of4 lastUnit">
|
<div class="content-container unit size3of4 lastUnit">
|
||||||
<article>
|
<article>
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
$Content
|
$Content
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Project</th>
|
<th>Project</th>
|
||||||
<th>Students</th>
|
<th>Students</th>
|
||||||
<th>Mentors</th>
|
<th>Mentors</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="$Link">$Title</a>
|
<a href="$Link">$Title</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<% loop $Students %>
|
<% loop $Students %>
|
||||||
$Name ($University)<% if $Last !=1 %>,<% end_if %>
|
$Name ($University)<% if not $Last %>, <% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<% loop $Mentors %>
|
<% loop $Mentors %>
|
||||||
$Name<% if $Last !=1 %>,<% end_if %>
|
$Name<% if not $Last %>, <% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
Navigate to the holder page through your website navigation,
|
Navigate to the holder page through your website navigation,
|
||||||
or the "Preview" feature in the CMS. You should see a list of all projects now.
|
or the "Preview" feature in the CMS. You should see a list of all projects now.
|
||||||
@ -344,36 +410,37 @@ we can access the "Students" and "Mentors" relationships directly in the templat
|
|||||||
|
|
||||||
**themes/simple/templates/Layout/Project.ss**
|
**themes/simple/templates/Layout/Project.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
<div class="content-container unit size3of4 lastUnit">
|
<div class="content-container unit size3of4 lastUnit">
|
||||||
<article>
|
<article>
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
$Content
|
$Content
|
||||||
<h2>Students</h2>
|
<h2>Students</h2>
|
||||||
<% if $Students %>
|
<% if $Students %>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Students %>
|
<% loop $Students %>
|
||||||
<li>$Name ($University)</li>
|
<li>$Name ($University)</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p>No students found</p>
|
<p>No students found</p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<h2>Mentors</h2>
|
<h2>Mentors</h2>
|
||||||
<% if $Mentors %>
|
<% if $Mentors %>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Mentors %>
|
<% loop $Mentors %>
|
||||||
<li>$Name</li>
|
<li>$Name</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p>No mentors found</p>
|
<p>No mentors found</p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
Follow the link to a project detail from from your holder page,
|
Follow the link to a project detail from from your holder page,
|
||||||
or navigate to it through the submenu provided by the theme.
|
or navigate to it through the submenu provided by the theme.
|
||||||
@ -387,17 +454,23 @@ by introducing a new template for them.
|
|||||||
|
|
||||||
**themes/simple/templates/Includes/StudentInfo.ss**
|
**themes/simple/templates/Includes/StudentInfo.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
$Name ($University)
|
$Name ($University)
|
||||||
|
```
|
||||||
|
|
||||||
To use this template, we need to add a new method to our student class:
|
To use this template, we need to add a new method to our student class:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class Student extends DataObject {
|
use SilverStripe\ORM\DataObject;
|
||||||
function getInfo() {
|
|
||||||
return $this->renderWith('StudentInfo');
|
class Student extends DataObject
|
||||||
}
|
{
|
||||||
}
|
public function getInfo()
|
||||||
|
{
|
||||||
|
return $this->renderWith('StudentInfo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Replace the student template code in both `Project.ss`
|
Replace the student template code in both `Project.ss`
|
||||||
and `ProjectHolder.ss` templates with the new placeholder, `$Info`.
|
and `ProjectHolder.ss` templates with the new placeholder, `$Info`.
|
||||||
|
@ -98,7 +98,8 @@ Variables can come from your database fields, or custom methods you define on yo
|
|||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
public function UsersIpAddress() {
|
public function UsersIpAddress()
|
||||||
|
{
|
||||||
return $this->getRequest()->getIP();
|
return $this->getRequest()->getIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,8 +113,8 @@ Variables can come from your database fields, or custom methods you define on yo
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
The variables that can be used in a template vary based on the object currently in [scope](#scope). Scope defines what
|
The variables that can be used in a template vary based on the object currently in [scope](#scope). Scope defines what
|
||||||
object the methods get called on. For the standard `Page.ss` template the scope is the current [api:Page_Controller]
|
object the methods get called on. For the standard `Page.ss` template the scope is the current [api:PageController]
|
||||||
class. This object gives you access to all the database fields on [api:Page_Controller], its corresponding [api:Page]
|
class. This object gives you access to all the database fields on [api:PageController], its corresponding [api:Page]
|
||||||
record and any subclasses of those two.
|
record and any subclasses of those two.
|
||||||
|
|
||||||
**mysite/code/Layout/Page.ss**
|
**mysite/code/Layout/Page.ss**
|
||||||
@ -407,12 +408,12 @@ In the `<% loop %>` section, we saw an example of two **scopes**. Outside the `<
|
|||||||
the scope of the top level `Page`. But inside the loop, we were in the scope of an item in the list (i.e the `Child`)
|
the scope of the top level `Page`. But inside the loop, we were in the scope of an item in the list (i.e the `Child`)
|
||||||
|
|
||||||
The scope determines where the value comes from when you refer to a variable. Typically the outer scope of a `Page.ss`
|
The scope determines where the value comes from when you refer to a variable. Typically the outer scope of a `Page.ss`
|
||||||
layout template is the [api:Page_Controller] that is currently being rendered.
|
layout template is the [api:PageController] that is currently being rendered.
|
||||||
|
|
||||||
When the scope is a `Page_Controller` it will automatically also look up any methods in the corresponding `Page` data
|
When the scope is a `PageController` it will automatically also look up any methods in the corresponding `Page` data
|
||||||
record. In the case of `$Title` the flow looks like
|
record. In the case of `$Title` the flow looks like
|
||||||
|
|
||||||
$Title --> [Looks up: Current Page_Controller and parent classes] --> [Looks up: Current Page and parent classes]
|
$Title --> [Looks up: Current PageController and parent classes] --> [Looks up: Current Page and parent classes]
|
||||||
|
|
||||||
The list of variables you could use in your template is the total of all the methods in the current scope object, parent
|
The list of variables you could use in your template is the total of all the methods in the current scope object, parent
|
||||||
classes of the current scope object, and any [api:Extension] instances you have.
|
classes of the current scope object, and any [api:Extension] instances you have.
|
||||||
|
@ -15,7 +15,7 @@ scope, and you can specify additional static methods to be available globally in
|
|||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
Want a quick way of knowing what scope you're in? Try putting `$ClassName` in your template. You should see a string
|
Want a quick way of knowing what scope you're in? Try putting `$ClassName` in your template. You should see a string
|
||||||
such as `Page` of the object that's in scope. The methods you can call on that object then are any functions, database
|
such as `Page` of the object that's in scope. The methods you can call on that object then are any functions, database
|
||||||
properties or relations on the `Page` class, `Page_Controller` class as well as anything from their subclasses **or**
|
properties or relations on the `Page` class, `PageController` class as well as anything from their subclasses **or**
|
||||||
extensions.
|
extensions.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -11,79 +11,88 @@ subclasses).
|
|||||||
The following will render the given data into a template. Given the template:
|
The following will render the given data into a template. Given the template:
|
||||||
|
|
||||||
**mysite/templates/Coach_Message.ss**
|
**mysite/templates/Coach_Message.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<strong>$Name</strong> is the $Role on our team.
|
<strong>$Name</strong> is the $Role on our team.
|
||||||
|
```
|
||||||
|
|
||||||
Our application code can render into that view using `renderWith`. This method is called on the [api:ViewableData]
|
Our application code can render into that view using `renderWith`. This method is called on the [api:ViewableData]
|
||||||
instance with a template name or an array of templates to render.
|
instance with a template name or an array of templates to render.
|
||||||
|
|
||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$arrayData = new ArrayData(array(
|
$arrayData = new ArrayData(array(
|
||||||
'Name' => 'John',
|
'Name' => 'John',
|
||||||
'Role' => 'Head Coach'
|
'Role' => 'Head Coach'
|
||||||
));
|
));
|
||||||
|
|
||||||
echo $arrayData->renderWith('Coach_Message');
|
echo $arrayData->renderWith('Coach_Message');
|
||||||
|
|
||||||
// returns "<strong>John</strong> is the Head Coach on our team."
|
// returns "<strong>John</strong> is the Head Coach on our team."
|
||||||
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
Most classes in SilverStripe you want in your template extend `ViewableData` and allow you to call `renderWith`. This
|
Most classes in SilverStripe you want in your template extend `ViewableData` and allow you to call `renderWith`. This
|
||||||
includes [api:Controller], [api:FormField] and [api:DataObject] instances.
|
includes [api:Controller], [api:FormField] and [api:DataObject] instances.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$controller->renderWith(array("MyController", "MyBaseController"));
|
$controller->renderWith(array('MyController', 'MyBaseController'));
|
||||||
|
|
||||||
Member::currentUser()->renderWith('Member_Profile');
|
Member::currentUser()->renderWith('Member_Profile');
|
||||||
|
```
|
||||||
|
|
||||||
`renderWith` can be used to override the default template process. For instance, to provide an ajax version of a
|
`renderWith` can be used to override the default template process. For instance, to provide an ajax version of a
|
||||||
template.
|
template.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
private static $allowed_actions = array('iwantmyajax');
|
class PageController extends ContentController
|
||||||
|
{
|
||||||
|
private static $allowed_actions = array('iwantmyajax');
|
||||||
|
|
||||||
public function iwantmyajax() {
|
public function iwantmyajax()
|
||||||
if(Director::is_ajax()) {
|
{
|
||||||
return $this->renderWith("AjaxTemplate");
|
if (Director::is_ajax()) {
|
||||||
} else {
|
return $this->renderWith('AjaxTemplate');
|
||||||
return $this->httpError(404);
|
} else {
|
||||||
}
|
return $this->httpError(404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Any data you want to render into the template that does not extend `ViewableData` should be wrapped in an object that
|
Any data you want to render into the template that does not extend `ViewableData` should be wrapped in an object that
|
||||||
does, such as `ArrayData` or `ArrayList`.
|
does, such as `ArrayData` or `ArrayList`.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
..
|
class PageController extends ContentController
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
public function iwantmyajax()
|
||||||
|
{
|
||||||
|
if (Director::is_ajax()) {
|
||||||
|
$experience = new ArrayList();
|
||||||
|
$experience->push(new ArrayData(array(
|
||||||
|
'Title' => 'First Job'
|
||||||
|
)));
|
||||||
|
|
||||||
public function iwantmyajax() {
|
return $this->customise(new ArrayData(array(
|
||||||
if(Director::is_ajax()) {
|
'Name' => 'John',
|
||||||
$experience = new ArrayList();
|
'Role' => 'Head Coach',
|
||||||
$experience->push(new ArrayData(array(
|
'Experience' => $experience
|
||||||
'Title' => 'First Job'
|
)))->renderWith('AjaxTemplate');
|
||||||
)));
|
} else {
|
||||||
|
return $this->httpError(404);
|
||||||
return $this->customise(new ArrayData(array(
|
}
|
||||||
'Name' => 'John',
|
}
|
||||||
'Role' => 'Head Coach',
|
}
|
||||||
'Experience' => $experience
|
```
|
||||||
)))->renderWith("AjaxTemplate");
|
|
||||||
} else {
|
|
||||||
return $this->httpError(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ at http://yoursite.com/teams/ and the `players` custom action is at http://yours
|
|||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
If you're using the `cms` module with and dealing with `Page` objects then for your custom `Page Type` controllers you
|
If you're using the `cms` module with and dealing with `Page` objects then for your custom `Page Type` controllers you
|
||||||
would extend `ContentController` or `Page_Controller`. You don't need to define the routes value as the `cms` handles
|
would extend `ContentController` or `PageController`. You don't need to define the routes value as the `cms` handles
|
||||||
routing.
|
routing.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ summary: A more in depth look at how to map requests to particular controllers a
|
|||||||
# Routing
|
# Routing
|
||||||
|
|
||||||
Routing is the process of mapping URL's to [api:Controllers] and actions. In the introduction we defined a new custom route
|
Routing is the process of mapping URL's to [api:Controllers] and actions. In the introduction we defined a new custom route
|
||||||
for our `TeamsController` mapping any `teams` URL to our `TeamsController`
|
for our `TeamController` mapping any `teams` URL to our `TeamController`
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
If you're using the `cms` module with and dealing with `Page` objects then for your custom `Page Type` controllers you
|
If you're using the `cms` module with and dealing with `Page` objects then for your custom `Page Type` controllers you
|
||||||
would extend `ContentController` or `Page_Controller`. You don't need to define the routes value as the `cms` handles
|
would extend `ContentController` or `PageController`. You don't need to define the routes value as the `cms` handles
|
||||||
routing.
|
routing.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -17,16 +17,17 @@ These routes by standard, go into a `routes.yml` file in your applications `_con
|
|||||||
|
|
||||||
**mysite/_config/routes.yml**
|
**mysite/_config/routes.yml**
|
||||||
|
|
||||||
:::yml
|
```yml
|
||||||
---
|
---
|
||||||
Name: mysiteroutes
|
Name: mysiteroutes
|
||||||
After: framework/routes#coreroutes
|
After: framework/routes#coreroutes
|
||||||
---
|
---
|
||||||
Director:
|
Director:
|
||||||
rules:
|
rules:
|
||||||
'teams//$Action/$ID/$Name': 'TeamController'
|
'teams//$Action/$ID/$Name': 'TeamController'
|
||||||
'player/': 'PlayerController'
|
'player/': 'PlayerController'
|
||||||
'': 'HomeController'
|
'': 'HomeController'
|
||||||
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
To understand the syntax for the `routes.yml` file better, read the [Configuration](../configuration) documentation.
|
To understand the syntax for the `routes.yml` file better, read the [Configuration](../configuration) documentation.
|
||||||
@ -34,8 +35,9 @@ To understand the syntax for the `routes.yml` file better, read the [Configurati
|
|||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
:::yml
|
```yml
|
||||||
'teams//$Action/$ID/$Name': 'TeamController'
|
'teams//$Action/$ID/$Name': 'TeamController'
|
||||||
|
```
|
||||||
|
|
||||||
This route has defined that any URL beginning with `team` should create, and be handled by a `TeamController` instance.
|
This route has defined that any URL beginning with `team` should create, and be handled by a `TeamController` instance.
|
||||||
|
|
||||||
@ -49,49 +51,49 @@ All Controllers have access to `$this->getRequest()` for the request object and
|
|||||||
|
|
||||||
Here is what those parameters would look like for certain requests
|
Here is what those parameters would look like for certain requests
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
// GET /teams/
|
// GET /teams/
|
||||||
|
|
||||||
print_r($this->getRequest()->params());
|
print_r($this->getRequest()->params());
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
// (
|
// (
|
||||||
// [Action] => null
|
// [Action] => null
|
||||||
// [ID] => null
|
// [ID] => null
|
||||||
// [Name] => null
|
// [Name] => null
|
||||||
// )
|
// )
|
||||||
|
|
||||||
// GET /teams/players/
|
// GET /teams/players/
|
||||||
|
|
||||||
print_r($this->getRequest()->params());
|
print_r($this->getRequest()->params());
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
// (
|
// (
|
||||||
// [Action] => 'players'
|
// [Action] => 'players'
|
||||||
// [ID] => null
|
// [ID] => null
|
||||||
// [Name] => null
|
// [Name] => null
|
||||||
// )
|
// )
|
||||||
|
|
||||||
// GET /teams/players/1
|
// GET /teams/players/1
|
||||||
|
|
||||||
print_r($this->getRequest()->params());
|
print_r($this->getRequest()->params());
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
// (
|
// (
|
||||||
// [Action] => 'players'
|
// [Action] => 'players'
|
||||||
// [ID] => 1
|
// [ID] => 1
|
||||||
// [Name] => null
|
// [Name] => null
|
||||||
// )
|
// )
|
||||||
|
```
|
||||||
|
|
||||||
You can also fetch one parameter at a time.
|
You can also fetch one parameter at a time.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
|
// GET /teams/players/1/
|
||||||
// GET /teams/players/1/
|
|
||||||
|
|
||||||
echo $this->getRequest()->param('ID');
|
|
||||||
// returns '1'
|
|
||||||
|
|
||||||
|
echo $this->getRequest()->param('ID');
|
||||||
|
// returns '1'
|
||||||
|
```
|
||||||
|
|
||||||
## URL Patterns
|
## URL Patterns
|
||||||
|
|
||||||
@ -108,26 +110,29 @@ A rule must always start with alphabetical ([A-Za-z]) characters or a $Variable
|
|||||||
| `!` | **Require Variable** - Placing this after a parameter variable requires data to be present for the rule to match |
|
| `!` | **Require Variable** - Placing this after a parameter variable requires data to be present for the rule to match |
|
||||||
| `//` | **Shift Point** - Declares that only variables denoted with a $ are parsed into the $params AFTER this point in the regex |
|
| `//` | **Shift Point** - Declares that only variables denoted with a $ are parsed into the $params AFTER this point in the regex |
|
||||||
|
|
||||||
:::yml
|
```yml
|
||||||
'teams/$Action/$ID/$OtherID': 'TeamController'
|
'teams/$Action/$ID/$OtherID': 'TeamController'
|
||||||
|
|
||||||
# /teams/
|
# /teams/
|
||||||
# /teams/players/
|
# /teams/players/
|
||||||
# /teams/
|
# /teams/
|
||||||
|
```
|
||||||
|
|
||||||
Standard URL handler syntax. For any URL that contains 'team' this rule will match and hand over execution to the
|
Standard URL handler syntax. For any URL that contains 'team' this rule will match and hand over execution to the
|
||||||
matching controller. The `TeamsController` is passed an optional action, id and other id parameters to do any more
|
matching controller. The `TeamsController` is passed an optional action, id and other id parameters to do any more
|
||||||
decision making.
|
decision making.
|
||||||
|
|
||||||
:::yml
|
```yml
|
||||||
'teams/$Action!/$ID!/': 'TeamController'
|
'teams/$Action!/$ID!/': 'TeamController'
|
||||||
|
```
|
||||||
|
|
||||||
This does the same matching as the previous example, any URL starting with `teams` will look at this rule **but** both
|
This does the same matching as the previous example, any URL starting with `teams` will look at this rule **but** both
|
||||||
`$Action` and `$ID` are required. Any requests to `team/` will result in a `404` error rather than being handed off to
|
`$Action` and `$ID` are required. Any requests to `team/` will result in a `404` error rather than being handed off to
|
||||||
the `TeamController`.
|
the `TeamController`.
|
||||||
|
|
||||||
:::yml
|
```yml
|
||||||
`admin/help//$Action/$ID`: 'AdminHelp'
|
'admin/help//$Action/$ID: 'AdminHelp'
|
||||||
|
```
|
||||||
|
|
||||||
Match an url starting with `/admin/help/`, but don't include `/help/` as part of the action (the shift point is set to
|
Match an url starting with `/admin/help/`, but don't include `/help/` as part of the action (the shift point is set to
|
||||||
start parsing variables and the appropriate controller action AFTER the `//`).
|
start parsing variables and the appropriate controller action AFTER the `//`).
|
||||||
@ -152,19 +157,22 @@ This is useful when you want to provide custom actions for the mapping of `teams
|
|||||||
|
|
||||||
**mysite/code/controllers/TeamController.php**
|
**mysite/code/controllers/TeamController.php**
|
||||||
|
|
||||||
:::php
|
```
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class TeamController extends Controller {
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
class TeamController extends Controller
|
||||||
'payroll'
|
{
|
||||||
);
|
private static $allowed_actions = array(
|
||||||
|
'payroll'
|
||||||
|
);
|
||||||
|
|
||||||
private static $url_handlers = array(
|
private static $url_handlers = array(
|
||||||
'staff/$ID/$Name' => 'payroll',
|
'staff/$ID/$Name' => 'payroll',
|
||||||
'coach/$ID/$Name' => 'payroll'
|
'coach/$ID/$Name' => 'payroll'
|
||||||
);
|
);
|
||||||
|
```
|
||||||
|
|
||||||
The syntax for the `$url_handlers` array users the same pattern matches as the `YAML` configuration rules.
|
The syntax for the `$url_handlers` array users the same pattern matches as the `YAML` configuration rules.
|
||||||
|
|
||||||
@ -175,28 +183,34 @@ class specifies the URL pattern in `$url_handlers`. Notice that it defines 5
|
|||||||
parameters.
|
parameters.
|
||||||
|
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class FeedController extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
private static $allowed_actions = array('go');
|
class FeedController extends ContentController
|
||||||
private static $url_handlers = array(
|
{
|
||||||
'go/$UserName/$AuthToken/$Timestamp/$OutputType/$DeleteMode' => 'go'
|
private static $allowed_actions = array('go');
|
||||||
);
|
private static $url_handlers = array(
|
||||||
public function go() {
|
'go/$UserName/$AuthToken/$Timestamp/$OutputType/$DeleteMode' => 'go'
|
||||||
$this->validateUser(
|
);
|
||||||
$this->getRequest()->param('UserName'),
|
|
||||||
$this->getRequest()->param('AuthToken')
|
public function go()
|
||||||
);
|
{
|
||||||
/* more processing goes here */
|
$this->validateUser(
|
||||||
}
|
$this->getRequest()->param('UserName'),
|
||||||
|
$this->getRequest()->param('AuthToken')
|
||||||
|
);
|
||||||
|
/* more processing goes here */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
The YAML rule, in contrast, is simple. It needs to provide only enough
|
The YAML rule, in contrast, is simple. It needs to provide only enough
|
||||||
information for the framework to choose the desired controller.
|
information for the framework to choose the desired controller.
|
||||||
|
|
||||||
:::yaml
|
```yml
|
||||||
Director:
|
Director:
|
||||||
rules:
|
rules:
|
||||||
'feed': 'FeedController'
|
'feed': 'FeedController'
|
||||||
|
```
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
@ -14,56 +14,66 @@ See the [Forms Tutorial](../../tutorials/forms/) for a step by step process of c
|
|||||||
|
|
||||||
Creating a [api:Form] has the following signature.
|
Creating a [api:Form] has the following signature.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$form = new Form(
|
$form = new Form(
|
||||||
$controller, // the Controller to render this form on
|
$controller, // the Controller to render this form on
|
||||||
$name, // name of the method that returns this form on the controller
|
$name, // name of the method that returns this form on the controller
|
||||||
FieldList $fields, // list of FormField instances
|
FieldList $fields, // list of FormField instances
|
||||||
FieldList $actions, // list of FormAction instances
|
FieldList $actions, // list of FormAction instances
|
||||||
$required // optional use of RequiredFields object
|
$required // optional use of RequiredFields object
|
||||||
);
|
);
|
||||||
|
|
||||||
In practice, this looks like:
|
In practice, this looks like:
|
||||||
|
|
||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
use SilverStripe\Forms\FieldList;
|
||||||
private static $allowed_actions = array(
|
use SilverStripe\Forms\Form;
|
||||||
'HelloForm'
|
use SilverStripe\Forms\FormAction;
|
||||||
);
|
use SilverStripe\Forms\RequiredFields;
|
||||||
|
use SilverStripe\Forms\TextField;
|
||||||
public function HelloForm() {
|
|
||||||
$fields = new FieldList(
|
|
||||||
TextField::create('Name', 'Your Name')
|
|
||||||
);
|
|
||||||
|
|
||||||
$actions = new FieldList(
|
class PageController extends ContentController
|
||||||
FormAction::create("doSayHello")->setTitle("Say hello")
|
{
|
||||||
);
|
private static $allowed_actions = array(
|
||||||
|
'HelloForm'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function HelloForm()
|
||||||
|
{
|
||||||
|
$fields = new FieldList(
|
||||||
|
TextField::create('Name', 'Your Name')
|
||||||
|
);
|
||||||
|
|
||||||
$required = new RequiredFields('Name');
|
$actions = new FieldList(
|
||||||
|
FormAction::create('doSayHello')->setTitle('Say hello')
|
||||||
|
);
|
||||||
|
|
||||||
$form = new Form($this, 'HelloForm', $fields, $actions, $required);
|
$required = new RequiredFields('Name');
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function doSayHello($data, Form $form) {
|
|
||||||
$form->sessionMessage('Hello '. $data['Name'], 'success');
|
|
||||||
|
|
||||||
return $this->redirectBack();
|
$form = new Form($this, 'HelloForm', $fields, $actions, $required);
|
||||||
}
|
|
||||||
}
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doSayHello($data, Form $form)
|
||||||
|
{
|
||||||
|
$form->sessionMessage('Hello ' . $data['Name'], 'success');
|
||||||
|
|
||||||
|
return $this->redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**mysite/templates/Page.ss**
|
**mysite/templates/Page.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
$HelloForm
|
$HelloForm
|
||||||
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
The examples above use `FormField::create()` instead of the `new` operator (`new FormField()`). These are functionally
|
The examples above use `FormField::create()` instead of the `new` operator (`new FormField()`). These are functionally
|
||||||
@ -80,10 +90,11 @@ the [api:FormActions]. The URL is known as the `$controller` instance will know
|
|||||||
Because the `HelloForm()` method will be the location the user is taken to, it needs to be handled like any other
|
Because the `HelloForm()` method will be the location the user is taken to, it needs to be handled like any other
|
||||||
controller action. To grant it access through URLs, we add it to the `$allowed_actions` array.
|
controller action. To grant it access through URLs, we add it to the `$allowed_actions` array.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
'HelloForm'
|
'HelloForm'
|
||||||
);
|
);
|
||||||
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
Form actions (`doSayHello`), on the other hand, should _not_ be included in `$allowed_actions`; these are handled
|
Form actions (`doSayHello`), on the other hand, should _not_ be included in `$allowed_actions`; these are handled
|
||||||
@ -96,8 +107,9 @@ separately through [api:Form::httpSubmission()].
|
|||||||
Fields in a [api:Form] are represented as a single [api:FieldList] instance containing subclasses of [api:FormField].
|
Fields in a [api:Form] are represented as a single [api:FieldList] instance containing subclasses of [api:FormField].
|
||||||
Some common examples are [api:TextField] or [api:DropdownField].
|
Some common examples are [api:TextField] or [api:DropdownField].
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
TextField::create($name, $title, $value);
|
TextField::create($name, $title, $value);
|
||||||
|
```
|
||||||
|
|
||||||
<div class="info" markdown='1'>
|
<div class="info" markdown='1'>
|
||||||
A list of the common FormField subclasses is available on the [Common Subclasses](field_types/common_subclasses/) page.
|
A list of the common FormField subclasses is available on the [Common Subclasses](field_types/common_subclasses/) page.
|
||||||
@ -106,48 +118,52 @@ A list of the common FormField subclasses is available on the [Common Subclasses
|
|||||||
The fields are added to the [api:FieldList] `fields` property on the `Form` and can be modified at up to the point the
|
The fields are added to the [api:FieldList] `fields` property on the `Form` and can be modified at up to the point the
|
||||||
`Form` is rendered.
|
`Form` is rendered.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
TextField::create('Name'),
|
TextField::create('Name'),
|
||||||
EmailField::create('Email')
|
EmailField::create('Email')
|
||||||
);
|
);
|
||||||
|
|
||||||
$form = new Form($controller, 'MethodName', $fields, ...);
|
$form = new Form($controller, 'MethodName', $fields, ...);
|
||||||
|
|
||||||
// or use `setFields`
|
// or use `setFields`
|
||||||
$form->setFields($fields);
|
$form->setFields($fields);
|
||||||
|
|
||||||
// to fetch the current fields..
|
// to fetch the current fields..
|
||||||
$fields = $form->getFields();
|
$fields = $form->getFields();
|
||||||
|
```
|
||||||
|
|
||||||
A field can be appended to the [api:FieldList].
|
A field can be appended to the [api:FieldList].
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$fields = $form->Fields();
|
$fields = $form->Fields();
|
||||||
|
|
||||||
// add a field
|
// add a field
|
||||||
$fields->push(TextField::create(..));
|
$fields->push(TextField::create(/* ... */));
|
||||||
|
|
||||||
// insert a field before another one
|
// insert a field before another one
|
||||||
$fields->insertBefore(TextField::create(..), 'Email');
|
$fields->insertBefore(TextField::create(/* ... */), 'Email');
|
||||||
|
|
||||||
// insert a field after another one
|
// insert a field after another one
|
||||||
$fields->insertAfter(TextField::create(..), 'Name');
|
$fields->insertAfter(TextField::create(/* ... */), 'Name');
|
||||||
|
|
||||||
// insert a tab before the main content tab (used to position tabs in the CMS)
|
// insert a tab before the main content tab (used to position tabs in the CMS)
|
||||||
$fields->insertBefore(Tab::create(...), 'Main');
|
$fields->insertBefore(Tab::create(/* ... */), 'Main');
|
||||||
// Note: you need to create and position the new tab prior to adding fields via addFieldToTab()
|
// Note: you need to create and position the new tab prior to adding fields via addFieldToTab()
|
||||||
|
```
|
||||||
|
|
||||||
Fields can be fetched after they have been added in.
|
Fields can be fetched after they have been added in.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$email = $form->Fields()->dataFieldByName('Email');
|
$email = $form->Fields()->dataFieldByName('Email');
|
||||||
$email->setTitle('Your Email Address');
|
$email->setTitle('Your Email Address');
|
||||||
|
```
|
||||||
|
|
||||||
Fields can be removed from the form.
|
Fields can be removed from the form.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$form->getFields()->removeByName('Email');
|
$form->getFields()->removeByName('Email');
|
||||||
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
Forms can be tabbed (such as the CMS interface). In these cases, there are additional functions such as `addFieldToTab`
|
Forms can be tabbed (such as the CMS interface). In these cases, there are additional functions such as `addFieldToTab`
|
||||||
@ -164,13 +180,14 @@ default `FormField` object has several methods for doing common operations.
|
|||||||
Most of the `set` operations will return the object back so methods can be chained.
|
Most of the `set` operations will return the object back so methods can be chained.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$field = new TextField(..);
|
$field = new TextField(..);
|
||||||
|
|
||||||
$field
|
$field
|
||||||
->setMaxLength(100)
|
->setMaxLength(100)
|
||||||
->setAttribute('placeholder', 'Enter a value..')
|
->setAttribute('placeholder', 'Enter a value..')
|
||||||
->setTitle('');
|
->setTitle('');
|
||||||
|
```
|
||||||
|
|
||||||
### Custom Templates
|
### Custom Templates
|
||||||
|
|
||||||
@ -178,61 +195,67 @@ The [api:Form] HTML markup and each of the [api:FormField] instances are rendere
|
|||||||
templates by using the `setTemplate` method on either the `Form` or `FormField`. For more details on providing custom
|
templates by using the `setTemplate` method on either the `Form` or `FormField`. For more details on providing custom
|
||||||
templates see [Form Templates](form_templates)
|
templates see [Form Templates](form_templates)
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(..);
|
||||||
|
|
||||||
$form->setTemplate('CustomForm');
|
$form->setTemplate('CustomForm');
|
||||||
|
|
||||||
// or, for a FormField
|
// or, for a FormField
|
||||||
$field = new TextField(..);
|
$field = new TextField(..);
|
||||||
|
|
||||||
$field->setTemplate('CustomTextField');
|
$field->setTemplate('CustomTextField');
|
||||||
$field->setFieldHolderTemplate('CustomTextField_Holder');
|
$field->setFieldHolderTemplate('CustomTextField_Holder');
|
||||||
|
```
|
||||||
|
|
||||||
## Adding FormActions
|
## Adding FormActions
|
||||||
|
|
||||||
[api:FormAction] objects are displayed at the bottom of the `Form` in the form of a `button` or `input` tag. When a
|
[api:FormAction] objects are displayed at the bottom of the `Form` in the form of a `button` or `input` tag. When a
|
||||||
user presses the button, the form is submitted to the corresponding method.
|
user presses the button, the form is submitted to the corresponding method.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
FormAction::create($action, $title);
|
FormAction::create($action, $title);
|
||||||
|
```
|
||||||
|
|
||||||
As with [api:FormField], the actions for a `Form` are stored within a [api:FieldList] instance in the `actions` property
|
As with [api:FormField], the actions for a `Form` are stored within a [api:FieldList] instance in the `actions` property
|
||||||
on the form.
|
on the form.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
public function MyForm() {
|
public function MyForm()
|
||||||
$fields = new FieldList(..);
|
{
|
||||||
|
$fields = new FieldList(/* .. */);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSubmitForm', 'Submit')
|
FormAction::create('doSubmitForm', 'Submit')
|
||||||
);
|
);
|
||||||
|
|
||||||
$form = new Form($controller, 'MyForm', $fields, $actions);
|
$form = new Form($controller, 'MyForm', $fields, $actions);
|
||||||
|
|
||||||
// Get the actions
|
// Get the actions
|
||||||
$actions = $form->Actions();
|
$actions = $form->Actions();
|
||||||
|
|
||||||
// As actions is a FieldList, push, insertBefore, removeByName and other
|
// As actions is a FieldList, push, insertBefore, removeByName and other
|
||||||
// methods described for `Fields` also work for actions.
|
// methods described for `Fields` also work for actions.
|
||||||
|
|
||||||
$actions->push(
|
$actions->push(
|
||||||
FormAction::create('doSecondaryFormAction', 'Another Button')
|
FormAction::create('doSecondaryFormAction', 'Another Button')
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions->removeByName('doSubmitForm');
|
$actions->removeByName('doSubmitForm');
|
||||||
$form->setActions($actions);
|
$form->setActions($actions);
|
||||||
|
|
||||||
return $form
|
return $form
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSubmitForm($data, $form) {
|
public function doSubmitForm($data, $form)
|
||||||
//
|
{
|
||||||
}
|
//
|
||||||
|
}
|
||||||
|
|
||||||
public function doSecondaryFormAction($data, $form) {
|
public function doSecondaryFormAction($data, $form)
|
||||||
//
|
{
|
||||||
}
|
//
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The first `$action` argument for creating a `FormAction` is the name of the method to invoke when submitting the form
|
The first `$action` argument for creating a `FormAction` is the name of the method to invoke when submitting the form
|
||||||
with the particular button. In the previous example, clicking the 'Another Button' would invoke the
|
with the particular button. In the previous example, clicking the 'Another Button' would invoke the
|
||||||
@ -252,45 +275,55 @@ The `$action` method takes two arguments:
|
|||||||
* `$data` an array containing the values of the form mapped from `$name => $value`
|
* `$data` an array containing the values of the form mapped from `$name => $value`
|
||||||
* `$form` the submitted [api:Form] instance.
|
* `$form` the submitted [api:Form] instance.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
use SilverStripe\Forms\EmailField;
|
||||||
|
use SilverStripe\Forms\FieldList;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\FormAction;
|
||||||
|
use SilverStripe\Forms\TextField;
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
class PageController extends ContentController
|
||||||
'MyForm'
|
{
|
||||||
);
|
private static $allowed_actions = array(
|
||||||
|
'MyForm'
|
||||||
|
);
|
||||||
|
|
||||||
public function MyForm() {
|
public function MyForm()
|
||||||
$fields = new FieldList(
|
{
|
||||||
TextField::create('Name'),
|
$fields = new FieldList(
|
||||||
EmailField::create('Email')
|
TextField::create('Name'),
|
||||||
);
|
EmailField::create('Email')
|
||||||
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSubmitForm', 'Submit')
|
FormAction::create('doSubmitForm', 'Submit')
|
||||||
);
|
);
|
||||||
|
|
||||||
$form = new Form($controller, 'MyForm', $fields, $actions);
|
$form = new Form($controller, 'MyForm', $fields, $actions);
|
||||||
|
|
||||||
return $form
|
return $form
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSubmitForm($data, $form) {
|
public function doSubmitForm($data, $form)
|
||||||
// Submitted data is available as a map.
|
{
|
||||||
echo $data['Name'];
|
// Submitted data is available as a map.
|
||||||
echo $data['Email'];
|
echo $data['Name'];
|
||||||
|
echo $data['Email'];
|
||||||
|
|
||||||
// You can also fetch the value from the field.
|
// You can also fetch the value from the field.
|
||||||
echo $form->Fields()->dataFieldByName('Email')->Value();
|
echo $form->Fields()->dataFieldByName('Email')->Value();
|
||||||
|
|
||||||
// Using the Form instance you can get / set status such as error messages.
|
// Using the Form instance you can get / set status such as error messages.
|
||||||
$form->sessionMessage("Successful!", 'good');
|
$form->sessionMessage('Successful!', 'good');
|
||||||
|
|
||||||
// After dealing with the data you can redirect the user back.
|
// After dealing with the data you can redirect the user back.
|
||||||
return $this->redirectBack();
|
return $this->redirectBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
@ -300,12 +333,14 @@ validating its' own data value.
|
|||||||
|
|
||||||
For more information, see the [Form Validation](validation) documentation.
|
For more information, see the [Form Validation](validation) documentation.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$validator = new RequiredFields(array(
|
$validator = new RequiredFields(array(
|
||||||
'Name', 'Email'
|
'Name',
|
||||||
));
|
'Email'
|
||||||
|
));
|
||||||
|
|
||||||
$form = new Form($this, 'MyForm', $fields, $actions, $validator);
|
$form = new Form($this, 'MyForm', $fields, $actions, $validator);
|
||||||
|
```
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
|
||||||
|
@ -7,43 +7,53 @@ SilverStripe provides server-side form validation out of the box through the [ap
|
|||||||
[api:RequiredFields]. A single `Validator` instance is set on each `Form`. Validators are implemented as an argument to
|
[api:RequiredFields]. A single `Validator` instance is set on each `Form`. Validators are implemented as an argument to
|
||||||
the [api:Form] constructor or through the function `setValidator`.
|
the [api:Form] constructor or through the function `setValidator`.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
use SilverStripe\Forms\EmailField;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\FormAction;
|
||||||
|
use SilverStripe\Forms\TextField;
|
||||||
|
use SilverStripe\Forms\RequiredFields;
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
class PageController extends ContentController
|
||||||
'MyForm'
|
{
|
||||||
);
|
private static $allowed_actions = array(
|
||||||
|
'MyForm'
|
||||||
|
);
|
||||||
|
|
||||||
public function MyForm() {
|
public function MyForm()
|
||||||
$fields = new FieldList(
|
{
|
||||||
TextField::create('Name'),
|
$fields = new FieldList(
|
||||||
EmailField::create('Email')
|
TextField::create('Name'),
|
||||||
);
|
EmailField::create('Email')
|
||||||
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSubmitForm', 'Submit')
|
FormAction::create('doSubmitForm', 'Submit')
|
||||||
);
|
);
|
||||||
|
|
||||||
// the fields 'Name' and 'Email' are required.
|
// the fields 'Name' and 'Email' are required.
|
||||||
$required = new RequiredFields(array(
|
$required = new RequiredFields(array(
|
||||||
'Name', 'Email'
|
'Name', 'Email'
|
||||||
));
|
));
|
||||||
|
|
||||||
// $required can be set as an argument
|
// $required can be set as an argument
|
||||||
$form = new Form($controller, 'MyForm', $fields, $actions, $required);
|
$form = new Form($controller, 'MyForm', $fields, $actions, $required);
|
||||||
|
|
||||||
// Or, through a setter.
|
// Or, through a setter.
|
||||||
$form->setValidator($required);
|
$form->setValidator($required);
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSubmitForm($data, $form) {
|
public function doSubmitForm($data, $form)
|
||||||
//..
|
{
|
||||||
}
|
//..
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In this example we will be required to input a value for `Name` and a valid email address for `Email` before the
|
In this example we will be required to input a value for `Name` and a valid email address for `Email` before the
|
||||||
`doSubmitForm` method is called.
|
`doSubmitForm` method is called.
|
||||||
@ -63,15 +73,17 @@ The data value of the `FormField` submitted is not passed into validate. It is s
|
|||||||
the `setValue` method.
|
the `setValue` method.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
public function validate($validator) {
|
public function validate($validator)
|
||||||
if($this->Value() == 10) {
|
{
|
||||||
$validator->validationError($this->Name(), 'This value cannot be 10');
|
if ((int) $this->Value() === 10) {
|
||||||
return false;
|
$validator->validationError($this->Name(), 'This value cannot be 10');
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The `validate` method should return `true` if the value passes any validation and `false` if SilverStripe should trigger
|
The `validate` method should return `true` if the value passes any validation and `false` if SilverStripe should trigger
|
||||||
a validation error on the page. In addition a useful error message must be set on the given validator.
|
a validation error on the page. In addition a useful error message must be set on the given validator.
|
||||||
@ -86,80 +98,94 @@ two ways to go about this:
|
|||||||
A custom `FormField` which handles the validation. This means the `FormField` can be reused throughout the site and have
|
A custom `FormField` which handles the validation. This means the `FormField` can be reused throughout the site and have
|
||||||
the same validation logic applied to it throughout.
|
the same validation logic applied to it throughout.
|
||||||
|
|
||||||
**mysite/code/formfields/CustomNumberField.php**
|
**mysite/code/CustomNumberField.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class CustomNumberField extends TextField {
|
use SilverStripe\Forms\TextField;
|
||||||
|
|
||||||
public function validate($validator) {
|
class CustomNumberField extends TextField
|
||||||
if(!is_numeric($this->value)) {
|
{
|
||||||
$validator->validationError(
|
public function validate($validator)
|
||||||
$this->name, "Not a number. This must be between 2 and 5", "validation", false
|
{
|
||||||
);
|
if (!is_numeric($this->value)) {
|
||||||
|
$validator->validationError(
|
||||||
return false;
|
$this->name, 'Not a number. This must be between 2 and 5', 'validation', false
|
||||||
}
|
);
|
||||||
else if($this->value > 5 || $this->value < 2) {
|
|
||||||
$validator->validationError(
|
return false;
|
||||||
$this->name, "Your number must be between 2 and 5", "validation", false
|
} elseif ($this->value > 5 || $this->value < 2) {
|
||||||
);
|
$validator->validationError(
|
||||||
|
$this->name, 'Your number must be between 2 and 5', 'validation', false
|
||||||
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Or, an alternative approach to the custom class is to define the behavior inside the Form's action method. This is less
|
Or, an alternative approach to the custom class is to define the behavior inside the Form's action method. This is less
|
||||||
reusable and would not be possible within the `CMS` or other automated `UI` but does not rely on creating custom
|
reusable and would not be possible within the `CMS` or other automated `UI` but does not rely on creating custom
|
||||||
`FormField` classes.
|
`FormField` classes.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
use SilverStripe\Forms\EmailField;
|
||||||
|
use SilverStripe\Forms\FieldList;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\FormAction;
|
||||||
|
use SilverStripe\Forms\TextField;
|
||||||
|
use SilverStripe\Security\Member;
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
class Page_Controller extends ContentController
|
||||||
'MyForm'
|
{
|
||||||
);
|
private static $allowed_actions = array(
|
||||||
|
'MyForm'
|
||||||
|
);
|
||||||
|
|
||||||
public function MyForm() {
|
public function MyForm()
|
||||||
$fields = new FieldList(
|
{
|
||||||
TextField::create('Name'),
|
$fields = new FieldList(
|
||||||
EmailField::create('Email')
|
TextField::create('Name'),
|
||||||
);
|
EmailField::create('Email')
|
||||||
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSubmitForm', 'Submit')
|
FormAction::create('doSubmitForm', 'Submit')
|
||||||
);
|
);
|
||||||
|
|
||||||
$form = new Form($controller, 'MyForm', $fields, $actions);
|
$form = new Form($controller, 'MyForm', $fields, $actions);
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSubmitForm($data, $form) {
|
public function doSubmitForm($data, $form)
|
||||||
// At this point, RequiredFields->isValid() will have been called already,
|
{
|
||||||
// so we can assume that the values exist. Say we want to make sure that email hasn't already been used.
|
// At this point, RequiredFields->isValid() will have been called already,
|
||||||
|
// so we can assume that the values exist. Say we want to make sure that email hasn't already been used.
|
||||||
$check = Member::get()->filter('Email', $data['Email'])->first();
|
|
||||||
|
$check = Member::get()->filter('Email', $data['Email'])->first();
|
||||||
|
|
||||||
if($check) {
|
if ($check) {
|
||||||
$form->addErrorMessage('Email', 'This email already exists', 'bad');
|
$form->addErrorMessage('Email', 'This email already exists', 'bad');
|
||||||
|
|
||||||
return $this->redirectBack();
|
return $this->redirectBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$form->sessionMessage("You have been added to our mailing list", 'good');
|
$form->sessionMessage('You have been added to our mailing list', 'good');
|
||||||
|
|
||||||
return $this->redirectBack();
|
return $this->redirectBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Exempt validation actions
|
## Exempt validation actions
|
||||||
|
|
||||||
In some cases you might need to disable validation for specific actions. E.g. actions which discard submitted
|
In some cases you might need to disable validation for specific actions. E.g. actions which discard submitted
|
||||||
@ -167,32 +193,32 @@ data may not need to check the validity of the posted content.
|
|||||||
|
|
||||||
You can disable validation on individual using one of two methods:
|
You can disable validation on individual using one of two methods:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$actions = new FieldList(
|
||||||
|
$action = FormAction::create('doSubmitForm', 'Submit')
|
||||||
|
);
|
||||||
|
$form = new Form($controller, 'MyForm', $fields, $actions);
|
||||||
|
|
||||||
:::php
|
// Disable actions on the form action themselves
|
||||||
$actions = new FieldList(
|
$action->setValidationExempt(true);
|
||||||
$action = FormAction::create('doSubmitForm', 'Submit')
|
|
||||||
);
|
|
||||||
$form = new Form($controller, 'MyForm', $fields, $actions);
|
|
||||||
|
|
||||||
// Disable actions on the form action themselves
|
|
||||||
$action->setValidationExempt(true);
|
|
||||||
|
|
||||||
// Alternatively, you can whitelist individual actions on the form object by name
|
|
||||||
$form->setValidationExemptActions(['doSubmitForm']);
|
|
||||||
|
|
||||||
|
// Alternatively, you can whitelist individual actions on the form object by name
|
||||||
|
$form->setValidationExemptActions(['doSubmitForm']);
|
||||||
|
```
|
||||||
|
|
||||||
## Server-side validation messages
|
## Server-side validation messages
|
||||||
|
|
||||||
If a `FormField` fails to pass `validate()` the default error message is returned.
|
If a `FormField` fails to pass `validate()` the default error message is returned.
|
||||||
|
|
||||||
:::php
|
```
|
||||||
'$Name' is required
|
'$Name' is required
|
||||||
|
```
|
||||||
|
|
||||||
Use `setCustomValidationMessage` to provide a custom message.
|
Use `setCustomValidationMessage` to provide a custom message.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$field = new TextField(..);
|
$field = new TextField(/* .. */);
|
||||||
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
|
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
|
||||||
|
|
||||||
## JavaScript validation
|
## JavaScript validation
|
||||||
|
|
||||||
@ -201,15 +227,15 @@ to provide the information required in order to plug in custom libraries like [P
|
|||||||
[jQuery.Validate](http://jqueryvalidation.org/). Most of these libraries work on HTML `data-` attributes or special
|
[jQuery.Validate](http://jqueryvalidation.org/). Most of these libraries work on HTML `data-` attributes or special
|
||||||
classes added to each input. For Parsley we can structure the form like.
|
classes added to each input. For Parsley we can structure the form like.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(/* .. */);
|
||||||
$form->setAttribute('data-parsley-validate', true);
|
$form->setAttribute('data-parsley-validate', true);
|
||||||
|
|
||||||
$field = $fields->dataFieldByName('Name');
|
$field = $fields->dataFieldByName('Name');
|
||||||
|
|
||||||
$field->setAttribute('required', true);
|
|
||||||
$field->setAttribute('data-parsley-mincheck', '2');
|
|
||||||
|
|
||||||
|
$field->setAttribute('required', true);
|
||||||
|
$field->setAttribute('data-parsley-mincheck', '2');
|
||||||
|
```
|
||||||
|
|
||||||
## Model Validation
|
## Model Validation
|
||||||
|
|
||||||
@ -228,23 +254,26 @@ error message, or a [api:ValidationResult] object containing the list of errors
|
|||||||
|
|
||||||
E.g.
|
E.g.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\ORM\ValidationException;
|
||||||
|
|
||||||
:::php
|
class MyController extends Controller
|
||||||
class MyController extends Controller
|
{
|
||||||
|
public function doSave($data, $form)
|
||||||
{
|
{
|
||||||
public function doSave($data, $form) {
|
$success = $this->sendEmail($data);
|
||||||
$success = $this->sendEmail($data);
|
|
||||||
|
// Example error handling
|
||||||
// Example error handling
|
if (!$success) {
|
||||||
if (!$success) {
|
throw new ValidationException('Sorry, we could not email to that address');
|
||||||
throw new ValidationException('Sorry, we could not email to that address');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If success
|
|
||||||
return $this->redirect($this->Link('success'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If success
|
||||||
|
return $this->redirect($this->Link('success'));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Validation in the CMS
|
### Validation in the CMS
|
||||||
|
|
||||||
@ -257,28 +286,36 @@ respect the provided `Validator` and handle displaying error and success respons
|
|||||||
Again, custom error messages can be provided through the `FormField`
|
Again, custom error messages can be provided through the `FormField`
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page extends SiteTree {
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Forms\TextField;
|
||||||
|
use SilverStripe\Forms\RequiredFields;
|
||||||
|
|
||||||
private static $db = array(
|
class Page extends SiteTree
|
||||||
'MyRequiredField' => 'Text'
|
{
|
||||||
);
|
private static $db = array(
|
||||||
|
'MyRequiredField' => 'Text'
|
||||||
|
);
|
||||||
|
|
||||||
public function getCMSFields() {
|
public function getCMSFields()
|
||||||
$fields = parent::getCMSFields();
|
{
|
||||||
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$fields->addFieldToTab('Root.Main',
|
$fields->addFieldToTab('Root.Main',
|
||||||
TextField::create('MyRequiredField')->setCustomValidationMessage('You missed me.')
|
TextField::create('MyRequiredField')->setCustomValidationMessage('You missed me.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCMSValidator() {
|
public function getCMSValidator()
|
||||||
return new RequiredFields(array(
|
{
|
||||||
'MyRequiredField'
|
return new RequiredFields(array(
|
||||||
));
|
'MyRequiredField'
|
||||||
}
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
|
||||||
|
@ -431,7 +431,7 @@ code could be used:
|
|||||||
:::php
|
:::php
|
||||||
class GalleryPage extends Page {}
|
class GalleryPage extends Page {}
|
||||||
|
|
||||||
class GalleryPage_Controller extends Page_Controller {
|
class GalleryPageController extends PageController {
|
||||||
private static $allowed_actions = array('Form');
|
private static $allowed_actions = array('Form');
|
||||||
public function Form() {
|
public function Form() {
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
|
@ -12,7 +12,7 @@ code for a `Form` is to create it as a subclass to `Form`. Let's look at a examp
|
|||||||
:::php
|
:::php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
|
|
||||||
public function SearchForm() {
|
public function SearchForm() {
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
@ -128,7 +128,7 @@ Our controller will now just have to create a new instance of this form object.
|
|||||||
:::php
|
:::php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
'SearchForm',
|
'SearchForm',
|
||||||
|
@ -8,7 +8,7 @@ Let's start by defining a new `ContactPage` page type:
|
|||||||
<?php
|
<?php
|
||||||
class ContactPage extends Page {
|
class ContactPage extends Page {
|
||||||
}
|
}
|
||||||
class ContactPage_Controller extends Page_Controller {
|
class ContactPageController extends PageController {
|
||||||
private static $allowed_actions = array('Form');
|
private static $allowed_actions = array('Form');
|
||||||
public function Form() {
|
public function Form() {
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
@ -61,7 +61,7 @@ If you now create a ContactPage in the CMS (making sure you have rebuilt the dat
|
|||||||
Now that we have a contact form, we need some way of collecting the data submitted. We do this by creating a function on the controller with the same name as the form action. In this case, we create the function 'submit' on the ContactPage_Controller class.
|
Now that we have a contact form, we need some way of collecting the data submitted. We do this by creating a function on the controller with the same name as the form action. In this case, we create the function 'submit' on the ContactPage_Controller class.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class ContactPage_Controller extends Page_Controller {
|
class ContactPageController extends PageController {
|
||||||
private static $allowed_actions = array('Form');
|
private static $allowed_actions = array('Form');
|
||||||
public function Form() {
|
public function Form() {
|
||||||
// ...
|
// ...
|
||||||
|
@ -83,7 +83,7 @@ If your caching logic is complex or re-usable, you can define a method on your c
|
|||||||
fragment.
|
fragment.
|
||||||
|
|
||||||
For example, a block that shows a collection of rotating slides needs to update whenever the relationship
|
For example, a block that shows a collection of rotating slides needs to update whenever the relationship
|
||||||
`Page::$many_many = array('Slides' => 'Slide')` changes. In Page_Controller:
|
`Page::$many_many = array('Slides' => 'Slide')` changes. In `PageController`:
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ heavy load:
|
|||||||
<% cached 'blogstatistics', $Blog.ID if $HighLoad %>
|
<% cached 'blogstatistics', $Blog.ID if $HighLoad %>
|
||||||
|
|
||||||
|
|
||||||
By adding a `HighLoad` function to your `Page_Controller`, you could enable or disable caching dynamically.
|
By adding a `HighLoad` function to your `PageController`, you could enable or disable caching dynamically.
|
||||||
|
|
||||||
To cache the contents of a page for all anonymous users, but dynamically calculate the contents for logged in members,
|
To cache the contents of a page for all anonymous users, but dynamically calculate the contents for logged in members,
|
||||||
use something like:
|
use something like:
|
||||||
|
@ -14,27 +14,35 @@ The simple usage, Permission::check("PERM_CODE") will detect if the currently lo
|
|||||||
|
|
||||||
**Group ACLs**
|
**Group ACLs**
|
||||||
|
|
||||||
* Call **Permission::check("MY_PERMISSION_CODE")** to see if the current user has MY_PERMISSION_CODE.
|
* Call **Permission::check('MY_PERMISSION_CODE')** to see if the current user has MY_PERMISSION_CODE.
|
||||||
* MY_PERMISSION_CODE can be loaded into the Security admin on the appropriate group, using the "Permissions" tab.
|
* `MY_PERMISSION_CODE` can be loaded into the Security admin on the appropriate group, using the "Permissions" tab.
|
||||||
|
|
||||||
## PermissionProvider
|
## PermissionProvider
|
||||||
|
|
||||||
[api:PermissionProvider] is an interface which lets you define a method *providePermissions()*.
|
[api:PermissionProvider] is an interface which lets you define a method *providePermissions()*.
|
||||||
This method should return a map of permission code names with a human readable explanation of its purpose.
|
This method should return a map of permission code names with a human readable explanation of its purpose.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class Page_Controller implements PermissionProvider {
|
use SilverStripe\Security\PermissionProvider;
|
||||||
public function init() {
|
|
||||||
parent::init();
|
|
||||||
if(!Permission::check("VIEW_SITE")) Security::permissionFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function providePermissions() {
|
class PageController implements PermissionProvider
|
||||||
return array(
|
{
|
||||||
"VIEW_SITE" => "Access the site",
|
public function init()
|
||||||
);
|
{
|
||||||
}
|
parent::init();
|
||||||
}
|
if (!Permission::check('VIEW_SITE')) {
|
||||||
|
Security::permissionFailure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providePermissions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'VIEW_SITE' => 'Access the site'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
This can then be used to add a dropdown for permission codes to the security panel. Permission::get_all_codes() will be
|
This can then be used to add a dropdown for permission codes to the security panel. Permission::get_all_codes() will be
|
||||||
@ -89,10 +97,11 @@ This works much like ADMIN permissions (see above)
|
|||||||
|
|
||||||
You can check if a user has access to the CMS by simply performing a check against `CMS_ACCESS`.
|
You can check if a user has access to the CMS by simply performing a check against `CMS_ACCESS`.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
if (Permission::checkMember($member, 'CMS_ACCESS')) {
|
if (Permission::checkMember($member, 'CMS_ACCESS')) {
|
||||||
//user can access the CMS
|
//user can access the CMS
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Internally, this checks that the user has any of the defined `CMS_ACCESS_*` permissions.
|
Internally, this checks that the user has any of the defined `CMS_ACCESS_*` permissions.
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ You can use [api:RSSFeed] to easily create a feed showing your latest Page updat
|
|||||||
|
|
||||||
..
|
..
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
'rss'
|
'rss'
|
||||||
@ -118,7 +118,7 @@ Then in our controller, we add a new action which returns a the XML list of `Pla
|
|||||||
:::php
|
:::php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
'players'
|
'players'
|
||||||
|
@ -79,9 +79,9 @@ the `$fields` constructor parameter.
|
|||||||
:::php
|
:::php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
..
|
// ..
|
||||||
|
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
|
|
||||||
public function SearchForm() {
|
public function SearchForm() {
|
||||||
$context = singleton('MyDataObject')->getCustomSearchContext();
|
$context = singleton('MyDataObject')->getCustomSearchContext();
|
||||||
|
@ -59,7 +59,7 @@ authorised users, the following should be considered:
|
|||||||
|
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
public function init() {
|
public function init() {
|
||||||
parent::init();
|
parent::init();
|
||||||
// Whitelist any protected files on this page for the current user
|
// Whitelist any protected files on this page for the current user
|
||||||
@ -88,7 +88,7 @@ authorised users, the following should be considered:
|
|||||||
|
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class Page_Controller extends ContentController {
|
class PageController extends ContentController {
|
||||||
public function init() {
|
public function init() {
|
||||||
parent::init();
|
parent::init();
|
||||||
// Whitelist any protected files on this page for the current user
|
// Whitelist any protected files on this page for the current user
|
||||||
|
@ -23,7 +23,7 @@ use SilverStripe\Core\Object;
|
|||||||
* If you want to implement a FileField into a form element, you need to pass it an array of source data.
|
* If you want to implement a FileField into a form element, you need to pass it an array of source data.
|
||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* class ExampleForm_Controller extends Page_Controller {
|
* class ExampleFormController extends PageController {
|
||||||
*
|
*
|
||||||
* function Form() {
|
* function Form() {
|
||||||
* $fields = new FieldList(
|
* $fields = new FieldList(
|
||||||
|
@ -518,7 +518,7 @@ class Security extends Controller implements TemplateGlobalProvider
|
|||||||
$tmpPage = new SiteTree();
|
$tmpPage = new SiteTree();
|
||||||
$tmpPage->Title = $title;
|
$tmpPage->Title = $title;
|
||||||
/** @skipUpgrade */
|
/** @skipUpgrade */
|
||||||
$tmpPage->URLSegment = "Security";
|
$tmpPage->URLSegment = 'Security';
|
||||||
// Disable ID-based caching of the log-in page by making it a random number
|
// Disable ID-based caching of the log-in page by making it a random number
|
||||||
$tmpPage->ID = -1 * rand(1, 10000000);
|
$tmpPage->ID = -1 * rand(1, 10000000);
|
||||||
|
|
||||||
|
@ -253,9 +253,9 @@ class SSViewer implements Flushable
|
|||||||
$templates[] = $template;
|
$templates[] = $template;
|
||||||
$templates[] = ['type' => 'Includes', $template];
|
$templates[] = ['type' => 'Includes', $template];
|
||||||
|
|
||||||
// If the class is "Page_Controller", look for Page.ss
|
// If the class is "PageController" (PSR-2 compatibility) or "Page_Controller" (legacy), look for Page.ss
|
||||||
if (stripos($class, '_controller') !== false) {
|
if (preg_match('/^(?<name>.+[^\\\\])_?Controller$/iU', $class, $matches)) {
|
||||||
$templates[] = str_ireplace('_controller', '', $class) . $suffix;
|
$templates[] = $matches['name'] . $suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($baseClass && $class == $baseClass) {
|
if ($baseClass && $class == $baseClass) {
|
||||||
|
@ -23,7 +23,7 @@ use SilverStripe\View\Requirements_Backend;
|
|||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
use SilverStripe\View\Tests\SSViewerTest\SSViewerTestModel;
|
use SilverStripe\View\Tests\SSViewerTest\SSViewerTestModel;
|
||||||
use SilverStripe\View\Tests\SSViewerTest\SSViewerTestModel_Controller;
|
use SilverStripe\View\Tests\SSViewerTest\SSViewerTestModelController;
|
||||||
use SilverStripe\View\ViewableData;
|
use SilverStripe\View\ViewableData;
|
||||||
use SilverStripe\View\SSViewer_FromString;
|
use SilverStripe\View\SSViewer_FromString;
|
||||||
use SilverStripe\View\SSTemplateParser;
|
use SilverStripe\View\SSTemplateParser;
|
||||||
@ -1526,17 +1526,15 @@ after'
|
|||||||
|
|
||||||
public function testLayout()
|
public function testLayout()
|
||||||
{
|
{
|
||||||
$self = $this;
|
|
||||||
|
|
||||||
$this->useTestTheme(
|
$this->useTestTheme(
|
||||||
__DIR__.'/SSViewerTest',
|
__DIR__.'/SSViewerTest',
|
||||||
'layouttest',
|
'layouttest',
|
||||||
function () use ($self) {
|
function () {
|
||||||
$template = new SSViewer(array('Page'));
|
$template = new SSViewer(array('Page'));
|
||||||
$self->assertEquals("Foo\n\n", $template->process(new ArrayData(array())));
|
$this->assertEquals("Foo\n\n", $template->process(new ArrayData(array())));
|
||||||
|
|
||||||
$template = new SSViewer(array('Shortcodes', 'Page'));
|
$template = new SSViewer(array('Shortcodes', 'Page'));
|
||||||
$self->assertEquals("[file_link]\n\n", $template->process(new ArrayData(array())));
|
$this->assertEquals("[file_link]\n\n", $template->process(new ArrayData(array())));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1546,48 +1544,47 @@ after'
|
|||||||
*/
|
*/
|
||||||
public function testGetTemplatesByClass()
|
public function testGetTemplatesByClass()
|
||||||
{
|
{
|
||||||
$self = $this;
|
|
||||||
$this->useTestTheme(
|
$this->useTestTheme(
|
||||||
__DIR__.'/SSViewerTest',
|
__DIR__ . '/SSViewerTest',
|
||||||
'layouttest',
|
'layouttest',
|
||||||
function () use ($self) {
|
function () {
|
||||||
// Test passing a string
|
// Test passing a string
|
||||||
$templates = SSViewer::get_templates_by_class(
|
$templates = SSViewer::get_templates_by_class(
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
'',
|
'',
|
||||||
Controller::class
|
Controller::class
|
||||||
);
|
);
|
||||||
$self->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
[
|
[
|
||||||
'type' => 'Includes',
|
'type' => 'Includes',
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
],
|
],
|
||||||
SSViewerTestModel::class,
|
SSViewerTestModel::class,
|
||||||
Controller::class,
|
Controller::class,
|
||||||
[
|
[
|
||||||
'type' => 'Includes',
|
'type' => 'Includes',
|
||||||
Controller::class,
|
Controller::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
$templates
|
$templates
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test to ensure we're stopping at the base class.
|
// Test to ensure we're stopping at the base class.
|
||||||
$templates = SSViewer::get_templates_by_class(
|
$templates = SSViewer::get_templates_by_class(
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
'',
|
'',
|
||||||
SSViewerTestModel_Controller::class
|
SSViewerTestModelController::class
|
||||||
);
|
);
|
||||||
$self->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
[
|
[
|
||||||
'type' => 'Includes',
|
'type' => 'Includes',
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
],
|
],
|
||||||
SSViewerTestModel::class,
|
SSViewerTestModel::class,
|
||||||
],
|
],
|
||||||
$templates
|
$templates
|
||||||
);
|
);
|
||||||
@ -1595,27 +1592,27 @@ after'
|
|||||||
// Make sure we can search templates by suffix.
|
// Make sure we can search templates by suffix.
|
||||||
$templates = SSViewer::get_templates_by_class(
|
$templates = SSViewer::get_templates_by_class(
|
||||||
SSViewerTestModel::class,
|
SSViewerTestModel::class,
|
||||||
'_Controller',
|
'Controller',
|
||||||
DataObject::class
|
DataObject::class
|
||||||
);
|
);
|
||||||
$self->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
[
|
[
|
||||||
'type' => 'Includes',
|
'type' => 'Includes',
|
||||||
SSViewerTestModel_Controller::class,
|
SSViewerTestModelController::class,
|
||||||
],
|
],
|
||||||
DataObject::class.'_Controller',
|
DataObject::class . 'Controller',
|
||||||
[
|
[
|
||||||
'type' => 'Includes',
|
'type' => 'Includes',
|
||||||
DataObject::class.'_Controller',
|
DataObject::class . 'Controller',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
$templates
|
$templates
|
||||||
);
|
);
|
||||||
|
|
||||||
// Let's throw something random in there.
|
// Let's throw something random in there.
|
||||||
$self->setExpectedException('InvalidArgumentException');
|
$this->setExpectedException('InvalidArgumentException');
|
||||||
SSViewer::get_templates_by_class(array());
|
SSViewer::get_templates_by_class(array());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ namespace SilverStripe\View\Tests\SSViewerTest;
|
|||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class SSViewerTestModel_Controller extends Controller implements TestOnly
|
class SSViewerTestModelController extends Controller implements TestOnly
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user