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
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
* Each database-table maps to a PHP class.
|
||||
* Each database-row maps to a PHP object.
|
||||
* Each database-column maps to a property on a PHP object.
|
||||
* Each database table maps to a PHP class.
|
||||
* Each database row maps to a PHP object.
|
||||
* Each database column maps to a property on a PHP object.
|
||||
|
||||
All data tables in SilverStripe are defined as subclasses of [api:DataObject]. The [api:DataObject] class represents a
|
||||
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
|
||||
|
||||
After adding, modifying or removing `DataObject` classes make sure to rebuild your SilverStripe database. The
|
||||
database-schema is generated automatically by visiting the URL http://www.yoursite.com/dev/build.
|
||||
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 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
|
||||
as required.
|
||||
@ -54,9 +54,9 @@ It will perform the following changes:
|
||||
|
||||
It **won't** do any of the following
|
||||
|
||||
* Deleting tables
|
||||
* Deleting fields
|
||||
* Rename any tables that it doesn't recognize - so other applications can co-exist in the same database, as long as
|
||||
* Delete tables
|
||||
* Delete fields
|
||||
* 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.
|
||||
|
||||
|
||||
@ -117,6 +117,11 @@ Or, a better way is to use the `create` method.
|
||||
:::php
|
||||
$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
|
||||
of the values through a custom `__set()` method.
|
||||
|
||||
@ -124,12 +129,18 @@ of the values through a custom `__set()` method.
|
||||
$player->FirstName = "Sam";
|
||||
$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.
|
||||
|
||||
:::php
|
||||
$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
|
||||
|
||||
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 %>`.
|
||||
|
||||
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
|
||||
|
||||
:::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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
## 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'
|
||||
|
||||
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
|
||||
$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()`.
|
||||
|
||||
<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>
|
||||
|
||||
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
|
||||
|
||||
Occasionally, the system described above won't let you do exactly what you need to do. In these situations, we have
|
||||
methods that manipulate the SQL query at a lower level. When using these, please ensure that all table & field names
|
||||
are escaped with double quotes, otherwise some DB back-ends (e.g. PostgreSQL) won't work.
|
||||
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 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
|
||||
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).
|
||||
|
||||
* 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:
|
||||
record #2 in Page refers to the same object as record #2 in `[api:SiteTree]`.
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
|
||||
@ -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
|
||||
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
|
||||
$player = Player::get()->byId(1);
|
||||
|
||||
@ -59,8 +61,7 @@ The relationship can also be navigated in [templates](../templates).
|
||||
|
||||
## has_many
|
||||
|
||||
Defines 1-to-many joins. A database-column named ""`<relationship-name>`ID"" will to be created in the child-class. As
|
||||
you can see from the previous example, `$has_many` goes hand in hand with `$has_one`.
|
||||
Defines 1-to-many joins. As you can see from the previous example, `$has_many` goes hand in hand with `$has_one`.
|
||||
|
||||
<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
|
||||
@ -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.
|
||||
|
||||
:::php
|
||||
@ -203,6 +204,11 @@ The relationship can also be navigated in [templates](../templates).
|
||||
<% end_if %>
|
||||
<% 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 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
|
||||
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
|
||||
|
||||
@ -9,7 +9,7 @@ modify.
|
||||
|
||||
## 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
|
||||
$members = Member::get();
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
|
||||
@ -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.
|
||||
|
||||
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).
|
||||
|
||||
|
@ -12,7 +12,7 @@ data records.
|
||||
|
||||
## 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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
$players = Player::get()->filter(array(
|
||||
|
@ -3,7 +3,7 @@ summary: Add versioning to your database content through the Versioned extension
|
||||
|
||||
# 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.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
to force a specific stage, we recommend the `Controller->init()` method for this purpose.
|
||||
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, for example:
|
||||
|
||||
**mysite/code/MyController.php**
|
||||
:::php
|
||||
public function init() {
|
||||
parent::init();
|
||||
Versioned::set_reading_mode('Stage.Stage');
|
||||
}
|
||||
|
||||
|
||||
### Controllers
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
|
||||
@ -24,7 +24,7 @@ An example is `DataObject`, SilverStripe will automatically create your CMS inte
|
||||
);
|
||||
|
||||
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->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
|
||||
[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 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.
|
||||
|
||||
:::php
|
||||
|
Loading…
x
Reference in New Issue
Block a user