Fixed references to deprecated APIs in docs

This commit is contained in:
Ingo Schommer 2012-06-28 11:43:56 +02:00
parent 0236a3c03a
commit 19e087d226
29 changed files with 243 additions and 357 deletions

View File

@ -625,7 +625,7 @@ Note that its just necessary if SilverStripe is used in a language other than th
The SS_Report::register() method is deprecated. You no longer need to explicitly register reports. The CMS now
automatically picks up and adds all Report classes to the list of reports in ReportAdmin. You can choose to exclude
certain reports by using the SS_Report::add_excluded_reports() method.
fe
### Removed the ability use a SQLQuery object in a SS_Report
You can no longer create reports that using the deprecated DataObject->buildSQL and DataObject->extendedSQL

View File

@ -2,7 +2,7 @@
The [api:DataObject::$defaults] array allows you to specify simple static values to be the default value for when a
record is created, but in many situations default values needs to be dynamically calculated. In order to do this, the
[api:DataObjectSet->populateDefaults()] method will need to be overloaded.
`[api:DataObject->populateDefaults()]` method will need to be overloaded.
This method is called whenever a new record is instantiated, and you must be sure to call the method on the parent
object!
@ -13,7 +13,6 @@ A simple example is to set a field to the current date and time:
/**
* Sets the Date field to the current date.
*/
public function populateDefaults() {
$this->Date = date('Y-m-d');
parent::populateDefaults();
@ -27,7 +26,6 @@ methods. For example:
* This method combines the Title of the parent object with the Title of this
* object in the FullTitle field.
*/
public function populateDefaults() {
if($parent = $this->Parent()) {
$this->FullTitle = $parent->Title . ': ' . $this->Title;

View File

@ -85,9 +85,8 @@ Create a new file called `zzz_admin/code/BookmarkedPageExtension.php` and insert
:::php
<?php
class BookmarkedPageExtension extends DataExtension {
public function extraStatics() {
return array('db' => array('IsBookmarked' => 'Boolean'));
}
static $db = array('IsBookmarked' => 'Boolean');
public function updateCMSFields(&$fields) {
$fields->addFieldToTab('Root.Main',
new CheckboxField('IsBookmarked', "Show in CMS bookmarks?")

View File

@ -2,6 +2,10 @@
## Introduction
<div class="warning" markdown="1">
This field is deprecated in favour of the new [GridField](/topics/grid-field) API.
</div>
Shows a group of DataObjects as a (readonly) tabular list (similiar to `[api:TableListField]`.)
You can specify limits and filters for the resultset by customizing query-settings (mostly the ID-field on the other
@ -128,23 +132,4 @@ Most of the time, you need to override the following methods:
* ComplexTableField->sourceItems() - querying
* ComplexTableField->DetailForm() - form output
* ComplexTableField_Popup->saveComplexTableField() - saving
### Examples
* `[api:AssetTableField]`
* `[api:MemberTableField]`
## API Documentation
`[api:ComplexTableField]`
## Todo
* Find a less fragile solution for accessing this field through the main controller and ReferencedField, e.g. build a
seperate CTF-instance (doesn't necessarly have to be connected to the original by ReferencedField)
* Control width/height of popup by constructor (hardcoded at the moment)
* Integrate search from MemberTableField.php directly on `[api:ComplexTableField]`
* Less performance-hungry implementation of detail-view paging (don't return all items on a single view)
* Use automatic has-many and many-many functions to return a ComponentSet rather than building the join manually
* Javascript/Ajax-Sorting (see [http://www.activewidgets.com/grid/](http://www.activewidgets.com/grid/) and [http://openrico.org/rico/livegrid.page](http://openrico.org/rico/livegrid.page))
* ComplexTableField_Popup->saveComplexTableField() - saving

View File

@ -45,56 +45,42 @@ For example above we want to override Member with a Custom Member so we would wr
### Adding extra database fields
Extra database fields can be added with a extension by defining an **extraStatics()** method. These will be added to the table of the base object - the extension will actually edit the $db, $has_one, etc static variables on load.
Extra database fields can be added with a extension in the same manner as if they
were placed on the `DataObject` class they're applied to. These will be added to the table of the base object - the extension will actually edit the $db, $has_one, etc static variables on load.
The function should return a map where the keys are the names of the static variables to update:
:::php
class CustomMember extends DataExtension {
public function extraStatics() {
return array(
'db' => array(
'AvatarURL' => 'Varchar',
),
'has_one' => array(
'RelatedMember' => 'Member',
),
);
}
static $db = array(
'AvatarURL' => 'Varchar',
);
static $has_one = array(
'RelatedMember' => 'Member',
);
}
*NOTE*
If you want to add has_one or db items to a particular class, then that class **must** have that static variable
explicitly defined, even if it's just a blank array. For example, the extension method above wouldn't work if you added
to a class that didn't have static $has_one explicitly declared on the object. This is because of PHP's crappy support
for statics.
### Modifying CMS Fields
The member class demonstrates an extension that allows you to update the default CMS fields for an object in a
extension:
The member class demonstrates an extension that allows you to update the default CMS fields for an
object in an extension:
:::php
public function getCMSFields() {
...
$this->extend('updateCMSFields', $fields);
return $fields;
// ...
$this->extend('updateCMSFields', $fields);
return $fields;
}
The $fields parameter is passed by reference, as it is an object.
The `$`fields parameter is passed by reference, as it is an object.
:::php
public function updateCMSFields(FieldList $fields) {
$fields->push(new TextField('Position', 'Position Title'));
$fields->push(new UploadField('Image', 'Profile Image'));
$fields->push(new TextField('Position', 'Position Title'));
$fields->push(new UploadField('Image', 'Profile Image'));
}
### Custom database generation
Some extensions are designed to transparently add more sophisticated data-collection capabilities to your data object.

View File

@ -31,7 +31,7 @@ by adding, removing or configuring fields.
'IsActive' => 'Boolean'
);
public function getCMSFields() {
return new FieldSet(
return new FieldList(
new CheckboxField('IsActive')
);
}

View File

@ -10,24 +10,6 @@ the viewer and/or perform processing steps.
* Checking for an Ajax-Request: Use Director::is_ajax() instead of checking for $_REQUEST['ajax'].
## Redirection
The `[api:Director]` class has a number of methods to facilitate 301 and 302 HTTP redirection.
* **Director::redirect("action-name")**: If there's no slash in the URL passed to redirect, then it is assumed that you
want to go to a different action on the current controller.
* **Director::redirect("relative/url")**: If there is a slash in the URL, it's taken to be a normal URL. Relative URLs
will are assumed to be relative to the site-root; so Director::redirect("home/") will work no matter what the current
URL is.
* **Director::redirect("http://www.absoluteurl.com")**: Of course, you can pass redirect() absolute URL s too.
* **Director::redirectPerm("any-url")**: redirectPerm takes the same arguments as redirect, but it will send a 301
(permanent) instead of a 302 (temporary) header. It improves search rankings, so this should be used whenever the
following two conditions are true:
* Nothing happens server-side prior to the redirection
* The redirection will always occur
* **Director::redirectBack()**: This will return you to the previous page. There's no permanent version of
redirectBack().
## Request processing

View File

@ -34,8 +34,6 @@ This is a highlevel overview of available `[api:apiFormField]` subclasses. An au
* `[api:NumericField]`: Text input field with validation for numeric values.
* `[api:OptionsetField]`: Set of radio buttons designed to emulate a dropdown.
* `[api:PhoneNumberField]`: Field for displaying phone numbers. It separates the number, the area code and optionally the country code and extension.
* `[api:UniqueRestrictedTextField]`: Text field that automatically checks that the value entered is unique for the given set of fields in a given set of tables
* `[api:SelectionGroup]`: SelectionGroup represents a number of fields which are selectable by a radio button that appears at the beginning of each item.
* `[api:TimeField]`: Input field with time-specific, localized validation.

View File

@ -72,10 +72,10 @@ You can also create your own functions by extending the image class, for example
public function Exif(){
//http://www.v-nessa.net/2010/08/02/using-php-to-extract-image-exif-data
$image = $this->AbsoluteURL;
$d=new DataObjectSet();
$d=new ArrayList();
$exif = exif_read_data($image, 0, true);
foreach ($exif as $key => $section) {
$a=new DataObjectSet();
$a=new ArrayList();
foreach ($section as $name => $val)
$a->push(new ArrayData(array("Title"=>$name,"Content"=>$val)));
$d->push(new ArrayData(array("Title"=>strtolower($key),"Content"=>$a)));

View File

@ -113,9 +113,12 @@ things, you should add appropriate `[api:Permission::checkMember()]` calls to th
}
}
public function extraStatics() {
// Return an array containing keys 'db', 'has_one', 'many_many', 'belongs_many_many',
}
// define additional properties
static $db = array();
static $has_one = array();
static $has_many = array();
static $many_many = array();
static $belongs_many_many = array();
public function somethingElse() {
// You can add any other methods you like, which you can call directly on the member object.

View File

@ -12,7 +12,7 @@ The default output of a `[api:SearchContext]` is either a `[api:SQLQuery]` objec
In case you need multiple contexts, consider namespacing your request parameters by using `FieldList->namespace()` on
the $fields constructor parameter.
`[api:SearchContext]` is mainly used by `[api:ModelAdmin]`, our generic data administration interface. Another
`[api:SearchContext]` is mainly used by `[ModelAdmin](/reference/modeladmin)`, our generic data administration interface. Another
implementation can be found in generic frontend search forms through the [genericviews](http://silverstripe.org/generic-views-module) module.
## Usage
@ -187,6 +187,6 @@ See `[api:SearchFilter]` API Documentation
## Related
* `[api:ModelAdmin]`
* [ModelAdmin](/reference/modeladmin)
* [RestfulServer module](https://github.com/silverstripe/silverstripe-restfulserver)
* [Tutorial: Site Search](/tutorials/4-site-search)

View File

@ -39,13 +39,9 @@ Create a mysite/code/CustomSiteConfig.php file.
class CustomSiteConfig extends DataExtension {
public function extraStatics() {
return array(
'db' => array(
'FooterContent' => 'HTMLText'
)
);
}
static $db = array(
'FooterContent' => 'HTMLText'
);
public function updateCMSFields(FieldList $fields) {
$fields->addFieldToTab("Root.Main", new HTMLEditorField("FooterContent", "Footer Content"));

View File

@ -2,45 +2,40 @@
## Introduction
An object representing a SQL query. It is easier to deal with object-wrappers than string-parsing a raw SQL-query. This
object is used by `[api:DataObject]`, though...
An object representing a SQL query. It is easier to deal with object-wrappers than string-parsing a raw SQL-query. This object is used by the SilverStripe ORM internally.
Dealing with low-level SQL is not encouraged, since the ORM provides
powerful abstraction APIs (see [datamodel](/topics/datamodel).
A word of caution: Dealing with low-level SQL is not encouraged in the SilverStripe [datamodel](/topics/datamodel) for various
reasons. You'll break the behaviour of:
You'll run the risk of breaking various assumptions the ORM and code based on it have:
* Custom getters/setters
* DataObject::onBeforeWrite/onBeforeDelete
* Custom getters/setters (object property can differ from database column)
* DataObject hooks like onBeforeWrite() and onBeforeDelete()
* Automatic casting
* Default-setting through object-model
* `[api:DataObject]`
* Default values set through objects
* Database abstraction
We'll explain some ways to use *SELECT* with the full power of SQL, but still maintain a connection to the SilverStripe
[datamodel](/topics/datamodel).
We'll explain some ways to use *SELECT* with the full power of SQL,
but still maintain a connection to the ORM where possible.
## Usage
### SELECT
:::php
$sqlQuery = new SQLQuery();
$sqlQuery->select = array(
'Firstname AS Name',
'YEAR(Birthday) AS BirthYear'
$sqlQuery->setFrom('Player');
$sqlQuery->selectField('FieldName', 'Name');
$sqlQuery->selectField('YEAR("Birthday")', 'BirthYear');
$sqlQuery->addLeftJoin(
'Team',
'"Player"."TeamID" = "Team"."ID"'
);
$sqlQuery->from = "
Player
LEFT JOIN Team ON Player.TeamID = Team.ID
";
$sqlQuery->where = "
YEAR(Birthday) = 1982
";
// $sqlQuery->orderby = "";
// $sqlQuery->groupby = "";
// $sqlQuery->having = "";
// $sqlQuery->limit = "";
// $sqlQuery->distinct = true;
$sqlQuery->addWhere('YEAR("Birthday") = 1982');
// $sqlQuery->setOrderBy(...);
// $sqlQuery->setGroupBy(...);
// $sqlQuery->setHaving(...);
// $sqlQuery->setLimit(...);
// $sqlQuery->setDistinct(true);
// get the raw SQL
$rawSQL = $sqlQuery->sql();
@ -53,7 +48,7 @@ We'll explain some ways to use *SELECT* with the full power of SQL, but still ma
:::php
// ...
$sqlQuery->delete = true;
$sqlQuery->setDelete(true);
### INSERT/UPDATE
@ -80,10 +75,10 @@ Raw SQL is handy for performance-optimized calls.
:::php
class Team extends DataObject {
public function getPlayerCount() {
$sqlQuery = new SQLQuery(
"COUNT(Player.ID)",
"Team LEFT JOIN Player ON Team.ID = Player.TeamID"
);
$sqlQuery = new SQLQuery();
$sqlQuery->setFrom('Player');
$sqlQuery->addSelect('COUNT("Player"."ID")');
$sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"');
return $sqlQuery->execute()->value();
}
@ -99,10 +94,9 @@ Way faster than dealing with `[api:DataObject]`s, but watch out for premature op
Useful for creating dropdowns.
:::php
$sqlQuery = new SQLQuery(
array('YEAR(Birthdate)', 'Birthdate'),
'Player'
);
$sqlQuery = new SQLQuery();
$sqlQuery->setFrom('Player');
$sqlQuery->selectField('YEAR("Birthdate")', 'Birthdate');
$map = $sqlQuery->execute()->map();
$field = new DropdownField('Birthdates', 'Birthdates', $map);
@ -112,8 +106,11 @@ Useful for creating dropdowns.
This is not recommended for most cases, but you can also use the SilverStripe database-layer to fire off a raw query:
:::php
DB::query("UPDATE Player SET Status='Active'");
DB::query('UPDATE "Player" SET "Status"=\'Active\'');
<<<<<<< Updated upstream
### Transforming a result to `[api:ArrayList]`
=======
One example for using a raw DB::query is when you are wanting to order twice in the database:
:::php
@ -130,46 +127,41 @@ You can gain some ground on the datamodel-side when involving the selected class
need to call *buildSQL* from a specific object-instance, a *singleton* will do just fine.
:::php
$sqlQuery = singleton('Player')->buildSQL(
'YEAR(Birthdate) = 1982'
);
$sqlQuery = singleton('Player')->buildSQL('YEAR("Birthdate") = 1982');
This form of building a query has the following advantages:
* Respects DataObject::$default_sort
* Automatically LEFT JOIN on all base-tables (see [database-structure](database-structure))
* Automatically `LEFT JOIN` on all base-tables (see [database-structure](database-structure))
* Selection of *ID*, *ClassName*, *RecordClassName*, which are necessary to use *buildDataObjectSet* later on
* Filtering records for correct *ClassName*
### Transforming a result to `[api:DataObjectSet]`
>>>>>>> Stashed changes
This is a commonly used technique inside SilverStripe: Use raw SQL, but transfer the resulting rows back into
`[api:DataObject]`s.
:::php
$sqlQuery = new SQLQuery();
$sqlQuery->select = array(
'Firstname AS Name',
'YEAR(Birthday) AS BirthYear',
$sqlQuery->setSelect(array(
'"Firstname" AS "Name"',
'YEAR("Birthday") AS "BirthYear"',
// IMPORTANT: Needs to be set after other selects to avoid overlays
'Player.ClassName AS ClassName',
'Player.ClassName AS RecordClassName',
'Player.ID AS ID'
);
$sqlQuery->from = array(
"Player",
"LEFT JOIN Team ON Player.TeamID = Team.ID"
);
$sqlQuery->where = array(
"YEAR(Player.Birthday) = 1982"
);
'"Player"."ClassName" AS "ClassName"',
'"Player"."ClassName" AS "RecordClassName"',
'"Player"."ID" AS "ID"'
));
$sqlQuery->setFrom('Player');
$sqlQuery->addLeftJoin('Team', '"Player"."TeamID" = "Team"."ID"');
$sqlQuery->addWhere("YEAR("Player"."Birthday") = 1982");
$result = $sqlQuery->execute();
var_dump($result->first()); // array
// let Silverstripe work the magic
$myDataObjectSet = singleton('Player')->buildDataObjectSet($result);
$myList = singleton('Player')->buildDataObjectSet($result);
var_dump($myDataObjectSet->First()); // DataObject
// this is where it gets tricky

View File

@ -2,6 +2,10 @@
## Introduction
<div class="warning" markdown="1">
This field is deprecated in favour of the new [GridField](/topics/grid-field) API.
</div>
Form field that embeds a list of `[api:DataObject]`s into a form, such as a member list or a file list.
Provides customizeable columns, record-deletion by ajax, paging, sorting, CSV-export, printing, input by
`[api:DataObject]` or raw SQL.

View File

@ -4,7 +4,7 @@ These are the main changes to the SiverStripe 3 template language.
## Control blocks: Loops vs. Scope
The `<% control var %>...<% end_control %>` in SilverStripe prior to version 3 has two different meanings. Firstly, if the control variable is a collection (e.g. DataObjectSet), then `<% control %>` iterates over that set. If it's a non-iteratable object, however, `<% control %>` introduces a new scope, which is used to render the inner template code. This dual-use is confusing to some people, and doesn't allow a collection of objects to be used as a scope.
The `<% control var %>...<% end_control %>` in SilverStripe prior to version 3 has two different meanings. Firstly, if the control variable is a collection (e.g. DataList), then `<% control %>` iterates over that set. If it's a non-iteratable object, however, `<% control %>` introduces a new scope, which is used to render the inner template code. This dual-use is confusing to some people, and doesn't allow a collection of objects to be used as a scope.
In SilverStripe 3, the first usage (iteration) is replaced by `<% loop var %>`. The second usage (scoping) is replaced by `<% with var %>`

View File

@ -49,20 +49,10 @@ Append the option and corresponding value to your URL in your browser's address
| URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- |
| debug_memory | | 1 | | Output the number of bytes of memory used for this
| debug_memory | | 1 | | Output the number of bytes of memory used for this request |
| debug_profile | | 1 | | Enable the [profiler](/topics/debugging) for the duration of the request |
| profile_trace | | 1 | | Includes full stack traces, must be used with **debug_profile** |
| debug_behaviour | | 1 | | Get profiling of [Behaviour.js](http://bennolan.com/behaviour) performance (Firebug recommended) |
| debug_javascript | | 1 | | Force debug-output on live-sites |
## Misc
| URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- |
| forceFormat | | xhtml,html | | Force the content negotiator to deliver HTML or XHTML is allowed |
| showspam | | 1 | | Show comments marked as spam when viewing Comments on a Page (Saving spam to the database must be enabled) |
| ajax | | 1 | | Force request to process as AJAX request, useful for debugging from a browser |
| force_ajax | | 1 | | Similar to **ajax** |
## Security Redirects

View File

@ -36,9 +36,6 @@ incomplete - please add to it** *Try to keep it in alphabetical order too! :)*
| Authenticator::register_authenticator($authenticator);| | Enable an authentication method (for more details see [security](/topics/security)). |
| Authenticator::set_default_authenticator($authenticator); | | Modify tab-order on login-form.|
| BBCodeParser::disable_autolink_urls(); | | Disables plain hyperlinks from being turned into links when bbcode is parsed. |
| BlogEntry::allow_wysiwyg_editing(); | | Enable rich text editing for blog posts. |
| ContentNegotiator::set_encoding(string $encoding) | | The encoding charset to use - UTF-8 by default |
| ContentNegotiator::disable() | | Disables the negotiation of content type -usually used to stop it from rewriting the DOCTYPE of the document
| DataObject::$create_table_options['MySQLDatabase'] = 'ENGINE=MyISAM';| | Set the default database engine to MyISAM (versions 2.4 and below already default to MyISAM) |
| Debug::send_errors_to(string $email) | | Send live errors on your site to this address (site has to be in 'live' mode using Director::set_environment_type(live) for this to occur |
| Director::set_environment_type(string dev,test,live) | | Sets the environment type (e.g. dev site will show errors, live site hides them and displays a 500 error instead) |
@ -48,8 +45,6 @@ incomplete - please add to it** *Try to keep it in alphabetical order too! :)*
| Email::send_all_emails_to(string $email) | | Sends all emails to this address. Useful for debugging your email sending functions |
| Email::cc_all_emails_to(string $email) | | Useful for CC'ing all emails to someone checking correspondence |
| Email::bcc_all_emails_to(string $email) | | BCC all emails to this address, similar to CC'ing emails (above) |
| MathSpamProtection::setEnabled() | | Adds a math spam question to all page comment forms |
| PageComment::enableModeration(); | | Enables comment moderation |
| Requirements::set_suffix_requirements(false); | | Disable appending the current date to included files |
| Security::encrypt_passwords($encrypt_passwords); | | Specify if you want store your passwords in clear text or encrypted (for more details see [security](/topics/security)) |
| Security::set_password_encryption_algorithm($algorithm, $use_salt);| | If you choose to encrypt your passwords, you can choose which algorithm is used to and if a salt should be used to increase the security level even more (for more details see [security](/topics/security)). |

View File

@ -88,6 +88,20 @@ after it. If the URLSegment is **order** then `/order/tag/34` and `/order/tag/a
You can use the `debug_request=1` switch from the [urlvariabletools](/reference/urlvariabletools) to see these in action.
## Redirection
Controllers facilitate HTTP redirection.
Note: These methods have been formerly located on the `[api:Director]` class.
* `redirect("action-name")`: If there's no slash in the URL passed to redirect, then it is assumed that you want to go to a different action on the current controller.
* `redirect("relative/url")`: If there is a slash in the URL, it's taken to be a normal URL. Relative URLs
will are assumed to be relative to the site-root.
* `redirect("http://www.absoluteurl.com")`: Of course, you can pass `redirect()` absolute URLs too.
* `redirectBack()`: This will return you to the previous page.
The `redirect()` method takes an optional HTTP status code,
either `301` for permanent redirects, or `302` for temporary redirects (default).
## API Documentation
`[api:Controller]`

View File

@ -10,7 +10,7 @@ TODO Screenshot of admin interface
## Upload
TODO Link to Upload and FileIframeField classes
TODO Link to UploadField and FileField classes
## Image Resizing

View File

@ -60,78 +60,42 @@ The real difference, however, is that you can then define your controller method
## Form Field Types
There are many classes extending `[api:FormField]`. Some examples:
* `[api:TextField]`
* `[api:EmailField]`
* `[api:NumericField]`
* `[api:DateField]`
* `[api:CheckboxField]`
* `[api:DropdownField]`
* `[api:OptionsetField]`
* `[api:CheckboxSetField]`
Full overview at [form-field-types](/reference/form-field-types)
There are many classes extending `[api:FormField]`,
there's a full overview at [form-field-types](/reference/form-field-types)
### Using Form Fields
To get these fields automatically rendered into a form element, all you need to do is create a new instance of the
To get these fields automatically rendered into a form element,
all you need to do is create a new instance of the
class, and add it to the fieldlist of the form.
:::php
$form = new Form(
$controller = $this,
$name = "SignupForm",
$fields = new FieldList(
new TextField(
$name = "FirstName",
$title = "First name"
),
new TextField("Surname"),
new EmailField("Email", "Email address"),
),
$actions = new FieldList(
// List the action buttons here
new FormAction("signup", "Sign up")
),
$requiredFields = new RequiredFields(
// List the required fields here: "Email", "FirstName"
)
);
You'll note some of the fields are optional.
Implementing the more complex fields requires extra arguments.
:::php
$form = new Form(
$controller = $this,
$name = "SignupForm",
$fields = new FieldList(
// List the your fields here
new TextField(
$name = "FirstName",
$title = "First name"
),
new TextField("Surname"),
new EmailField("Email", "Email address")
new DropdownField(
$name = "Country",
$title = "Country (if outside nz)",
$source = Geoip::getCountryDropDown(),
$value = Geoip::visitor_country()
)
), new FieldList(
// List the action buttons here
new FormAction("signup", "Sign up")
), new RequiredFields(
// List the required fields here: "Email", "FirstName"
)
$this, // controller
"SignupForm", // form name
new FieldList( // fields
TextField::create("FirstName")
->setTitle('First name')
TextField::create("Surname")
->setTitle('Last name')
->setMaxLength(50),
EmailField::create("Email")
->setTitle("Email address")
->setAttribute('type', 'email')
),
new FieldList( // actions
FormAction::create("signup")->setTitle("Sign up")
),
new RequiredFields( // validation
"Email", "FirstName"
)
);
You'll notice that we've used a new notation for creating form fields,
using `create()` instead of the `new` operator. These are functionally equivalent,
but allows PHP to chain operations like `setTitle()` without assigning
the field instance to a temporary variable.
## Readonly
@ -144,7 +108,7 @@ Readonly on a Form
Readonly on a FieldList
:::php
$myFieldSet->makeReadonly();
$myFieldList->makeReadonly();
Readonly on a FormField
@ -167,29 +131,29 @@ First of all, you need to create your form on it's own class, that way you can d
:::php
class MyForm extends Form {
public function __construct($controller, $name) {
$fields = new FieldList(
new TextField('FirstName', 'First name'),
new EmailField('Email', 'Email address')
);
public function __construct($controller, $name) {
$fields = new FieldList(
new TextField('FirstName', 'First name'),
new EmailField('Email', 'Email address')
);
$actions = new FieldList(
new FormAction('submit', 'Submit')
);
$actions = new FieldList(
new FormAction('submit', 'Submit')
);
parent::__construct($controller, $name, $fields, $actions);
}
parent::__construct($controller, $name, $fields, $actions);
}
public function forTemplate() {
return $this->renderWith(array(
$this->class,
'Form'
));
}
public function forTemplate() {
return $this->renderWith(array(
$this->class,
'Form'
));
}
public function submit($data, $form) {
// do stuff here
}
public function submit($data, $form) {
// do stuff here
}
}
@ -201,31 +165,31 @@ basic customisation:
:::ss
<form $FormAttributes>
<% if Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
<% end_if %>
<fieldset>
<div id="FirstName" class="field text">
<label class="left" for="{$FormName}_FirstName">First name</label>
$dataFieldByName(FirstName)
</div>
<% if Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
<% end_if %>
<fieldset>
<div id="FirstName" class="field text">
<label class="left" for="{$FormName}_FirstName">First name</label>
$dataFieldByName(FirstName)
</div>
<div id="Email" class="field email">
<label class="left" for="{$FormName}_Email">Email</label>
$dataFieldByName(Email)
</div>
<div id="Email" class="field email">
<label class="left" for="{$FormName}_Email">Email</label>
$dataFieldByName(Email)
</div>
$dataFieldByName(SecurityID)
</fieldset>
$dataFieldByName(SecurityID)
</fieldset>
<% if Actions %>
<div class="Actions">
<% loop Actions %>$Field<% end_loop %>
</div>
<% end_if %>
<% if Actions %>
<div class="Actions">
<% loop Actions %>$Field<% end_loop %>
</div>
<% end_if %>
</form>
`$dataFieldByName(FirstName)` will return the form control contents of `Field()` for the particular field object, in

View File

@ -14,7 +14,7 @@ This example might come from a Controller designed to manage the members of a gr
*/
public function MemberForm() {
$field = new GridField("Members", "Members of this group", $this->group->Members());
return new Form("MemberForm", $this, new FieldSet($field), new FieldSet());
return new Form("MemberForm", $this, new FieldList($field), new FieldList());
}
Note that the only way to specify the data that is listed in a grid field is with `SS_List` argument. If you want to customise the data displayed, you can do so by customising this object.

View File

@ -70,9 +70,9 @@ to write your own logic for any frontend output.
i18n::set_date_format('dd.MM.YYYY');
i18n::set_time_format('HH:mm');
Most localization routines in SilverStripe use the [http://framework.zend.com/manual/en/zend.date.html](Zend_Date API).
Most localization routines in SilverStripe use the [Zend_Date API](http://framework.zend.com/manual/en/zend.date.html).
This means all formats are defined in
[http://framework.zend.com/manual/en/zend.date.constants.html#zend.date.constants.selfdefinedformats](ISO date format),
[ISO date format](http://framework.zend.com/manual/en/zend.date.constants.html#zend.date.constants.selfdefinedformats),
not PHP's built-in [date()](http://nz.php.net/manual/en/function.date.php).
### i18n in URLs
@ -227,22 +227,20 @@ which supports different translation adapters, dealing with different storage fo
By default, SilverStripe 3.x uses a YAML format (through the [Zend_Translate_RailsYAML adapter](https://github.com/chillu/zend_translate_railsyaml)).
Example: sapphire/lang/en.yml (extract)
Example: framework/lang/en.yml (extract)
:::yml
en:
ImageUploader:
Attach: 'Attach %s'
FileIFrameField:
UploadField:
NOTEADDFILES: 'You can add files once you have saved for the first time.'
Translation table: sapphire/lang/de.yml (extract)
Translation table: framework/lang/de.yml (extract)
:::yml
de:
ImageUploader:
ATTACH: '%s anhängen'
FileIframeField:
UploadField:
NOTEADDFILES: 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben'
Note that translations are cached across requests.
@ -262,7 +260,7 @@ Example: framework/lang/en_US.php (extract)
'Attach %s',
'Attach image/file'
);
$lang['en_US']['FileIFrameField']['NOTEADDFILES'] = 'You can add files once you have saved for the first time.';
$lang['en_US']['UploadField']['NOTEADDFILES'] = 'You can add files once you have saved for the first time.';
// ...
@ -270,7 +268,7 @@ Translation table: framework/lang/de_DE.php (extract)
:::php
$lang['de_DE']['ImageUploader']['ATTACH'] = '%s anhängen';
$lang['de_DE']['FileIframeField']['NOTEADDFILES'] = 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben';
$lang['de_DE']['UploadField']['NOTEADDFILES'] = 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben';
In order to enable usage of PHP language definitions in 3.x, you need to register a legacy adapter
in your `mysite/_config.php`:

View File

@ -52,14 +52,11 @@ especially useful if you know how long your source data needs to be.
:::php
class StaffPage extends Page {
static $db = array(
'Author' => 'Varchar(50)'
);
}
class StaffPage_Controller extends Page_Controller {
}
@ -68,7 +65,8 @@ model works.
## Adding Form Fields and Tabs
See [form](/topics/forms) and [tutorial:2-extending-a-basic-site](/tutorials/2-extending-a-basic-site)
See [form](/topics/forms) and [tutorial:2-extending-a-basic-site](/tutorials/2-extending-a-basic-site).
Note: To modify fields in the "Settings" tab, you need to use `updateSettingsFields()` instead.
## Removing inherited form fields and tabs

View File

@ -17,7 +17,7 @@ It is usually added through the `[api:DataObject->getCMSFields()]` method:
static $db = array('Content' => 'HTMLText');
public function getCMSFields() {
return new FieldSet(new HTMLEditorField('Content'));
return new FieldList(new HTMLEditorField('Content'));
}
}

View File

@ -8,7 +8,7 @@ See [Tutorial: Site Search](/tutorials/4-site-search) for details.
## Searching for DataObjects
The `[api:SearchContext]` class provides a good base implementation that you can hook into your own controllers.
A working implementation of searchable DataObjects can be seen in the `[api:ModelAdmin]` class.
A working implementation of searchable DataObjects can be seen in the `[ModelAdmin](/reference/modeladmin)` class.
[SearchContext](/reference/searchcontext) goes into more detail about setting up a default search form for `[api:DataObject]`s.
@ -33,7 +33,7 @@ dedicated search service like the [sphinx module](http://silverstripe.org/sphinx
## Related
* `[api:ModelAdmin]`
* [ModelAdmin](/reference/modeladmin)
* [RestfulServer module](https://github.com/silverstripe/silverstripe-restfulserver)
* [Tutorial: Site Search](/tutorials/4-site-search)
* [SearchContext](/reference/searchcontext)

View File

@ -99,7 +99,6 @@ our theme in action. The code for mine is below.
<h1>$Title</h1>
$Content
$Form
$PageComments
All you have to do now is tell your site to use your new theme - This is defined in the **mysite/_config.php** file

View File

@ -207,7 +207,7 @@ that the *BrowserPollSubmission* table is created. Now we just need to define 'd
$submission = new BrowserPollSubmission();
$form->saveInto($submission);
$submission->write();
Director::redirectBack();
return $this->redirectBack();
}
}
@ -218,7 +218,7 @@ A function that processes a form submission takes two arguments - the first is t
In our function we create a new *BrowserPollSubmission* object. Since the name of our form fields and the name of the
database fields are the same we can save the form directly into the data object.
We call the 'write' method to write our data to the database, and 'Director::redirectBack()' will redirect the user back
We call the 'write' method to write our data to the database, and 'redirectBack()' will redirect the user back
to the home page.
@ -237,11 +237,8 @@ Change the end of the 'BrowserPollForm' function so it looks like this:
:::php
public function BrowserPollForm() {
...
// Create validator
// ...
$validator = new RequiredFields('Name', 'Browser');
return new Form($this, 'BrowserPollForm', $fields, $actions, $validator);
}
@ -266,22 +263,16 @@ First modify the 'doBrowserPoll' to set the session variable 'BrowserPollVoted'
*mysite/code/HomePage.php*
:::php
...
HomePage_Controller extends Page_Controller {
...
// ...
class HomePage_Controller extends Page_Controller {
// ...
public function doBrowserPoll($data, $form) {
$submission = new BrowserPollSubmission();
$form->saveInto($submission);
$submission->write();
Session::set('BrowserPollVoted', true);
Director::redirectBack();
return $this->redirectBack();
}
...
}
@ -293,59 +284,55 @@ it is.
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 (or if you're using Firefox and have installed the [Web
Developer](http://chrispederick.com/work/web-developer/) extension, you can use its Clear Session Cookies command).
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.
Although the form is not shown, you'll still see the 'Browser Poll' heading. We'll leave this for now: after we've built
the bar graph of the results, we'll modify the template to show the graph instead of the form if the user has already
voted.
Now that we're collecting data, it would be nice to show the results
on the website as well. We could simply output every vote, but that's boring.
Let's group the results by browser, through the SilverStripe data model.
We now need some way of getting the data from the database into the template.
In the [second tutorial](/tutorials/2-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 we got the latest news articles for the home page by using the 'DataObject::get' function. We
can't use the 'DataObject::get' function here directly as we wish to count the total number of votes for each browser.
By looking at the documentation for 'DataObject::get', we can see that it returns a `[api:DataObjectSet]`
object. In fact, all data that can be iterated over in a template with a page control is contained in a DataObjectSet.
A `[api:DataObjectSet]` is a set of not just DataObjects, but of ViewableData, which the majority of
SilverStripe's classes (including DataObject) inherit from. We can create a DataObjectSet, fill it with our data, and
then create our graph using a page control in the template. Create the function 'BrowserPollResults' on the
*HomePage_Controller* class.
Create the function 'BrowserPollResults' on the *HomePage_Controller* class.
** mysite/code/HomePage.php **
:::php
public function BrowserPollResults() {
$submissions = BrowserPollSubmission::get();
$submissions = new GroupedList(BrowserPollSubmission::get());
$total = $submissions->Count();
$doSet = new DataObjectSet();
foreach($submissions->groupBy('Browser') as $browser => $data) {
$list = new ArrayList();
foreach($submissions->groupBy('Browser') as $browserName => $browserSubmissions) {
$percentage = (int) ($data->Count() / $total * 100);
$record = array(
'Browser' => $browser,
$list->push(new ArrayData(array(
'Browser' => $browserName,
'Percentage' => $percentage
);
$doSet->push(new ArrayData($record));
)));
}
return $doSet;
return $list;
}
This 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
$submissions = BrowserPollSubmission::get();
$submissions = new GroupedList(BrowserPollSubmission::get());
First we get all of the *BrowserPollSubmission*s from the database. This returns the submissions as a
DataObjectSet, which contains the submissions as *BrowserPollSubmission* objects.
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();
@ -354,29 +341,24 @@ DataObjectSet, which contains the submissions as *BrowserPollSubmission* objects
We get the total number of submissions, which is needed to calculate the percentages.
:::php
$doSet = new DataObjectSet();
foreach($submissions->groupBy('Browser') as $browser => $data) {
$percentage = (int) ($data->Count() / $total * 100);
$record = array(
'Browser' => $browser,
$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
);
$doSet->push(new ArrayData($record));
)));
}
Now we create an empty DataObjectSet to hold our data and then iterate over the 'Browser' submissions field. The 'groupBy'
method of DataObjectSet splits our DataObjectSet by the 'Browser' field passed to it. The percentage of submissions for each
browser is calculated using the size of the DataObjectSet. It puts these new DataObjectSets into an array indexed
by the value of the field. The `[api:ArrayData]` class wraps an array into a ViewableData object, so we finally create a new
ArrayData object, which we can add to our *$doSet* DataObjectSet of results.
:::php
return $doSet;
After we have iterated through all the browsers, the DataObjectSet contains all the results, which is then
returned.
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 iterate over the 'Browser' submissions field.
The `groupBy()` method splits our list by the 'Browser' field passed to it,
creating new lists with submissions just for a specific browser.
Each of those lists is keyed by the browser name.
The aggregated result is then contained in an `[api:ArrayData]` object,
which behaves much like a standard PHP array, but allows us to use it in SilverStripe templates.
The final step is to create the template to display our data. Change the 'BrowserPoll' div in
*themes/tutorial/templates/Layout/HomePage.ss* to the below.
@ -408,6 +390,9 @@ a complete poll.
![](_images/pollresults.jpg)
<div class="hint" markdown="1">
While the ORM is
</div>
## Summary

View File

@ -98,7 +98,7 @@ function, and then attempt to render it with *Page_results.ss*, falling back to
## Creating the template
Lastly we need the template for the search page. This template uses all the same techniques used in previous
tutorials. It also uses a number of pagination variables, which are provided by the `[api:DataObjectSet]`
tutorials. It also uses a number of pagination variables, which are provided by the `[api:PaginatedList]`
class.
*themes/simple/templates/Layout/Page_results.ss*

View File

@ -214,7 +214,7 @@ To use your *HasOneComplexTableField* table for a **1-to-1** relation, make this
$tablefield->setOneToOne();
$fields->addFieldToTab( 'Root.Content.Student', $tablefield );
$fields->addFieldToTab( 'Root.Student', $tablefield );
return $fields;
}