mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-09-19 16:06:32 +02:00
5c9044a007
API Introduce HTMLFragment as casting helper for HTMLText with shortcodes disabled API Introduce DBField::CDATA for XML file value encoding API RSSFeed now casts from the underlying model rather than by override API Introduce CustomMethods::getExtraMethodConfig() to allow metadata to be queried BUG Remove _call hack from VirtualPage API Remove FormField::$dontEscape API Introduce HTMLReadonlyField for non-editable readonly HTML API FormField::Field() now returns string in many cases rather than DBField instance. API Remove redundant *_val methods from ViewableData API ViewableData::obj() no longer has a $forceReturnObject parameter as it always returns an object BUG Fix issue with ViewableData caching incorrect field values after being modified. API Remove deprecated DB class methods API Enforce plain text left/right formfield titles
285 lines
6.6 KiB
PHP
285 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\ORM\FieldType;
|
|
|
|
use Object;
|
|
use SilverStripe\ORM\DataObject;
|
|
use SilverStripe\ORM\DB;
|
|
use SilverStripe\ORM\Queries\SQLSelect;
|
|
|
|
|
|
/**
|
|
* Apply this interface to any {@link DBField} that doesn't have a 1-1 mapping with a database field.
|
|
* This includes multi-value fields and transformed fields
|
|
*
|
|
* @todo Unittests for loading and saving composite values (see GIS module for existing similiar unittests)
|
|
*
|
|
* Example with a combined street name and number:
|
|
* <code>
|
|
* class Street extends DBComposite {
|
|
* private static $composite_db = return array(
|
|
* "Number" => "Int",
|
|
* "Name" => "Text"
|
|
* );
|
|
* }
|
|
* </code>
|
|
*
|
|
* @package framework
|
|
* @subpackage orm
|
|
*/
|
|
abstract class DBComposite extends DBField {
|
|
|
|
/**
|
|
* Similiar to {@link DataObject::$db},
|
|
* holds an array of composite field names.
|
|
* Don't include the fields "main name",
|
|
* it will be prefixed in {@link requireField()}.
|
|
*
|
|
* @config
|
|
* @var array
|
|
*/
|
|
private static $composite_db = array();
|
|
|
|
/**
|
|
* Either the parent dataobject link, or a record of saved values for each field
|
|
*
|
|
* @var array|DataObject
|
|
*/
|
|
protected $record = array();
|
|
|
|
/**
|
|
* Write all nested fields into a manipulation
|
|
*
|
|
* @param array $manipulation
|
|
*/
|
|
public function writeToManipulation(&$manipulation) {
|
|
foreach($this->compositeDatabaseFields() as $field => $spec) {
|
|
// Write sub-manipulation
|
|
$fieldObject = $this->dbObject($field);
|
|
$fieldObject->writeToManipulation($manipulation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add all columns which are defined through {@link requireField()}
|
|
* and {@link $composite_db}, or any additional SQL that is required
|
|
* to get to these columns. Will mostly just write to the {@link SQLSelect->select}
|
|
* array.
|
|
*
|
|
* @param SQLSelect $query
|
|
*/
|
|
public function addToQuery(&$query) {
|
|
parent::addToQuery($query);
|
|
|
|
foreach($this->compositeDatabaseFields() as $field => $spec) {
|
|
$table = $this->getTable();
|
|
$key = $this->getName() . $field;
|
|
if($table) {
|
|
$query->selectField("\"{$table}\".\"{$key}\"");
|
|
} else {
|
|
$query->selectField("\"{$key}\"");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return array in the format of {@link $composite_db}.
|
|
* Used by {@link DataObject->hasOwnDatabaseField()}.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function compositeDatabaseFields() {
|
|
return $this->config()->composite_db;
|
|
}
|
|
|
|
|
|
public function isChanged() {
|
|
// When unbound, use the local changed flag
|
|
if(! ($this->record instanceof DataObject) ) {
|
|
return $this->isChanged;
|
|
}
|
|
|
|
// Defer to parent record
|
|
foreach($this->compositeDatabaseFields() as $field => $spec) {
|
|
$key = $this->getName() . $field;
|
|
if($this->record->isChanged($key)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Composite field defaults to exists only if all fields have values
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function exists() {
|
|
// By default all fields
|
|
foreach($this->compositeDatabaseFields() as $field => $spec) {
|
|
$fieldObject = $this->dbObject($field);
|
|
if(!$fieldObject->exists()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function requireField() {
|
|
foreach($this->compositeDatabaseFields() as $field => $spec){
|
|
$key = $this->getName() . $field;
|
|
DB::require_field($this->tableName, $key, $spec);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assign the given value.
|
|
* If $record is assigned to a dataobject, this field becomes a loose wrapper over
|
|
* the records on that object instead.
|
|
*
|
|
* {@see ViewableData::obj}
|
|
*
|
|
* @param mixed $value
|
|
* @param mixed $record Parent object to this field, which could be a DataObject, record array, or other
|
|
* @param bool $markChanged
|
|
*/
|
|
public function setValue($value, $record = null, $markChanged = true) {
|
|
$this->isChanged = $markChanged;
|
|
|
|
// When given a dataobject, bind this field to that
|
|
if($record instanceof DataObject) {
|
|
$this->bindTo($record);
|
|
$record = null;
|
|
}
|
|
|
|
foreach($this->compositeDatabaseFields() as $field => $spec) {
|
|
// Check value
|
|
if($value instanceof DBComposite) {
|
|
// Check if saving from another composite field
|
|
$this->setField($field, $value->getField($field));
|
|
|
|
} elseif(isset($value[$field])) {
|
|
// Check if saving from an array
|
|
$this->setField($field, $value[$field]);
|
|
}
|
|
|
|
// Load from $record
|
|
$key = $this->getName() . $field;
|
|
if(is_array($record) && isset($record[$key])) {
|
|
$this->setField($field, $record[$key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bind this field to the dataobject, and set the underlying table to that of the owner
|
|
*
|
|
* @param DataObject $dataObject
|
|
*/
|
|
public function bindTo($dataObject) {
|
|
$this->record = $dataObject;
|
|
}
|
|
|
|
public function saveInto($dataObject) {
|
|
foreach($this->compositeDatabaseFields() as $field => $spec) {
|
|
// Save into record
|
|
$key = $this->getName() . $field;
|
|
$dataObject->setField($key, $this->getField($field));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* get value of a single composite field
|
|
*
|
|
* @param string $field
|
|
* @return mixed
|
|
*/
|
|
public function getField($field) {
|
|
// Skip invalid fields
|
|
$fields = $this->compositeDatabaseFields();
|
|
if(!isset($fields[$field])) {
|
|
return null;
|
|
}
|
|
|
|
// Check bound object
|
|
if($this->record instanceof DataObject) {
|
|
$key = $this->getName().$field;
|
|
return $this->record->getField($key);
|
|
}
|
|
|
|
// Check local record
|
|
if(isset($this->record[$field])) {
|
|
return $this->record[$field];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function hasField($field) {
|
|
$fields = $this->compositeDatabaseFields();
|
|
return isset($fields[$field]);
|
|
}
|
|
|
|
/**
|
|
* Set value of a single composite field
|
|
*
|
|
* @param string $field
|
|
* @param mixed $value
|
|
* @param bool $markChanged
|
|
* @return $this
|
|
*/
|
|
public function setField($field, $value, $markChanged = true) {
|
|
$this->objCacheClear();
|
|
|
|
// Skip non-db fields
|
|
if(!$this->hasField($field)) {
|
|
return $this;
|
|
}
|
|
|
|
// Set changed
|
|
if($markChanged) {
|
|
$this->isChanged = true;
|
|
}
|
|
|
|
// Set bound object
|
|
if($this->record instanceof DataObject) {
|
|
$key = $this->getName() . $field;
|
|
$this->record->setField($key, $value);
|
|
return;
|
|
}
|
|
|
|
// Set local record
|
|
$this->record[$field] = $value;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get a db object for the named field
|
|
*
|
|
* @param string $field Field name
|
|
* @return DBField|null
|
|
*/
|
|
public function dbObject($field) {
|
|
$fields = $this->compositeDatabaseFields();
|
|
if(!isset($fields[$field])) {
|
|
return null;
|
|
}
|
|
|
|
// Build nested field
|
|
$key = $this->getName() . $field;
|
|
$spec = $fields[$field];
|
|
$fieldObject = Object::create_from_string($spec, $key);
|
|
$fieldObject->setValue($this->getField($field), null, false);
|
|
return $fieldObject;
|
|
}
|
|
|
|
public function castingHelper($field) {
|
|
$fields = $this->compositeDatabaseFields();
|
|
if(isset($fields[$field])) {
|
|
return $fields[$field];
|
|
}
|
|
|
|
return parent::castingHelper($field);
|
|
}
|
|
|
|
}
|