mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
DOCS Update docs to reference PageController
without an underscore, implement some PSR-2
This commit is contained in:
parent
a996e20e79
commit
c620063608
@ -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:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class HomePage extends Page {
|
||||
}
|
||||
class HomePage_Controller extends Page_Controller {
|
||||
}
|
||||
```php
|
||||
<?php
|
||||
|
||||
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.
|
||||
We can do this by going to `http://localhost/your_site_name/dev/build`.
|
||||
|
@ -18,17 +18,26 @@ We will create a poll on the home page that asks the user their favourite web br
|
||||
|
||||
## 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
|
||||
class HomePage_Controller extends Page_Controller {
|
||||
```php
|
||||
use PageController;
|
||||
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() {
|
||||
public function BrowserPollForm()
|
||||
{
|
||||
// Create fields
|
||||
$fields = new FieldList(
|
||||
new TextField('Name'),
|
||||
@ -50,17 +59,16 @@ The poll we will be creating on our homepage will ask the user for their name an
|
||||
return new Form($this, 'BrowserPollForm', $fields, $actions);
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
...
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
Let's step through this code.
|
||||
|
||||
```php
|
||||
// Create fields
|
||||
$fields = new FieldList(
|
||||
// Create fields
|
||||
$fields = new FieldList(
|
||||
new TextField('Name'),
|
||||
new OptionsetField('Browser', 'Your Favourite Browser', array(
|
||||
'Firefox' => 'Firefox',
|
||||
@ -70,7 +78,7 @@ Let's step through this code.
|
||||
'Opera' => 'Opera',
|
||||
'Lynx' => 'Lynx'
|
||||
))
|
||||
);
|
||||
);
|
||||
```
|
||||
|
||||
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
|
||||
array mapping the values to the options listed in the dropdown.
|
||||
|
||||
```php
|
||||
$actions = new FieldList(
|
||||
```php
|
||||
$actions = new FieldList(
|
||||
new FormAction('doBrowserPoll', 'Submit');
|
||||
);
|
||||
);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```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.
|
||||
@ -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**
|
||||
|
||||
```ss
|
||||
...
|
||||
<div id="BrowserPoll">
|
||||
```ss
|
||||
...
|
||||
<div id="BrowserPoll">
|
||||
<h2>Browser Poll</h2>
|
||||
$BrowserPollForm
|
||||
</div>
|
||||
<div class="Content">
|
||||
...
|
||||
</div>
|
||||
<div class="Content">
|
||||
...
|
||||
```
|
||||
|
||||
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**
|
||||
|
||||
```css
|
||||
/* BROWSER POLL */
|
||||
#BrowserPoll {
|
||||
```css
|
||||
/* BROWSER POLL */
|
||||
#BrowserPoll {
|
||||
float: right;
|
||||
margin: 20px 10px 0 0;
|
||||
width: 20%;
|
||||
}
|
||||
form FieldList {
|
||||
border:0;
|
||||
}
|
||||
}
|
||||
form FieldList {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#BrowserPoll .message {
|
||||
float:left;
|
||||
#BrowserPoll .message {
|
||||
float: left;
|
||||
display: block;
|
||||
color:red;
|
||||
background:#efefef;
|
||||
border:1px solid #ccc;
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
color: red;
|
||||
background: #efefef;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#BrowserPoll h2 {
|
||||
#BrowserPoll h2 {
|
||||
font-size: 1.5em;
|
||||
line-height:2em;
|
||||
color: #0083C8;
|
||||
}
|
||||
}
|
||||
|
||||
#BrowserPoll .field {
|
||||
#BrowserPoll .field {
|
||||
padding:3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
#BrowserPoll input.text {
|
||||
#BrowserPoll input.text {
|
||||
padding: 0;
|
||||
font-size:1em;
|
||||
}
|
||||
}
|
||||
|
||||
#BrowserPoll .btn-toolbar {
|
||||
padding:5px 0;
|
||||
}
|
||||
#BrowserPoll .btn-toolbar {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
#BrowserPoll .bar {
|
||||
#BrowserPoll .bar {
|
||||
background-color: #015581;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -183,21 +191,29 @@ If you recall, in the [second tutorial](/tutorials/extending_a_basic_site) we sa
|
||||
|
||||
**mysite/code/BrowserPollSubmission.php**
|
||||
|
||||
```php
|
||||
<?php
|
||||
class BrowserPollSubmission extends DataObject {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
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
|
||||
class HomePage_Controller extends Page_Controller {
|
||||
```php
|
||||
use BrowserPollSubmission;
|
||||
use PageController;
|
||||
|
||||
class HomePageController extends PageController
|
||||
{
|
||||
// ...
|
||||
public function doBrowserPoll($data, $form) {
|
||||
$submission = new BrowserPollSubmission();
|
||||
@ -205,7 +221,7 @@ If we then rebuild the database ([http://localhost/your_site_name/dev/build](htt
|
||||
$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.
|
||||
@ -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.
|
||||
|
||||
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**
|
||||
|
||||
```php
|
||||
public function BrowserPollForm() {
|
||||
```php
|
||||
public function BrowserPollForm()
|
||||
{
|
||||
// ...
|
||||
$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.
|
||||
@ -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.
|
||||
|
||||
**mysite/code/HomePage.php**
|
||||
**mysite/code/HomePageController.php**
|
||||
|
||||
```php
|
||||
```php
|
||||
// ...
|
||||
class HomePageController extends PageController
|
||||
{
|
||||
// ...
|
||||
class HomePage_Controller extends Page_Controller {
|
||||
// ...
|
||||
public function doBrowserPoll($data, $form) {
|
||||
public function doBrowserPoll($data, $form)
|
||||
{
|
||||
$submission = new BrowserPollSubmission();
|
||||
$form->saveInto($submission);
|
||||
$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
|
||||
it is.
|
||||
|
||||
```php
|
||||
```php
|
||||
// ...
|
||||
class HomePageController extends PageController
|
||||
{
|
||||
// ...
|
||||
class HomePage_Controller extends Page_Controller {
|
||||
// ...
|
||||
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,
|
||||
@ -281,12 +304,13 @@ 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.
|
||||
|
||||
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
|
||||
public function BrowserPollResults() {
|
||||
```php
|
||||
public function BrowserPollResults()
|
||||
{
|
||||
$submissions = new GroupedList(BrowserPollSubmission::get());
|
||||
$total = $submissions->Count();
|
||||
|
||||
@ -299,29 +323,30 @@ Create the function 'BrowserPollResults' on the *HomePage_Controller* class.
|
||||
)));
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
```
|
||||
This code introduces a few new concepts, so let's step through it.
|
||||
|
||||
```php
|
||||
$submissions = new GroupedList(BrowserPollSubmission::get());
|
||||
```php
|
||||
$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).
|
||||
|
||||
```php
|
||||
$total = $submissions->Count();
|
||||
$total = $submissions->Count();
|
||||
```
|
||||
We get the total number of submissions, which is needed to calculate the percentages.
|
||||
|
||||
```php
|
||||
$list = new ArrayList();
|
||||
foreach($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
|
||||
```php
|
||||
$list = new ArrayList();
|
||||
foreach ($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
|
||||
$percentage = (int) ($browserSubmissions->Count() / $total * 100);
|
||||
$list->push(new ArrayData(array(
|
||||
'Browser' => $browserName,
|
||||
'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.
|
||||
@ -334,7 +359,7 @@ The final step is to create the template to display our data. Change the 'Browse
|
||||
**themes/simple/templates/Layout/HomePage.ss**
|
||||
|
||||
```ss
|
||||
<div id="BrowserPoll">
|
||||
<div id="BrowserPoll">
|
||||
<h2>Browser Poll</h2>
|
||||
<% if $BrowserPollForm %>
|
||||
$BrowserPollForm
|
||||
@ -348,7 +373,7 @@ The final step is to create the template to display our data. Change the 'Browse
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
<% 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,
|
||||
|
@ -38,9 +38,13 @@ Let's create the `Student` and `Project` objects.
|
||||
|
||||
**mysite/code/Student.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class Student extends DataObject {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class Student extends DataObject
|
||||
{
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar',
|
||||
'University' => 'Varchar',
|
||||
@ -48,19 +52,35 @@ Let's create the `Student` and `Project` objects.
|
||||
private static $has_one = array(
|
||||
'Project' => 'Project'
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**mysite/code/Project.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class Project extends Page {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Page;
|
||||
|
||||
class Project extends Page
|
||||
{
|
||||
private static $has_many = array(
|
||||
'Students' => 'Student'
|
||||
);
|
||||
}
|
||||
class Project_Controller extends Page_Controller {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**mysite/code/ProjectController.php**
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PageController;
|
||||
|
||||
class ProjectController extends PageController
|
||||
{
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The relationships are defined through the `$has_one`
|
||||
and `$has_many` properties on the objects.
|
||||
@ -95,15 +115,30 @@ The restriction is enforced through the `$allowed_children` directive.
|
||||
|
||||
**mysite/code/ProjectsHolder.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class ProjectsHolder extends Page {
|
||||
:::php
|
||||
<?php
|
||||
|
||||
use Page;
|
||||
|
||||
class ProjectsHolder extends Page {
|
||||
private static $allowed_children = array(
|
||||
'Project'
|
||||
);
|
||||
}
|
||||
class ProjectsHolder_Controller extends Page_Controller {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**mysite/code/ProjectsHolderController.php
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use PageController;
|
||||
|
||||
class ProjectsHolderController extends PageController
|
||||
{
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
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,
|
||||
@ -128,17 +163,26 @@ All customization to fields for a page type are managed through a method called
|
||||
|
||||
**mysite/code/Project.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class Project extends Page {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Forms\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||
|
||||
class Project extends Page
|
||||
{
|
||||
// ...
|
||||
public function getCMSFields() {
|
||||
public function getCMSFields()
|
||||
{
|
||||
// Get the fields from the parent implementation
|
||||
$fields = parent::getCMSFields();
|
||||
// Create a default configuration for the new GridField, allowing record editing
|
||||
$config = GridFieldConfig_RelationEditor::create();
|
||||
// Set the names and data for our gridfield columns
|
||||
$config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
|
||||
$config
|
||||
->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns')
|
||||
->setDisplayFields(array(
|
||||
'Name' => 'Name',
|
||||
'Project.Title'=> 'Project' // Retrieve from a has-one relationship
|
||||
));
|
||||
@ -153,7 +197,8 @@ All customization to fields for a page type are managed through a method called
|
||||
$fields->addFieldToTab('Root.Students', $studentsField);
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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,
|
||||
@ -200,26 +245,37 @@ The first step is to create the `Mentor` object and set the relation with the `P
|
||||
|
||||
**mysite/code/Mentor.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class Mentor extends DataObject {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class Mentor extends DataObject
|
||||
{
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar',
|
||||
);
|
||||
private static $belongs_many_many = array(
|
||||
'Projects' => 'Project'
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**mysite/code/Project.php**
|
||||
|
||||
:::php
|
||||
class Project extends Page {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Page;
|
||||
|
||||
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"
|
||||
(after you've performed a `dev/build` command, of course).
|
||||
@ -231,10 +287,18 @@ to configure it a bit differently.
|
||||
|
||||
**mysite/code/Project.php**
|
||||
|
||||
:::php
|
||||
class Project extends Page {
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Forms\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||
|
||||
class Project extends Page
|
||||
{
|
||||
// ...
|
||||
public function getCMSFields() {
|
||||
public function getCMSFields()
|
||||
{
|
||||
// ...
|
||||
// Same setup, but for mentors
|
||||
$mentorsField = new GridField(
|
||||
@ -246,7 +310,8 @@ to configure it a bit differently.
|
||||
$fields->addFieldToTab('Root.Mentors', $mentorsField);
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The important difference to our student management UI is the usage
|
||||
of `$this->Mentor()` (rather than `Mentor::get()`). It will limit
|
||||
@ -286,9 +351,9 @@ a named list of object.
|
||||
|
||||
**themes/simple/templates/Layout/ProjectsHolder.ss**
|
||||
|
||||
:::ss
|
||||
<% include SideBar %>
|
||||
<div class="content-container unit size3of4 lastUnit">
|
||||
```ss
|
||||
<% include SideBar %>
|
||||
<div class="content-container unit size3of4 lastUnit">
|
||||
<article>
|
||||
<h1>$Title</h1>
|
||||
<div class="content">
|
||||
@ -309,12 +374,12 @@ a named list of object.
|
||||
</td>
|
||||
<td>
|
||||
<% loop $Students %>
|
||||
$Name ($University)<% if $Last !=1 %>,<% end_if %>
|
||||
$Name ($University)<% if not $Last %>, <% end_if %>
|
||||
<% end_loop %>
|
||||
</td>
|
||||
<td>
|
||||
<% loop $Mentors %>
|
||||
$Name<% if $Last !=1 %>,<% end_if %>
|
||||
$Name<% if not $Last %>, <% end_if %>
|
||||
<% end_loop %>
|
||||
</td>
|
||||
</tr>
|
||||
@ -323,7 +388,8 @@ a named list of object.
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
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.
|
||||
@ -344,9 +410,9 @@ we can access the "Students" and "Mentors" relationships directly in the templat
|
||||
|
||||
**themes/simple/templates/Layout/Project.ss**
|
||||
|
||||
:::ss
|
||||
<% include SideBar %>
|
||||
<div class="content-container unit size3of4 lastUnit">
|
||||
```ss
|
||||
<% include SideBar %>
|
||||
<div class="content-container unit size3of4 lastUnit">
|
||||
<article>
|
||||
<h1>$Title</h1>
|
||||
<div class="content">
|
||||
@ -373,7 +439,8 @@ we can access the "Students" and "Mentors" relationships directly in the templat
|
||||
<% end_if %>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Follow the link to a project detail from from your holder page,
|
||||
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**
|
||||
|
||||
:::ss
|
||||
$Name ($University)
|
||||
```ss
|
||||
$Name ($University)
|
||||
```
|
||||
|
||||
To use this template, we need to add a new method to our student class:
|
||||
|
||||
:::php
|
||||
class Student extends DataObject {
|
||||
function getInfo() {
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class Student extends DataObject
|
||||
{
|
||||
public function getInfo()
|
||||
{
|
||||
return $this->renderWith('StudentInfo');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace the student template code in both `Project.ss`
|
||||
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**
|
||||
|
||||
:::php
|
||||
public function UsersIpAddress() {
|
||||
public function UsersIpAddress()
|
||||
{
|
||||
return $this->getRequest()->getIP();
|
||||
}
|
||||
|
||||
@ -112,8 +113,8 @@ Variables can come from your database fields, or custom methods you define on yo
|
||||
</div>
|
||||
|
||||
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]
|
||||
class. This object gives you access to all the database fields on [api:Page_Controller], its corresponding [api:Page]
|
||||
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:PageController], its corresponding [api:Page]
|
||||
record and any subclasses of those two.
|
||||
|
||||
**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 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
|
||||
|
||||
$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
|
||||
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">
|
||||
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
|
||||
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.
|
||||
</div>
|
||||
|
||||
|
@ -12,65 +12,74 @@ The following will render the given data into a template. Given the template:
|
||||
|
||||
**mysite/templates/Coach_Message.ss**
|
||||
|
||||
:::ss
|
||||
<strong>$Name</strong> is the $Role on our team.
|
||||
```ss
|
||||
<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]
|
||||
instance with a template name or an array of templates to render.
|
||||
|
||||
**mysite/code/Page.php**
|
||||
|
||||
:::php
|
||||
$arrayData = new ArrayData(array(
|
||||
```php
|
||||
$arrayData = new ArrayData(array(
|
||||
'Name' => 'John',
|
||||
'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">
|
||||
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.
|
||||
</div>
|
||||
|
||||
:::php
|
||||
$controller->renderWith(array("MyController", "MyBaseController"));
|
||||
```php
|
||||
$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
|
||||
template.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
private static $allowed_actions = array('iwantmyajax');
|
||||
|
||||
public function iwantmyajax() {
|
||||
if(Director::is_ajax()) {
|
||||
return $this->renderWith("AjaxTemplate");
|
||||
public function iwantmyajax()
|
||||
{
|
||||
if (Director::is_ajax()) {
|
||||
return $this->renderWith('AjaxTemplate');
|
||||
} 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
|
||||
does, such as `ArrayData` or `ArrayList`.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
..
|
||||
|
||||
public function iwantmyajax() {
|
||||
if(Director::is_ajax()) {
|
||||
class PageController extends ContentController
|
||||
{
|
||||
// ..
|
||||
public function iwantmyajax()
|
||||
{
|
||||
if (Director::is_ajax()) {
|
||||
$experience = new ArrayList();
|
||||
$experience->push(new ArrayData(array(
|
||||
'Title' => 'First Job'
|
||||
@ -80,10 +89,10 @@ does, such as `ArrayData` or `ArrayList`.
|
||||
'Name' => 'John',
|
||||
'Role' => 'Head Coach',
|
||||
'Experience' => $experience
|
||||
)))->renderWith("AjaxTemplate");
|
||||
)))->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">
|
||||
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.
|
||||
</div>
|
||||
|
||||
|
@ -4,11 +4,11 @@ summary: A more in depth look at how to map requests to particular controllers a
|
||||
# Routing
|
||||
|
||||
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">
|
||||
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.
|
||||
</div>
|
||||
|
||||
@ -17,16 +17,17 @@ These routes by standard, go into a `routes.yml` file in your applications `_con
|
||||
|
||||
**mysite/_config/routes.yml**
|
||||
|
||||
:::yml
|
||||
---
|
||||
Name: mysiteroutes
|
||||
After: framework/routes#coreroutes
|
||||
---
|
||||
Director:
|
||||
```yml
|
||||
---
|
||||
Name: mysiteroutes
|
||||
After: framework/routes#coreroutes
|
||||
---
|
||||
Director:
|
||||
rules:
|
||||
'teams//$Action/$ID/$Name': 'TeamController'
|
||||
'player/': 'PlayerController'
|
||||
'': 'HomeController'
|
||||
```
|
||||
|
||||
<div class="notice" markdown="1">
|
||||
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
|
||||
|
||||
:::yml
|
||||
'teams//$Action/$ID/$Name': 'TeamController'
|
||||
```yml
|
||||
'teams//$Action/$ID/$Name': 'TeamController'
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
:::php
|
||||
// GET /teams/
|
||||
```php
|
||||
// GET /teams/
|
||||
|
||||
print_r($this->getRequest()->params());
|
||||
print_r($this->getRequest()->params());
|
||||
|
||||
// Array
|
||||
// (
|
||||
// [Action] => null
|
||||
// [ID] => null
|
||||
// [Name] => null
|
||||
// )
|
||||
// Array
|
||||
// (
|
||||
// [Action] => null
|
||||
// [ID] => null
|
||||
// [Name] => null
|
||||
// )
|
||||
|
||||
// GET /teams/players/
|
||||
// GET /teams/players/
|
||||
|
||||
print_r($this->getRequest()->params());
|
||||
print_r($this->getRequest()->params());
|
||||
|
||||
// Array
|
||||
// (
|
||||
// [Action] => 'players'
|
||||
// [ID] => null
|
||||
// [Name] => null
|
||||
// )
|
||||
// Array
|
||||
// (
|
||||
// [Action] => 'players'
|
||||
// [ID] => null
|
||||
// [Name] => null
|
||||
// )
|
||||
|
||||
// GET /teams/players/1
|
||||
// GET /teams/players/1
|
||||
|
||||
print_r($this->getRequest()->params());
|
||||
print_r($this->getRequest()->params());
|
||||
|
||||
// Array
|
||||
// (
|
||||
// [Action] => 'players'
|
||||
// [ID] => 1
|
||||
// [Name] => null
|
||||
// )
|
||||
// Array
|
||||
// (
|
||||
// [Action] => 'players'
|
||||
// [ID] => 1
|
||||
// [Name] => null
|
||||
// )
|
||||
```
|
||||
|
||||
You can also fetch one parameter at a time.
|
||||
|
||||
:::php
|
||||
|
||||
// GET /teams/players/1/
|
||||
|
||||
echo $this->getRequest()->param('ID');
|
||||
// returns '1'
|
||||
```php
|
||||
// GET /teams/players/1/
|
||||
|
||||
echo $this->getRequest()->param('ID');
|
||||
// returns '1'
|
||||
```
|
||||
|
||||
## 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 |
|
||||
| `//` | **Shift Point** - Declares that only variables denoted with a $ are parsed into the $params AFTER this point in the regex |
|
||||
|
||||
:::yml
|
||||
'teams/$Action/$ID/$OtherID': 'TeamController'
|
||||
```yml
|
||||
'teams/$Action/$ID/$OtherID': 'TeamController'
|
||||
|
||||
# /teams/
|
||||
# /teams/players/
|
||||
# /teams/
|
||||
# /teams/
|
||||
# /teams/players/
|
||||
# /teams/
|
||||
```
|
||||
|
||||
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
|
||||
decision making.
|
||||
|
||||
:::yml
|
||||
'teams/$Action!/$ID!/': 'TeamController'
|
||||
```yml
|
||||
'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
|
||||
`$Action` and `$ID` are required. Any requests to `team/` will result in a `404` error rather than being handed off to
|
||||
the `TeamController`.
|
||||
|
||||
:::yml
|
||||
`admin/help//$Action/$ID`: 'AdminHelp'
|
||||
```yml
|
||||
'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
|
||||
start parsing variables and the appropriate controller action AFTER the `//`).
|
||||
@ -152,11 +157,13 @@ This is useful when you want to provide custom actions for the mapping of `teams
|
||||
|
||||
**mysite/code/controllers/TeamController.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
```
|
||||
<?php
|
||||
|
||||
class TeamController extends Controller {
|
||||
use SilverStripe\Control\Controller;
|
||||
|
||||
class TeamController extends Controller
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'payroll'
|
||||
);
|
||||
@ -165,6 +172,7 @@ This is useful when you want to provide custom actions for the mapping of `teams
|
||||
'staff/$ID/$Name' => 'payroll',
|
||||
'coach/$ID/$Name' => 'payroll'
|
||||
);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
:::php
|
||||
class FeedController extends ContentController {
|
||||
```php
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class FeedController extends ContentController
|
||||
{
|
||||
private static $allowed_actions = array('go');
|
||||
private static $url_handlers = array(
|
||||
'go/$UserName/$AuthToken/$Timestamp/$OutputType/$DeleteMode' => 'go'
|
||||
);
|
||||
public function go() {
|
||||
|
||||
public function go()
|
||||
{
|
||||
$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
|
||||
information for the framework to choose the desired controller.
|
||||
|
||||
:::yaml
|
||||
Director:
|
||||
```yml
|
||||
Director:
|
||||
rules:
|
||||
'feed': 'FeedController'
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
|
@ -27,22 +27,30 @@ In practice, this looks like:
|
||||
|
||||
**mysite/code/Page.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\RequiredFields;
|
||||
use SilverStripe\Forms\TextField;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'HelloForm'
|
||||
);
|
||||
|
||||
public function HelloForm() {
|
||||
public function HelloForm()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
TextField::create('Name', 'Your Name')
|
||||
);
|
||||
|
||||
$actions = new FieldList(
|
||||
FormAction::create("doSayHello")->setTitle("Say hello")
|
||||
FormAction::create('doSayHello')->setTitle('Say hello')
|
||||
);
|
||||
|
||||
$required = new RequiredFields('Name');
|
||||
@ -52,18 +60,20 @@ In practice, this looks like:
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function doSayHello($data, Form $form) {
|
||||
$form->sessionMessage('Hello '. $data['Name'], 'success');
|
||||
public function doSayHello($data, Form $form)
|
||||
{
|
||||
$form->sessionMessage('Hello ' . $data['Name'], 'success');
|
||||
|
||||
return $this->redirectBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**mysite/templates/Page.ss**
|
||||
|
||||
:::ss
|
||||
$HelloForm
|
||||
|
||||
```ss
|
||||
$HelloForm
|
||||
```
|
||||
|
||||
<div class="info" markdown="1">
|
||||
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
|
||||
controller action. To grant it access through URLs, we add it to the `$allowed_actions` array.
|
||||
|
||||
:::php
|
||||
private static $allowed_actions = array(
|
||||
```php
|
||||
private static $allowed_actions = array(
|
||||
'HelloForm'
|
||||
);
|
||||
);
|
||||
```
|
||||
|
||||
<div class="notice" markdown="1">
|
||||
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].
|
||||
Some common examples are [api:TextField] or [api:DropdownField].
|
||||
|
||||
:::php
|
||||
TextField::create($name, $title, $value);
|
||||
```php
|
||||
TextField::create($name, $title, $value);
|
||||
```
|
||||
|
||||
<div class="info" markdown='1'>
|
||||
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
|
||||
`Form` is rendered.
|
||||
|
||||
:::php
|
||||
$fields = new FieldList(
|
||||
```php
|
||||
$fields = new FieldList(
|
||||
TextField::create('Name'),
|
||||
EmailField::create('Email')
|
||||
);
|
||||
);
|
||||
|
||||
$form = new Form($controller, 'MethodName', $fields, ...);
|
||||
$form = new Form($controller, 'MethodName', $fields, ...);
|
||||
|
||||
// or use `setFields`
|
||||
$form->setFields($fields);
|
||||
// or use `setFields`
|
||||
$form->setFields($fields);
|
||||
|
||||
// to fetch the current fields..
|
||||
$fields = $form->getFields();
|
||||
// to fetch the current fields..
|
||||
$fields = $form->getFields();
|
||||
```
|
||||
|
||||
A field can be appended to the [api:FieldList].
|
||||
|
||||
:::php
|
||||
$fields = $form->Fields();
|
||||
```php
|
||||
$fields = $form->Fields();
|
||||
|
||||
// add a field
|
||||
$fields->push(TextField::create(..));
|
||||
// add a field
|
||||
$fields->push(TextField::create(/* ... */));
|
||||
|
||||
// insert a field before another one
|
||||
$fields->insertBefore(TextField::create(..), 'Email');
|
||||
// insert a field before another one
|
||||
$fields->insertBefore(TextField::create(/* ... */), 'Email');
|
||||
|
||||
// insert a field after another one
|
||||
$fields->insertAfter(TextField::create(..), 'Name');
|
||||
// insert a field after another one
|
||||
$fields->insertAfter(TextField::create(/* ... */), 'Name');
|
||||
|
||||
// insert a tab before the main content tab (used to position tabs in the CMS)
|
||||
$fields->insertBefore(Tab::create(...), 'Main');
|
||||
// Note: you need to create and position the new tab prior to adding fields via addFieldToTab()
|
||||
// insert a tab before the main content tab (used to position tabs in the CMS)
|
||||
$fields->insertBefore(Tab::create(/* ... */), 'Main');
|
||||
// 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.
|
||||
|
||||
:::php
|
||||
$email = $form->Fields()->dataFieldByName('Email');
|
||||
$email->setTitle('Your Email Address');
|
||||
```php
|
||||
$email = $form->Fields()->dataFieldByName('Email');
|
||||
$email->setTitle('Your Email Address');
|
||||
```
|
||||
|
||||
Fields can be removed from the form.
|
||||
|
||||
:::php
|
||||
$form->getFields()->removeByName('Email');
|
||||
```php
|
||||
$form->getFields()->removeByName('Email');
|
||||
```
|
||||
|
||||
<div class="alert" markdown="1">
|
||||
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.
|
||||
</div>
|
||||
|
||||
:::php
|
||||
$field = new TextField(..);
|
||||
```php
|
||||
$field = new TextField(..);
|
||||
|
||||
$field
|
||||
$field
|
||||
->setMaxLength(100)
|
||||
->setAttribute('placeholder', 'Enter a value..')
|
||||
->setTitle('');
|
||||
```
|
||||
|
||||
### Custom Templates
|
||||
|
||||
@ -178,31 +195,34 @@ 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 see [Form Templates](form_templates)
|
||||
|
||||
:::php
|
||||
$form = new Form(..);
|
||||
```php
|
||||
$form = new Form(..);
|
||||
|
||||
$form->setTemplate('CustomForm');
|
||||
$form->setTemplate('CustomForm');
|
||||
|
||||
// or, for a FormField
|
||||
$field = new TextField(..);
|
||||
// or, for a FormField
|
||||
$field = new TextField(..);
|
||||
|
||||
$field->setTemplate('CustomTextField');
|
||||
$field->setFieldHolderTemplate('CustomTextField_Holder');
|
||||
$field->setTemplate('CustomTextField');
|
||||
$field->setFieldHolderTemplate('CustomTextField_Holder');
|
||||
```
|
||||
|
||||
## Adding FormActions
|
||||
|
||||
[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.
|
||||
|
||||
:::php
|
||||
FormAction::create($action, $title);
|
||||
```php
|
||||
FormAction::create($action, $title);
|
||||
```
|
||||
|
||||
As with [api:FormField], the actions for a `Form` are stored within a [api:FieldList] instance in the `actions` property
|
||||
on the form.
|
||||
|
||||
:::php
|
||||
public function MyForm() {
|
||||
$fields = new FieldList(..);
|
||||
```php
|
||||
public function MyForm()
|
||||
{
|
||||
$fields = new FieldList(/* .. */);
|
||||
|
||||
$actions = new FieldList(
|
||||
FormAction::create('doSubmitForm', 'Submit')
|
||||
@ -224,15 +244,18 @@ on the form.
|
||||
$form->setActions($actions);
|
||||
|
||||
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
|
||||
with the particular button. In the previous example, clicking the 'Another Button' would invoke the
|
||||
@ -252,16 +275,24 @@ The `$action` method takes two arguments:
|
||||
* `$data` an array containing the values of the form mapped from `$name => $value`
|
||||
* `$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;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'MyForm'
|
||||
);
|
||||
|
||||
public function MyForm() {
|
||||
public function MyForm()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
TextField::create('Name'),
|
||||
EmailField::create('Email')
|
||||
@ -276,7 +307,8 @@ The `$action` method takes two arguments:
|
||||
return $form
|
||||
}
|
||||
|
||||
public function doSubmitForm($data, $form) {
|
||||
public function doSubmitForm($data, $form)
|
||||
{
|
||||
// Submitted data is available as a map.
|
||||
echo $data['Name'];
|
||||
echo $data['Email'];
|
||||
@ -285,12 +317,13 @@ The `$action` method takes two arguments:
|
||||
echo $form->Fields()->dataFieldByName('Email')->Value();
|
||||
|
||||
// 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.
|
||||
return $this->redirectBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
@ -300,12 +333,14 @@ validating its' own data value.
|
||||
|
||||
For more information, see the [Form Validation](validation) documentation.
|
||||
|
||||
:::php
|
||||
$validator = new RequiredFields(array(
|
||||
'Name', 'Email'
|
||||
));
|
||||
```php
|
||||
$validator = new RequiredFields(array(
|
||||
'Name',
|
||||
'Email'
|
||||
));
|
||||
|
||||
$form = new Form($this, 'MyForm', $fields, $actions, $validator);
|
||||
$form = new Form($this, 'MyForm', $fields, $actions, $validator);
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
||||
|
@ -7,16 +7,24 @@ 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
|
||||
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;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'MyForm'
|
||||
);
|
||||
|
||||
public function MyForm() {
|
||||
public function MyForm()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
TextField::create('Name'),
|
||||
EmailField::create('Email')
|
||||
@ -40,10 +48,12 @@ the [api:Form] constructor or through the function `setValidator`.
|
||||
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
|
||||
`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.
|
||||
</div>
|
||||
|
||||
:::php
|
||||
public function validate($validator) {
|
||||
if($this->Value() == 10) {
|
||||
```php
|
||||
public function validate($validator)
|
||||
{
|
||||
if ((int) $this->Value() === 10) {
|
||||
$validator->validationError($this->Name(), 'This value cannot be 10');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
@ -86,24 +98,26 @@ 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
|
||||
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) {
|
||||
if(!is_numeric($this->value)) {
|
||||
class CustomNumberField extends TextField
|
||||
{
|
||||
public function validate($validator)
|
||||
{
|
||||
if (!is_numeric($this->value)) {
|
||||
$validator->validationError(
|
||||
$this->name, "Not a number. This must be between 2 and 5", "validation", false
|
||||
$this->name, 'Not a number. This must be between 2 and 5', 'validation', false
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if($this->value > 5 || $this->value < 2) {
|
||||
} elseif ($this->value > 5 || $this->value < 2) {
|
||||
$validator->validationError(
|
||||
$this->name, "Your number must be between 2 and 5", "validation", false
|
||||
$this->name, 'Your number must be between 2 and 5', 'validation', false
|
||||
);
|
||||
|
||||
return false;
|
||||
@ -111,22 +125,32 @@ the same validation logic applied to it throughout.
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
`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;
|
||||
|
||||
class Page_Controller extends ContentController
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'MyForm'
|
||||
);
|
||||
|
||||
public function MyForm() {
|
||||
public function MyForm()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
TextField::create('Name'),
|
||||
EmailField::create('Email')
|
||||
@ -141,24 +165,26 @@ reusable and would not be possible within the `CMS` or other automated `UI` but
|
||||
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.
|
||||
|
||||
$check = Member::get()->filter('Email', $data['Email'])->first();
|
||||
|
||||
if($check) {
|
||||
if ($check) {
|
||||
$form->addErrorMessage('Email', 'This email already exists', 'bad');
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Exempt validation actions
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
:::php
|
||||
$actions = new FieldList(
|
||||
```php
|
||||
$actions = new FieldList(
|
||||
$action = FormAction::create('doSubmitForm', 'Submit')
|
||||
);
|
||||
$form = new Form($controller, 'MyForm', $fields, $actions);
|
||||
);
|
||||
$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']);
|
||||
// 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']);
|
||||
```
|
||||
|
||||
## Server-side validation messages
|
||||
|
||||
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.
|
||||
|
||||
:::php
|
||||
$field = new TextField(..);
|
||||
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
|
||||
```php
|
||||
$field = new TextField(/* .. */);
|
||||
$field->setCustomValidationMessage('Whoops, looks like you have missed me!');
|
||||
|
||||
## 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
|
||||
classes added to each input. For Parsley we can structure the form like.
|
||||
|
||||
:::php
|
||||
$form = new Form(..);
|
||||
$form->setAttribute('data-parsley-validate', true);
|
||||
```php
|
||||
$form = new Form(/* .. */);
|
||||
$form->setAttribute('data-parsley-validate', true);
|
||||
|
||||
$field = $fields->dataFieldByName('Name');
|
||||
|
||||
$field->setAttribute('required', true);
|
||||
$field->setAttribute('data-parsley-mincheck', '2');
|
||||
$field = $fields->dataFieldByName('Name');
|
||||
|
||||
$field->setAttribute('required', true);
|
||||
$field->setAttribute('data-parsley-mincheck', '2');
|
||||
```
|
||||
|
||||
## Model Validation
|
||||
|
||||
@ -228,11 +254,14 @@ error message, or a [api:ValidationResult] object containing the list of errors
|
||||
|
||||
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);
|
||||
|
||||
// Example error handling
|
||||
@ -243,8 +272,8 @@ E.g.
|
||||
// If success
|
||||
return $this->redirect($this->Link('success'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Validation in the CMS
|
||||
|
||||
@ -257,16 +286,21 @@ respect the provided `Validator` and handle displaying error and success respons
|
||||
Again, custom error messages can be provided through the `FormField`
|
||||
</div>
|
||||
|
||||
:::php
|
||||
<?php
|
||||
```php
|
||||
<?php
|
||||
|
||||
class Page extends SiteTree {
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Forms\RequiredFields;
|
||||
|
||||
class Page extends SiteTree
|
||||
{
|
||||
private static $db = array(
|
||||
'MyRequiredField' => 'Text'
|
||||
);
|
||||
|
||||
public function getCMSFields() {
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->addFieldToTab('Root.Main',
|
||||
@ -274,11 +308,14 @@ Again, custom error messages can be provided through the `FormField`
|
||||
);
|
||||
}
|
||||
|
||||
public function getCMSValidator() {
|
||||
public function getCMSValidator()
|
||||
{
|
||||
return new RequiredFields(array(
|
||||
'MyRequiredField'
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
||||
|
@ -431,7 +431,7 @@ code could be used:
|
||||
:::php
|
||||
class GalleryPage extends Page {}
|
||||
|
||||
class GalleryPage_Controller extends Page_Controller {
|
||||
class GalleryPageController extends PageController {
|
||||
private static $allowed_actions = array('Form');
|
||||
public function Form() {
|
||||
$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
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
class PageController extends ContentController {
|
||||
|
||||
public function SearchForm() {
|
||||
$fields = new FieldList(
|
||||
@ -128,7 +128,7 @@ Our controller will now just have to create a new instance of this form object.
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
class PageController extends ContentController {
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'SearchForm',
|
||||
|
@ -8,7 +8,7 @@ Let's start by defining a new `ContactPage` page type:
|
||||
<?php
|
||||
class ContactPage extends Page {
|
||||
}
|
||||
class ContactPage_Controller extends Page_Controller {
|
||||
class ContactPageController extends PageController {
|
||||
private static $allowed_actions = array('Form');
|
||||
public function Form() {
|
||||
$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.
|
||||
|
||||
:::php
|
||||
class ContactPage_Controller extends Page_Controller {
|
||||
class ContactPageController extends PageController {
|
||||
private static $allowed_actions = array('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.
|
||||
|
||||
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
|
||||
|
||||
@ -151,7 +151,7 @@ heavy load:
|
||||
<% 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,
|
||||
use something like:
|
||||
|
@ -14,27 +14,35 @@ The simple usage, Permission::check("PERM_CODE") will detect if the currently lo
|
||||
|
||||
**Group ACLs**
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
## PermissionProvider
|
||||
|
||||
[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.
|
||||
|
||||
:::php
|
||||
class Page_Controller implements PermissionProvider {
|
||||
public function init() {
|
||||
```php
|
||||
use SilverStripe\Security\PermissionProvider;
|
||||
|
||||
class PageController implements PermissionProvider
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if(!Permission::check("VIEW_SITE")) Security::permissionFailure();
|
||||
if (!Permission::check('VIEW_SITE')) {
|
||||
Security::permissionFailure();
|
||||
}
|
||||
}
|
||||
|
||||
public function providePermissions() {
|
||||
public function providePermissions()
|
||||
{
|
||||
return array(
|
||||
"VIEW_SITE" => "Access the site",
|
||||
'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
|
||||
@ -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`.
|
||||
|
||||
:::php
|
||||
if (Permission::checkMember($member, 'CMS_ACCESS')) {
|
||||
```php
|
||||
if (Permission::checkMember($member, 'CMS_ACCESS')) {
|
||||
//user can access the CMS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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(
|
||||
'rss'
|
||||
@ -118,7 +118,7 @@ Then in our controller, we add a new action which returns a the XML list of `Pla
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
class PageController extends ContentController {
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'players'
|
||||
|
@ -79,9 +79,9 @@ the `$fields` constructor parameter.
|
||||
:::php
|
||||
<?php
|
||||
|
||||
..
|
||||
// ..
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
class PageController extends ContentController {
|
||||
|
||||
public function SearchForm() {
|
||||
$context = singleton('MyDataObject')->getCustomSearchContext();
|
||||
|
@ -59,7 +59,7 @@ authorised users, the following should be considered:
|
||||
|
||||
|
||||
:::php
|
||||
class Page_Controller extends ContentController {
|
||||
class PageController extends ContentController {
|
||||
public function init() {
|
||||
parent::init();
|
||||
// Whitelist any protected files on this page for the current user
|
||||
@ -88,7 +88,7 @@ authorised users, the following should be considered:
|
||||
|
||||
|
||||
:::php
|
||||
class Page_Controller extends ContentController {
|
||||
class PageController extends ContentController {
|
||||
public function init() {
|
||||
parent::init();
|
||||
// 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.
|
||||
*
|
||||
* <code>
|
||||
* class ExampleForm_Controller extends Page_Controller {
|
||||
* class ExampleFormController extends PageController {
|
||||
*
|
||||
* function Form() {
|
||||
* $fields = new FieldList(
|
||||
|
@ -518,7 +518,7 @@ class Security extends Controller implements TemplateGlobalProvider
|
||||
$tmpPage = new SiteTree();
|
||||
$tmpPage->Title = $title;
|
||||
/** @skipUpgrade */
|
||||
$tmpPage->URLSegment = "Security";
|
||||
$tmpPage->URLSegment = 'Security';
|
||||
// Disable ID-based caching of the log-in page by making it a random number
|
||||
$tmpPage->ID = -1 * rand(1, 10000000);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user