Newer
Older
<?php
/**
* SimpleORMap.class.php
* simple object-relational mapping
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author André Noack <noack@data-quest.de>
* @copyright 2010 Stud.IP Core-Group
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
*/
class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate
{

Marcus Eibrink-Lunzenauer
committed
/**
* Defines `_` as character used when joining composite primary keys.
*/
const ID_SEPARATOR = '_';
/**
* table row data
* @var array $content
*/
protected $content = [];
/**
* table row data
* @var array $content_db
*/
protected $content_db = [];
/**
* new state of entry
* @var boolean $is_new
*/
protected $is_new = true;
/**
* deleted state of entry
* @var boolean $is_deleted
*/
protected $is_deleted = false;
/**
* db table metadata
* @var ?array $schemes;
*/
public static $schemes = null;
* configuration data for subclasses
* @see self::configure()
* @var array $config;
protected static $config = [];
* stores instantiated related objects
* @var array $relations
protected $relations = [];
* assoc array for storing values for additional fields
*
* @var array $additional_data
protected $additional_data = [];
* reserved indentifiers, fields with those names must not have an explicit getXXX() method
* @var array $reserved_slots
protected static $reserved_slots = ['value','newid','iterator','tablemetadata', 'relationvalue','wherequery','relationoptions','data','new','id'];
* indicator for batch operations in findEachBySQL
*
* @var bool $performs_batch_operation
protected static $performs_batch_operation = false;
/**
* name of db table
* @return string
protected static function db_table()
{
return static::config('db_table');
}
* table columns
* @return array
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
protected static function db_fields()
{
return static::config('db_fields');
}
/**
* primary key columns
* @return array
*/
protected static function pk()
{
return static::config('pk');
}
/**
* default values for columns
* @return array
*/
protected static function default_values()
{
return static::config('default_values');
}
/**
* list of columns to deserialize
* @return array key is name of column, value is name of ArrayObject class
*/
protected static function serialized_fields()
{
return static::config('serialized_fields');
}
/**
* aliases for columns
* alias => column
protected static function alias_fields()
{
return static::config('alias_fields');
}
/**
* multi-language fields
* name => boolean
protected static function i18n_fields()
{
return static::config('i18n_fields');
}
/**
* additional computed fields
* name => callable
protected static function additional_fields()
{
return static::config('additional_fields');
}
protected static function has_many()
{
return static::config('has_many');
}
protected static function has_one()
{
return static::config('has_one');
}
protected static function belongs_to()
{
return static::config('belongs_to');
}
protected static function has_and_belongs_to_many()
{
return static::config('has_and_belongs_to_many');
}
* @return array<string, array<string|Closure>>
protected static function registered_callbacks()
{
return static::config('registered_callbacks');
}
/**
* contains an array of all used identifiers for fields
* (db columns + aliased columns + additional columns + relations)
protected static function known_slots()
{
return static::config('known_slots');
}
/**
* assoc array used to map SORM callback to NotificationCenter
* keys are SORM callbacks, values notifications
* eg. 'after_create' => 'FooDidCreate'
*
protected static function notification_map()
{
return static::config('notification_map');
}
/**
* assoc array for mapping get/set Methods
*
protected static function getter_setter_map()
{
return static::config('getter_setter_map');
}
//////////////////////////////////////////////////
/**
* set configuration data from subclass
*

Marcus Eibrink-Lunzenauer
committed
* @param ?array $config configuration data
* @return void
*/
protected static function configure($config = [])
{
$class = static::class;
if (empty($config['db_table'])) {
$config['db_table'] = strtolower($class);
}
if (!isset($config['db_fields'])) {
if (static::tableScheme($config['db_table'])) {
$config['db_fields'] = self::$schemes[$config['db_table']]['db_fields'];
$config['pk'] = self::$schemes[$config['db_table']]['pk'];
}
}
if (isset($config['pk'])
&& !isset($config['db_fields']['id'])
&& !isset($config['alias_fields']['id'])
&& !isset($config['additional_fields']['id'])
) {
if (count($config['pk']) === 1) {
$config['alias_fields']['id'] = $config['pk'][0];
} else {
$config['additional_fields']['id'] = ['get' => '_getId',
'set' => '_setId'];
}
}
if (isset($config['additional_fields'])) {
foreach ($config['additional_fields'] as $a_field => $a_config) {
if (is_array($a_config) && !(isset($a_config['get']) || isset($a_config['set']))) {
$relation = $a_config[0] ?? '';
$relation_field = $a_config[1] ?? '';
if (!$relation) {
list($relation, $relation_field) = explode('_', $a_field);
}
if (!$relation_field || !$relation) {
throw new UnexpectedValueException('no relation found for autoget/set additional field: ' . $a_field);
}
$config['additional_fields'][$a_field] = ['get' => '_getAdditionalValueFromRelation',
'set' => '_setAdditionalValue',
'relation' => $relation,
'relation_field' => $relation_field];
}
}
}
if (isset($config['serialized_fields'])) {
foreach ($config['serialized_fields'] as $a_field => $object_type) {
if (!(is_subclass_of($object_type, 'StudipArrayObject'))) {
throw new UnexpectedValueException(sprintf('serialized field %s must use subclass of StudipArrayObject', $a_field));
}
}
}
foreach (['default_values', 'serialized_fields', 'alias_fields', 'i18n_fields', 'additional_fields'] as $fields) {
if (!isset($config[$fields])) {
$config[$fields] = [];
}
}
foreach (['has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many'] as $type) {
if (isset($config[$type]) && is_array($config[$type])) {
foreach (array_keys($config[$type]) as $one) {
$config['relations'][$one] = null;
}
} else {
$config[$type] = [];
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
}
}
$callbacks = ['before_create',
'before_update',
'before_store',
'before_delete',
'before_initialize',
'after_create',
'after_update',
'after_store',
'after_delete',
'after_initialize'];
foreach ($callbacks as $callback) {
if (!isset($config['registered_callbacks'][$callback])) {
$config['registered_callbacks'][$callback] = [];
}
}
$auto_notification_map['after_create'] = $class . 'DidCreate';
$auto_notification_map['after_store'] = $class . 'DidStore';
$auto_notification_map['after_delete'] = $class . 'DidDelete';
$auto_notification_map['after_update'] = $class . 'DidUpdate';
$auto_notification_map['before_create'] = $class . 'WillCreate';
$auto_notification_map['before_store'] = $class . 'WillStore';
$auto_notification_map['before_delete'] = $class . 'WillDelete';
$auto_notification_map['before_update'] = $class . 'WillUpdate';
foreach ($auto_notification_map as $cb => $notification) {
if (isset($config['notification_map'][$cb])) {
if (strpos($config['notification_map'][$cb], $notification) !== false) {
$config['notification_map'][$cb] .= ' ' . $notification;
}
} else {
$config['notification_map'][$cb] = $notification;
}
}
if (is_array($config['notification_map'])) {
foreach (array_keys($config['notification_map']) as $cb) {
$config['registered_callbacks'][$cb][] = 'cbNotificationMapper';
}
}
if (!I18N::isEnabled() || empty($config['i18n_fields'])) {
} elseif (is_string($config['i18n_fields'])) {
$i18n_fields = words($config['i18n_fields']);
$config['i18n_fields'] = array_combine(
$i18n_fields,
array_fill(0, count($i18n_fields), true)
);
} elseif (array_is_list($config['i18n_fields'])) {
$config['i18n_fields'] = array_combine(
$config['i18n_fields'],
array_fill(0, count($config['i18n_fields']), true)
);
}
array_unshift($config['registered_callbacks']['after_initialize'], 'cbAfterInitialize');
$config['known_slots'] = array_merge(
array_keys($config['db_fields']),
array_keys($config['alias_fields'] ?? []),
array_keys($config['additional_fields'] ?? []),
array_keys($config['relations'] ?? [])
foreach (array_map('strtolower', get_class_methods($class)) as $method) {
if (in_array(substr($method, 0, 3), ['get', 'set'])) {
$verb = substr($method, 0, 3);
$name = substr($method, 3);
if (in_array($name, $config['known_slots']) && !in_array($name, static::$reserved_slots) && !isset($config['additional_fields'][$name][$verb])) {
$config['getter_setter_map'][$name][$verb] = $method;
}
}
}
self::$config[$class] = $config;
}
/**
* fetch config data for the called class
*
* @param string $key config key

Marcus Eibrink-Lunzenauer
committed
* @return mixed value of config key (null if not set)
*/
protected static function config($key)
{
if (!array_key_exists(static::class, self::$config)) {
static::configure();
}
}
/**
* fetch table metadata from db or from local cache
*
* @param string $db_table
* @return bool true if metadata could be fetched
*/
public static function tableScheme($db_table)
{
if (self::$schemes === null) {
$cache = StudipCacheFactory::getCache();
self::$schemes = unserialize($cache->read('DB_TABLE_SCHEMES'));
}
if (!isset(self::$schemes[$db_table])) {
$db = DBManager::get()->query("SHOW COLUMNS FROM $db_table");
while($rs = $db->fetch(PDO::FETCH_ASSOC)){
$db_fields[strtolower($rs['Field'])] = [
'name' => $rs['Field'],
'null' => $rs['Null'],
'default' => $rs['Default'],
'type' => $rs['Type'],
'extra' => $rs['Extra']
];
$pk[] = strtolower($rs['Field']);
}
}
self::$schemes[$db_table]['db_fields'] = $db_fields;
self::$schemes[$db_table]['pk'] = $pk;
$cache = StudipCacheFactory::getCache();
$cache->write('DB_TABLE_SCHEMES', serialize(self::$schemes));
}
return isset(self::$schemes[$db_table]);
}
/**
* force reload of cached table metadata

Marcus Eibrink-Lunzenauer
committed
* @return void
*/
public static function expireTableScheme()
{
StudipCacheFactory::getCache()->expire('DB_TABLE_SCHEMES');
self::$schemes = null;
self::$config = [];
}
/**

Marcus Eibrink-Lunzenauer
committed
* Returns new instance for given key when found in the database, else null.
*
* @param string $id primary key
* @return static|null
*/
public static function find($id)
{
$ref = new ReflectionClass(static::class);

Marcus Eibrink-Lunzenauer
committed
/** @var static $record */
$record = $ref->newInstanceArgs(func_get_args());
if (!$record->isNew()) {
return $record;
} else {
return null;
}
}
/**

Marcus Eibrink-Lunzenauer
committed
* Returns true if given key exists in the database.
*
* @param string|array $id primary key
* @return boolean
*/
public static function exists($id)
{
$ret = false;
$db_table = static::db_table();
$record = new static();
$record->setId(...func_get_args());
$where_query = $record->getWhereQuery();
if ($where_query) {
$query = "SELECT 1 FROM `$db_table` WHERE "
. join(" AND ", $where_query);
$ret = (bool)DBManager::get()->query($query)->fetchColumn();
}
return $ret;
}
/**
* returns number of records
*
Jan-Hendrik Willms
committed
* @param ?string $sql sql clause to use on the right side of WHERE
* @param ?array $params params for query

Marcus Eibrink-Lunzenauer
committed
* @return int
Jan-Hendrik Willms
committed
public static function countBySql($sql = '1', $params = [])
$db_table = static::db_table();
Jan-Hendrik Willms
committed
$db = DBManager::get();
$has_join = stripos($sql, 'JOIN ');
if ($has_join === false || $has_join > 10) {
Jan-Hendrik Willms
committed
$sql = 'WHERE ' . $sql;
Jan-Hendrik Willms
committed
$sql = "SELECT count(*) FROM `" . $db_table . "` " . $sql;
$st = $db->prepare($sql);
$st->execute($params);
return (int)$st->fetchColumn();
}
/**
* creates new record with given data in db
* returns the new object or null
* @param array $data assoc array of record

Marcus Eibrink-Lunzenauer
committed
* @return ?static
*/
public static function create($data)
{
$record = new static();
$record->setData($data, false);
if ($record->store()) {
return $record;
} else {
return null;
}
}
/**
* build object with given data
*
* @param array $data assoc array of record

Marcus Eibrink-Lunzenauer
committed
* @param ?bool $is_new set object to new state
* @return static
*/
public static function build($data, $is_new = true)
{
$record = new static();
$record->setData($data, !$is_new);
$record->setNew($is_new);
return $record;
}
/**
* build object with given data and mark it as existing
*

Marcus Eibrink-Lunzenauer
committed
* @param array $data assoc array of record
* @return static
*/
public static function buildExisting($data)
{
return static::build($data, false);
}
/**
* generate SimpleORMap object structure from assoc array
* if given array contains data of related objects in sub-arrays
* they are also generated. Existing records are updated, new records are created
* (but changes are not yet stored)
*
* @param array $data
* @return static
*/
public static function import($data)
{
$record_data = [];
$relation_data = [];
foreach ($data as $key => $value) {
$temp = static::alias_fields()[$key] ?? $key;
if (isset(static::db_fields()[$temp])) {
}
}
$record = static::toObject($record_data);
if (!$record instanceof static) {
$record = new static();
$record->setData($record_data, true);
} else {
$record->setData($record_data);
}
foreach ($relation_data as $relation => $data) {
if (!$record->isRelation($relation)) {
continue;
}
$options = $record->getRelationOptions($relation);
if ($options['type'] == 'has_one') {
$record->{$relation} = call_user_func([$options['class_name'], 'import'], $data);
}
if ($options['type'] == 'has_many' || $options['type'] == 'has_and_belongs_to_many') {
foreach ($data as $one) {
$current = call_user_func([$options['class_name'], 'import'], $one);
if ($options['type'] == 'has_many') {
$foreign_key_value = call_user_func($options['assoc_func_params_func'], $record);
call_user_func($options['assoc_foreign_key_setter'], $current, $foreign_key_value);
}
if ($current->id !== null) {
$existing = $record->{$relation}->find($current->id);
if ($existing) {
$existing->setData($current);
} else {
$record->{$relation}->append($current);
}
}
}
}
}
return $record;
}
/**
* returns array of instances of given class filtered by given sql
Jan-Hendrik Willms
committed
* @param string $sql sql clause to use on the right side of WHERE
* @param ?array $params parameters for query
* @return static[] array of "self" objects
Jan-Hendrik Willms
committed
public static function findBySQL($sql, $params = [])
$db_table = static::db_table();

Jan-Hendrik Willms
committed
Jan-Hendrik Willms
committed
$has_join = stripos($sql, 'JOIN ');
if ($has_join === false || $has_join > 10) {
Jan-Hendrik Willms
committed
$sql = 'WHERE ' . $sql;
Jan-Hendrik Willms
committed
$sql = "SELECT `" . $db_table . "`.* FROM `" . $db_table . "` " . $sql;
$stmt = DBManager::get()->prepare($sql);
$stmt->execute($params);

Jan-Hendrik Willms
committed
$record = static::build([], false);
$ret = [];
do {
$clone = clone $record;

Jan-Hendrik Willms
committed
$stmt->setFetchMode(PDO::FETCH_INTO, $clone);
if ($clone = $stmt->fetch()) {
$clone->applyCallbacks('after_initialize');
$ret[] = $clone;
}
} while ($clone);
return $ret;
}
/**
* returns one instance of given class filtered by given sql
* only first row of query is used

Marcus Eibrink-Lunzenauer
committed
* @param string $where sql clause to use on the right side of WHERE
* @param ?array $params parameters for query
* @return ?static
*/
public static function findOneBySQL($where, $params = [])
{
if (stripos($where, 'LIMIT') === false) {
$where .= " LIMIT 1";
}
$found = static::findBySQL($where, $params);
return isset($found[0]) ? $found[0] : null;
}
/**
* find related records for a n:m relation (has_many_and_belongs_to_many)
* using a combination table holding the keys
*

Marcus Eibrink-Lunzenauer
committed
* @param string $foreign_key_value value of foreign key to find related records
* @param array $options relation options from other side of relation
* @return static[] array of "self" objects
*/
public static function findThru($foreign_key_value, $options)
{
$thru_table = $options['thru_table'];
$thru_key = $options['thru_key'];
$thru_assoc_key = $options['thru_assoc_key'];
$assoc_foreign_key = $options['assoc_foreign_key'];
$db_table = static::db_table();

Jan-Hendrik Willms
committed
$sql = "SELECT `$db_table`.* FROM `$thru_table`
INNER JOIN `$db_table` ON `$thru_table`.`$thru_assoc_key` = `$db_table`.`$assoc_foreign_key`
WHERE `$thru_table`.`$thru_key` = ? " . ($options['order_by'] ?? '');

Jan-Hendrik Willms
committed
$st = DBManager::get()->prepare($sql);
$st->execute([$foreign_key_value]);

Jan-Hendrik Willms
committed
$record = static::build([], false);

Jan-Hendrik Willms
committed
do {
$clone = clone $record;

Jan-Hendrik Willms
committed
$st->setFetchMode(PDO::FETCH_INTO, $clone);

Jan-Hendrik Willms
committed
if ($clone = $st->fetch()) {
$clone->applyCallbacks('after_initialize');
$ret[] = $clone;
}
} while ($clone);
return $ret;
}
/**
* passes objects for given sql through given callback
*
* @param callable $callable callback which gets the current record as param
Jan-Hendrik Willms
committed
* @param string $sql where clause of sql

Marcus Eibrink-Lunzenauer
committed
* @param ?array $params sql statement parameters
* @return integer number of found records
*/
Jan-Hendrik Willms
committed
public static function findEachBySQL($callable, $sql, $params = [])
Jan-Hendrik Willms
committed
$has_join = stripos($sql, 'JOIN ');
if ($has_join === false || $has_join > 10) {
Jan-Hendrik Willms
committed
$sql = "WHERE {$sql}";
Jan-Hendrik Willms
committed
$db_table = static::db_table();
$st = DBManager::get()->prepare("SELECT `{$db_table}`.* FROM `{$db_table}` {$sql}");
$st->execute($params);
// Indicate that we are performing a batch operation
static::$performs_batch_operation = true;

Jan-Hendrik Willms
committed
$record = static::build([], false);

Jan-Hendrik Willms
committed
do {
$clone = clone $record;

Jan-Hendrik Willms
committed
$st->setFetchMode(PDO::FETCH_INTO, $clone);

Jan-Hendrik Willms
committed
if ($clone = $st->fetch()) {
$clone->applyCallbacks('after_initialize');
$callable($clone, $ret++);
}
} while ($clone);
// Reset batch operation indicator
static::$performs_batch_operation = false;
return $ret;
}
/**
* returns array of instances of given class for by given pks

Marcus Eibrink-Lunzenauer
committed
* @param ?array $pks array of primary keys
* @param ?string $order order by clause
* @param ?array $order_params
*/
public static function findMany($pks = [], $order = '', $order_params = [])
{
$db_table = static::db_table();
$pk = static::pk();
$db = DBManager::get();
if (count($pk) > 1) {
throw new Exception('not implemented yet');
}
$where = "`$db_table`.`{$pk[0]}` IN (" . $db->quote($pks) . ") ";
return static::findBySQL($where . $order, $order_params);
}
/**
* passes objects for by given pks through given callback
*
* @param callable $callable callback which gets the current record as param

Marcus Eibrink-Lunzenauer
committed
* @param ?array $pks array of primary keys of called class
* @param ?string $order order by sql
* @param ?array $order_params
* @return integer number of found records
*/
public static function findEachMany($callable, $pks = [], $order = '', $order_params = [])
{
$db_table = static::db_table();
$pk = static::pk();
$db = DBManager::get();
if (count($pk) > 1) {
throw new Exception('not implemented yet');
}
$where = "`$db_table`.`{$pk[0]}` IN (" . $db->quote($pks) . ") ";
return static::findEachBySQL($callable, $where . $order, $order_params);
}
/**
* passes objects for given sql through given callback
* and returns an array of callback return values
*
* @param callable $callable callback which gets the current record as param

Marcus Eibrink-Lunzenauer
committed
* @param string $where where clause of sql
* @param array $params sql statement parameters
* @return array return values of callback
*/
public static function findAndMapBySQL($callable, $where, $params = [])
{
$ret = [];
$calleach = function($m) use (&$ret, $callable) {
$ret[] = $callable($m);
};
static::findEachBySQL($calleach, $where, $params);
return $ret;
}
/**
* passes objects for by given pks through given callback
* and returns an array of callback return values
*
* @param callable $callable callback which gets the current record as param

Marcus Eibrink-Lunzenauer
committed
* @param ?array $pks array of primary keys of called class
* @param ?string $order order by sql
* @param ?array $order_params
* @return array return values of callback
*/
public static function findAndMapMany($callable, $pks = [], $order = '', $order_params = [])
{
$ret = [];
$calleach = function($m) use (&$ret, $callable) {
$ret[] = $callable($m);
};
$db_table = static::db_table();
$pk = static::pk();
$db = DBManager::get();
if (count($pk) > 1) {
throw new Exception('not implemented yet');
}
$where = "`$db_table`.`{$pk[0]}` IN (" . $db->quote($pks) . ") ";
static::findEachBySQL($calleach, $where . $order, $order_params);
return $ret;
}
/**
* deletes objects specified by sql clause
* @param string $where sql clause to use on the right side of WHERE

Marcus Eibrink-Lunzenauer
committed
* @param ?array $params parameters for query
* @return integer number of deleted records
*/
public static function deleteBySQL($where, $params = [])
{
$killeach = function($record) {$record->delete();};
return static::findEachBySQL($killeach, $where, $params);
}
/**
* returns object of given class for given id or null
* the param could be a string, an assoc array containing primary key field
* or an already matching object. In all these cases an object is returned
*

Marcus Eibrink-Lunzenauer
committed
* @param string|static|array $id_or_object id as string, object or assoc array
* @return static
*/
public static function toObject($id_or_object)
{
if ($id_or_object instanceof static) {
return $id_or_object;
}
if (is_array($id_or_object)) {
if (array_key_exists($key, $id_or_object)) {
$key_values[] = $id_or_object[$key];
}
}
if (count($pk) === count($key_values)) {
if (count($pk) === 1) {
$id = $key_values[0];
} else {
$id = $key_values;
}
}
} else {
$id = $id_or_object;
}
return static::find($id);
}
/**
* interceptor for static findByColumn / findEachByColumn / countByColumn /
* deleteByColumn magic
* @param string $name
* @param array $arguments
* @throws BadMethodCallException
public static function __callStatic(string $name, array $arguments)
$db_table = static::db_table();
$alias_fields = static::alias_fields();
$db_fields = static::db_fields();
$name = strtolower($name);
$param_arr = [];
$where = '';
$where_param = is_array($arguments[0]) ? $arguments[0] : [$arguments[0]];
$action = strstr($name, 'by', true);
$field = substr($name, strlen($action) + 2);
switch ($action) {
$param_arr[0] =& $where;
$param_arr[1] = [$where_param];
$method = 'findonebysql';
break;
case 'find':
case 'findmany':
$param_arr[0] =& $where;
$param_arr[1] = [$where_param];
$method = 'findbysql';
break;
case 'findeach':
case 'findeachmany':
$param_arr[0] = $arguments[0];
$param_arr[1] =& $where;
$param_arr[2] = [$arguments[1]];
$method = 'findeachbysql';
break;
case 'count':
case 'delete':
$param_arr[0] =& $where;
$param_arr[1] = [$where_param];
$method = "{$action}bysql";
throw new BadMethodCallException("Method " . static::class . "::$name not found");
}
if (isset($alias_fields[$field])) {
$field = $alias_fields[$field];
}
if (isset($db_fields[$field])) {
$where = "`$db_table`.`$field` IN(?) " . $order;
return call_user_func_array([static::class, $method], $param_arr);
throw new BadMethodCallException("Method " . static::class . "::$name not found");
}
/**
* constructor, give primary key of record as param to fetch
* corresponding record from db if available, if not preset primary key
* with given value. Give null to create new record
*

Marcus Eibrink-Lunzenauer
committed
* @param null|int|string|array $id primary key of table
*/
function __construct($id = null)
{
foreach(['has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many'] as $type) {
foreach (array_keys($this->$type()) as $one) {
$this->relations[$one] = null;
}
}
if ($id) {
$this->setId($id);
}
$this->restore();
}
/**
* returns internal used id value (multiple keys concatenated with _)

Marcus Eibrink-Lunzenauer
committed
* @param mixed $field unused parameter
* @return ?string
*/
protected function _getId($field)
{
return is_null($this->getId())
? null
: implode(self::ID_SEPARATOR, $this->getId());
}
/**
* sets internal used id value (multiple keys concatenated with _)
* @param string $field Field to set (unused since it's always the id)
* @param string $value Value for id field

Marcus Eibrink-Lunzenauer
committed
* @return bool
*/
protected function _setId($field, $value)
{
return $this->setId(explode(self::ID_SEPARATOR, $value));
}
/**
* retrieves an additional field value from relation
*
* @param string $field

Marcus Eibrink-Lunzenauer
committed
* @return mixed
*/
protected function _getAdditionalValueFromRelation($field)
{
list($relation, $relation_field) = [$this->additional_fields()[$field]['relation'],
$this->additional_fields()[$field]['relation_field']];
if (!array_key_exists($field, $this->additional_data)) {
$this->_setAdditionalValue($field, $this->getRelationValue($relation, $relation_field));
}