mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
developer guides / models
This commit is contained in:
parent
30a85be38e
commit
09be3352a0
@ -1,14 +1,14 @@
|
|||||||
title: Introduction to the Data Model and ORM
|
title: Introduction to the Data Model and ORM
|
||||||
summary: Introduction to creating and querying a Data Model through the ORM.
|
summary: Introduction to creating and querying a database records through the ORM (object-relational model)
|
||||||
|
|
||||||
# Introduction to the Data Model and ORM
|
# Introduction to the Data Model and ORM
|
||||||
|
|
||||||
SilverStripe uses an [object-relational model](http://en.wikipedia.org/wiki/Object-relational_model) to represent it's
|
SilverStripe uses an [object-relational model](http://en.wikipedia.org/wiki/Object-relational_model) to represent its
|
||||||
information.
|
information.
|
||||||
|
|
||||||
* Each database-table maps to a PHP class.
|
* Each database table maps to a PHP class.
|
||||||
* Each database-row maps to a PHP object.
|
* Each database row maps to a PHP object.
|
||||||
* Each database-column maps to a property on a PHP object.
|
* Each database column maps to a property on a PHP object.
|
||||||
|
|
||||||
All data tables in SilverStripe are defined as subclasses of [api:DataObject]. The [api:DataObject] class represents a
|
All data tables in SilverStripe are defined as subclasses of [api:DataObject]. The [api:DataObject] class represents a
|
||||||
single row in a database table, following the ["Active Record"](http://en.wikipedia.org/wiki/Active_record_pattern)
|
single row in a database table, following the ["Active Record"](http://en.wikipedia.org/wiki/Active_record_pattern)
|
||||||
@ -38,8 +38,8 @@ so on. After writing this class, we need to regenerate the database schema.
|
|||||||
|
|
||||||
## Generating the Database Schema
|
## Generating the Database Schema
|
||||||
|
|
||||||
After adding, modifying or removing `DataObject` classes make sure to rebuild your SilverStripe database. The
|
After adding, modifying or removing `DataObject` subclasses, make sure to rebuild your SilverStripe database. The
|
||||||
database-schema is generated automatically by visiting the URL http://www.yoursite.com/dev/build.
|
database schema is generated automatically by visiting the URL http://www.yoursite.com/dev/build while authenticated as an administrator.
|
||||||
|
|
||||||
This script will analyze the existing schema, compare it to what's required by your data classes, and alter the schema
|
This script will analyze the existing schema, compare it to what's required by your data classes, and alter the schema
|
||||||
as required.
|
as required.
|
||||||
@ -54,9 +54,9 @@ It will perform the following changes:
|
|||||||
|
|
||||||
It **won't** do any of the following
|
It **won't** do any of the following
|
||||||
|
|
||||||
* Deleting tables
|
* Delete tables
|
||||||
* Deleting fields
|
* Delete fields
|
||||||
* Rename any tables that it doesn't recognize - so other applications can co-exist in the same database, as long as
|
* Rename any tables that it doesn't recognize. This allows other applications to coexist in the same database, as long as
|
||||||
their table names don't match a SilverStripe data class.
|
their table names don't match a SilverStripe data class.
|
||||||
|
|
||||||
|
|
||||||
@ -117,6 +117,11 @@ Or, a better way is to use the `create` method.
|
|||||||
:::php
|
:::php
|
||||||
$player = Player::create();
|
$player = Player::create();
|
||||||
|
|
||||||
|
<div class="notice" markdown='1'>
|
||||||
|
Using the `create()` method provides chainability, which can create add elegance and brevity to your code, e.g. `Player::create()->write()`. More importantly, however, it will look up the class in the [Injector](../extending/injector) so that can the class can be overriden by [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection).
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
Database columns and properties can be set as class properties on the object. The SilverStripe ORM handles the saving
|
Database columns and properties can be set as class properties on the object. The SilverStripe ORM handles the saving
|
||||||
of the values through a custom `__set()` method.
|
of the values through a custom `__set()` method.
|
||||||
|
|
||||||
@ -124,12 +129,18 @@ of the values through a custom `__set()` method.
|
|||||||
$player->FirstName = "Sam";
|
$player->FirstName = "Sam";
|
||||||
$player->PlayerNumber = 07;
|
$player->PlayerNumber = 07;
|
||||||
|
|
||||||
To save the `DataObject` to the database use the `write()` method. The first time `write()` is called an `ID` will be
|
To save the `DataObject` to the database, use the `write()` method. The first time `write()` is called, an `ID` will be
|
||||||
set.
|
set.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$player->write();
|
$player->write();
|
||||||
|
|
||||||
|
For convenience, the `write()` method returns the record's ID. This is particularly useful when creating new records.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$player = Player::create();
|
||||||
|
$id = $player->write();
|
||||||
|
|
||||||
## Querying Data
|
## Querying Data
|
||||||
|
|
||||||
With the `Player` class defined we can query our data using the `ORM` or Object-Relational Model. The `ORM` provides
|
With the `Player` class defined we can query our data using the `ORM` or Object-Relational Model. The `ORM` provides
|
||||||
@ -166,7 +177,7 @@ Provided `filter` values are automatically escaped and do not require any escapi
|
|||||||
|
|
||||||
The `ORM` doesn't actually execute the [api:SQLQuery] until you iterate on the result with a `foreach()` or `<% loop %>`.
|
The `ORM` doesn't actually execute the [api:SQLQuery] until you iterate on the result with a `foreach()` or `<% loop %>`.
|
||||||
|
|
||||||
It's smart enough to generate a single efficient query at the last moment in time without needing to post process the
|
It's smart enough to generate a single efficient query at the last moment in time without needing to post-process the
|
||||||
result set in PHP. In `MySQL` the query generated by the ORM may look something like this
|
result set in PHP. In `MySQL` the query generated by the ORM may look something like this
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
@ -202,6 +213,15 @@ This also means that getting the count of a list of objects will be done with a
|
|||||||
echo $player->FirstName;
|
echo $player->FirstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Notice that we can step into the loop safely without having to check if `$players` exists. The `get()` call is robust, and will at worst return an empty `DataList` object. If you do want to check if the query returned any records, you can use the `exists()` method, e.g.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$players = Player::get();
|
||||||
|
|
||||||
|
if($players->exists()) {
|
||||||
|
// do something here
|
||||||
|
}
|
||||||
|
|
||||||
See the [Lists](../lists) documentation for more information on dealing with [api:SS_List] instances.
|
See the [Lists](../lists) documentation for more information on dealing with [api:SS_List] instances.
|
||||||
|
|
||||||
## Returning a single DataObject
|
## Returning a single DataObject
|
||||||
@ -279,7 +299,7 @@ So, this would return only those players called "Sam Minnée".
|
|||||||
|
|
||||||
// SELECT * FROM Player WHERE FirstName = 'Sam' AND LastName = 'Minnée'
|
// SELECT * FROM Player WHERE FirstName = 'Sam' AND LastName = 'Minnée'
|
||||||
|
|
||||||
There is also a short hand way of getting Players with the FirstName of Sam.
|
There is also a shorthand way of getting Players with the FirstName of Sam.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$players = Player::get()->filter('FirstName', 'Sam');
|
$players = Player::get()->filter('FirstName', 'Sam');
|
||||||
@ -342,7 +362,9 @@ It is also possible to filter by a PHP callback, this will force the data model
|
|||||||
PHP, thus `filter()` or `filterAny()` are to be preferred over `filterByCallback()`.
|
PHP, thus `filter()` or `filterAny()` are to be preferred over `filterByCallback()`.
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
Because `filterByCallback()` has to run in PHP, it will always return an `ArrayList`
|
Because `filterByCallback()` has to run in PHP, it has a significant performance tradeoff, and should not be used on large recordsets.
|
||||||
|
|
||||||
|
`filterByCallback()` will always return an `ArrayList`.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The first parameter to the callback is the item, the second parameter is the list itself. The callback will run once
|
The first parameter to the callback is the item, the second parameter is the list itself. The callback will run once
|
||||||
@ -436,8 +458,8 @@ Note that the `limit` argument order is different from a MySQL LIMIT clause.
|
|||||||
### Raw SQL
|
### Raw SQL
|
||||||
|
|
||||||
Occasionally, the system described above won't let you do exactly what you need to do. In these situations, we have
|
Occasionally, the system described above won't let you do exactly what you need to do. In these situations, we have
|
||||||
methods that manipulate the SQL query at a lower level. When using these, please ensure that all table & field names
|
methods that manipulate the SQL query at a lower level. When using these, please ensure that all table and field names
|
||||||
are escaped with double quotes, otherwise some DB back-ends (e.g. PostgreSQL) won't work.
|
are escaped with double quotes, otherwise some DB backends (e.g. PostgreSQL) won't work.
|
||||||
|
|
||||||
Under the hood, query generation is handled by the `[api:DataQuery]` class. This class does provide more direct access
|
Under the hood, query generation is handled by the `[api:DataQuery]` class. This class does provide more direct access
|
||||||
to certain SQL features that `DataList` abstracts away from you.
|
to certain SQL features that `DataList` abstracts away from you.
|
||||||
@ -548,7 +570,7 @@ special fields. This is called the "base table". In our case, `SiteTree` is the
|
|||||||
sub-classes of the base class (including the base class itself).
|
sub-classes of the base class (including the base class itself).
|
||||||
|
|
||||||
* Each sub-class of the base object will also be given its own table, *as long as it has custom fields*. In the
|
* Each sub-class of the base object will also be given its own table, *as long as it has custom fields*. In the
|
||||||
example above, NewsSection didn't have its own data and so an extra table would be redundant.
|
example above, NewsSection didn't have its own data, so an extra table would be redundant.
|
||||||
|
|
||||||
* In all the tables, ID is the primary key. A matching ID number is used for all parts of a particular record:
|
* In all the tables, ID is the primary key. A matching ID number is used for all parts of a particular record:
|
||||||
record #2 in Page refers to the same object as record #2 in `[api:SiteTree]`.
|
record #2 in Page refers to the same object as record #2 in `[api:SiteTree]`.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
title: Relations between Records
|
title: Relations between Records
|
||||||
summary: Relate models together using the ORM.
|
summary: Relate models together using the ORM using has_one, has_many, and many_many.
|
||||||
|
|
||||||
# Relations between Records
|
# Relations between Records
|
||||||
|
|
||||||
@ -39,6 +39,8 @@ A 1-to-1 relation creates a database-column called "`<relationship-name>`ID", in
|
|||||||
This defines a relationship called `Team` which links to a `Team` class. The `ORM` handles navigating the relationship
|
This defines a relationship called `Team` which links to a `Team` class. The `ORM` handles navigating the relationship
|
||||||
and provides a short syntax for accessing the related object.
|
and provides a short syntax for accessing the related object.
|
||||||
|
|
||||||
|
At the database level, the `has_one` creates a `TeamID` field on `Player`. A `has_many` field does not impose any database changes. It merely injects a new method into the class to access the related records (in this case, `Players()`)
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$player = Player::get()->byId(1);
|
$player = Player::get()->byId(1);
|
||||||
|
|
||||||
@ -59,8 +61,7 @@ The relationship can also be navigated in [templates](../templates).
|
|||||||
|
|
||||||
## has_many
|
## has_many
|
||||||
|
|
||||||
Defines 1-to-many joins. A database-column named ""`<relationship-name>`ID"" will to be created in the child-class. As
|
Defines 1-to-many joins. As you can see from the previous example, `$has_many` goes hand in hand with `$has_one`.
|
||||||
you can see from the previous example, `$has_many` goes hand in hand with `$has_one`.
|
|
||||||
|
|
||||||
<div class="alert" markdown='1'>
|
<div class="alert" markdown='1'>
|
||||||
Please specify a $has_one-relationship on the related child-class as well, in order to have the necessary accessors
|
Please specify a $has_one-relationship on the related child-class as well, in order to have the necessary accessors
|
||||||
@ -184,7 +185,7 @@ available on both ends.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Much like the `has_one` relationship, `mant_many` can be navigated through the `ORM` as well. The only difference being
|
Much like the `has_one` relationship, `many_many` can be navigated through the `ORM` as well. The only difference being
|
||||||
you will get an instance of [api:ManyManyList] rather than the object.
|
you will get an instance of [api:ManyManyList] rather than the object.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
@ -203,6 +204,11 @@ The relationship can also be navigated in [templates](../templates).
|
|||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
|
|
||||||
|
## many_many or belongs_many_many?
|
||||||
|
|
||||||
|
If you're unsure about whether an object should take on `many_many` or `belongs_many_many`, the best way to think about it is that the object where the relationship will be edited (i.e. via checkboxes) should contain the `many_many`. For instance, in a `many_many` of Product => Categories, the `Product` should contain the `many_many`, because it is much more likely that the user will select Categories for a Product than vice-versa.
|
||||||
|
|
||||||
|
|
||||||
## Adding relations
|
## Adding relations
|
||||||
|
|
||||||
Adding new items to a relations works the same, regardless if you're editing a **has_many** or a **many_many**. They are
|
Adding new items to a relations works the same, regardless if you're editing a **has_many** or a **many_many**. They are
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
title: Managing Lists
|
title: Managing Lists
|
||||||
summary: Learn how to manipulate SS_List objects.
|
summary: The SS_List interface allows you to iterate through and manipulate a list of objects.
|
||||||
|
|
||||||
# Managing Lists
|
# Managing Lists
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ modify.
|
|||||||
|
|
||||||
## Iterating over the list.
|
## Iterating over the list.
|
||||||
|
|
||||||
[api:SS_List] implements `IteratorAggregate` allowing you to loop over the instance.
|
[api:SS_List] implements `IteratorAggregate`, allowing you to loop over the instance.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$members = Member::get();
|
$members = Member::get();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
title: Data Types, Overloading and Casting
|
title: Data Types, Overloading and Casting
|
||||||
summary: Documentation on how data is stored going in, coming out of the ORM and how to modify it.
|
summary: Learn how how data is stored going in and coming out of the ORM and how to modify it.
|
||||||
|
|
||||||
# Data Types and Casting
|
# Data Types and Casting
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ Each model in a SilverStripe [api:DataObject] will handle data at some point. Th
|
|||||||
the ones defined in a `$db` array or simply a method that returns data for the template.
|
the ones defined in a `$db` array or simply a method that returns data for the template.
|
||||||
|
|
||||||
A Data Type is represented in SilverStripe by a [api:DBField] subclass. The class is responsible for telling the ORM
|
A Data Type is represented in SilverStripe by a [api:DBField] subclass. The class is responsible for telling the ORM
|
||||||
about how to store it's data in the database and how to format the information coming out of the database.
|
about how to store its data in the database and how to format the information coming out of the database, i.e. on a template.
|
||||||
|
|
||||||
In the `Player` example, we have four database columns each with a different data type (Int, Varchar).
|
In the `Player` example, we have four database columns each with a different data type (Int, Varchar).
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ data records.
|
|||||||
|
|
||||||
## onBeforeWrite
|
## onBeforeWrite
|
||||||
|
|
||||||
You can customize saving-behaviour for each DataObject, e.g. for adding workflow or data customization. The function is
|
You can customize saving-behavior for each DataObject, e.g. for adding workflow or data customization. The function is
|
||||||
triggered when calling *write()* to save the object to the database. This includes saving a page in the CMS or altering
|
triggered when calling *write()* to save the object to the database. This includes saving a page in the CMS or altering
|
||||||
a `ModelAdmin` record.
|
a `ModelAdmin` record.
|
||||||
|
|
||||||
|
@ -32,11 +32,11 @@ An example of a `SearchFilter` in use:
|
|||||||
Developers can define their own [api:SearchFilter] if needing to extend the ORM filter and exclude behaviors.
|
Developers can define their own [api:SearchFilter] if needing to extend the ORM filter and exclude behaviors.
|
||||||
|
|
||||||
These suffixes can also take modifiers themselves. The modifiers currently supported are `":not"`, `":nocase"` and
|
These suffixes can also take modifiers themselves. The modifiers currently supported are `":not"`, `":nocase"` and
|
||||||
`":case"`. These negate the filter, make it case-insensitive and make it case-sensitive respectively. The default
|
`":case"`. These negate the filter, make it case-insensitive and make it case-sensitive, respectively. The default
|
||||||
comparison uses the database's default. For MySQL and MSSQL, this is case-insensitive. For PostgreSQL, this is
|
comparison uses the database's default. For MySQL and MSSQL, this is case-insensitive. For PostgreSQL, this is
|
||||||
case-sensitive.
|
case-sensitive.
|
||||||
|
|
||||||
The following is a query which will return everyone whose first name starts with S either lower or uppercase
|
The following is a query which will return everyone whose first name starts with "S", either lowercase or uppercase:
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$players = Player::get()->filter(array(
|
$players = Player::get()->filter(array(
|
||||||
|
@ -3,7 +3,7 @@ summary: Add versioning to your database content through the Versioned extension
|
|||||||
|
|
||||||
# Versioning
|
# Versioning
|
||||||
|
|
||||||
Database content in SilverStripe can be "staged" before its publication, as well as tracking all changes through the
|
Database content in SilverStripe can be "staged" before its publication, as well as track all changes through the
|
||||||
lifetime of a database record.
|
lifetime of a database record.
|
||||||
|
|
||||||
It is most commonly applied to pages in the CMS (the `SiteTree` class). Draft content edited in the CMS can be different
|
It is most commonly applied to pages in the CMS (the `SiteTree` class). Draft content edited in the CMS can be different
|
||||||
@ -148,8 +148,16 @@ helpers.
|
|||||||
### Templates Variables
|
### Templates Variables
|
||||||
|
|
||||||
In templates, you don't need to worry about this distinction. The `$Content` variable contain the published content by
|
In templates, you don't need to worry about this distinction. The `$Content` variable contain the published content by
|
||||||
default, and only preview draft content if explicitly requested (e.g. by the "preview" feature in the CMS). If you want
|
default, and only preview draft content if explicitly requested (e.g. by the "preview" feature in the CMS, or by adding ?stage=Stage to the URL). If you want
|
||||||
to force a specific stage, we recommend the `Controller->init()` method for this purpose.
|
to force a specific stage, we recommend the `Controller->init()` method for this purpose, for example:
|
||||||
|
|
||||||
|
**mysite/code/MyController.php**
|
||||||
|
:::php
|
||||||
|
public function init() {
|
||||||
|
parent::init();
|
||||||
|
Versioned::set_reading_mode('Stage.Stage');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
### Controllers
|
### Controllers
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
title: Building Model and Search Interfaces around Scaffolding
|
title: Building Model and Search Interfaces around Scaffolding
|
||||||
summary: Model Driven approach to defining your application UI.
|
summary: A Model-driven approach to defining your application UI.
|
||||||
|
|
||||||
# Scaffolding
|
# Scaffolding
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ An example is `DataObject`, SilverStripe will automatically create your CMS inte
|
|||||||
);
|
);
|
||||||
|
|
||||||
public function getCMSFields() {
|
public function getCMSFields() {
|
||||||
// parent::getCMSFields() does all the hard work and creates the fields for title, isactive and content.
|
// parent::getCMSFields() does all the hard work and creates the fields for Title, IsActive and Content.
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
$fields->fieldByName('IsActive')->setTitle('Is active?');
|
$fields->fieldByName('IsActive')->setTitle('Is active?');
|
||||||
|
|
||||||
@ -32,6 +32,26 @@ An example is `DataObject`, SilverStripe will automatically create your CMS inte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
To fully customize your form fields, start with an empty FieldList.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
public function getCMSFields() {
|
||||||
|
$fields = FieldList::create(
|
||||||
|
TabSet::create("Root",
|
||||||
|
CheckboxSetField::create('IsActive','Is active?'),
|
||||||
|
TextField::create('Title'),
|
||||||
|
TextareaField::create('Content')
|
||||||
|
->setRows(5)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You can also alter the fields of built-in and module `DataObject` classes through your own
|
You can also alter the fields of built-in and module `DataObject` classes through your own
|
||||||
[DataExtension](../extensions), and a call to `DataExtension->updateCMSFields`.
|
[DataExtension](../extensions), and a call to `DataExtension->updateCMSFields`.
|
||||||
|
|
||||||
@ -125,7 +145,7 @@ To include relations (`$has_one`, `$has_many` and `$many_many`) in your search,
|
|||||||
|
|
||||||
### Summary Fields
|
### Summary Fields
|
||||||
|
|
||||||
Summary fields can be used to show a quick overview of the data for a specific [api:DataObject] record. Most common use
|
Summary fields can be used to show a quick overview of the data for a specific [api:DataObject] record. The most common use
|
||||||
is their display as table columns, e.g. in the search results of a `[api:ModelAdmin]` CMS interface.
|
is their display as table columns, e.g. in the search results of a `[api:ModelAdmin]` CMS interface.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
|
Loading…
x
Reference in New Issue
Block a user