ILIAS  release_8 Revision v8.23
class.ActiveRecord.php
Go to the documentation of this file.
1 <?php
2 
3 /******************************************************************************
4  *
5  * This file is part of ILIAS, a powerful learning management system.
6  *
7  * ILIAS is licensed with the GPL-3.0, you should have received a copy
8  * of said license along with the source code.
9  *
10  * If this is not the case or you just want to try ILIAS, you'll find
11  * us at:
12  * https://www.ilias.de
13  * https://github.com/ILIAS-eLearning
14  *
15  *****************************************************************************/
24 abstract class ActiveRecord
25 {
26  protected bool $ar_safe_read = true;
27  protected string $connector_container_name = '';
28  protected bool $is_new = true;
29 
30  public function getArConnector(): \arConnector
31  {
32  return arConnectorMap::get($this);
33  }
34 
35  public function getArFieldList(): \arFieldList
36  {
37  return arFieldCache::get($this);
38  }
39 
44  public static function returnDbTableName(): string
45  {
46  throw new arException(
48  'Implement getConnectorContainerName in your child-class'
49  );
50  }
51 
55  public function getConnectorContainerName(): string
56  {
57  // WILL BE ABSTRACT TO REPLACE returnDbTableName() IN NEXT VERSION
58  if ($this->connector_container_name) {
60  }
61 
62  $ar = self::getCalledClass();
63 
64  return $ar::returnDbTableName();
65  }
66 
67  public function setConnectorContainerName(string $connector_container_name): void
68  {
69  $this->connector_container_name = $connector_container_name;
70  }
71 
75  public function getPrimaryFieldValue()
76  {
77  $primary_fieldname = arFieldCache::getPrimaryFieldName($this);
78 
79  return $this->{$primary_fieldname};
80  }
81 
85  public function setPrimaryFieldValue($value): void
86  {
87  $primary_fieldname = arFieldCache::getPrimaryFieldName($this);
88 
89  $this->{$primary_fieldname} = $value;
90  }
91 
95  public function __construct($primary_key = 0)
96  {
97  $arFieldList = arFieldCache::get($this);
98 
99  $key = $arFieldList->getPrimaryFieldName();
100  $this->{$key} = $primary_key;
101  if ($primary_key !== 0 && $primary_key !== null && $primary_key !== false) {
102  $this->read();
103  }
104  }
105 
106  public function storeObjectToCache(): void
107  {
108  arObjectCache::store($this);
109  }
110 
111  public function asStdClass(): \stdClass
112  {
113  $return = new stdClass();
114  foreach ($this->getArFieldList()->getFields() as $field) {
115  $fieldname = $field->getName();
116  $return->{$fieldname} = $this->{$fieldname};
117  }
118 
119  return $return;
120  }
121 
122  public function buildFromArray(array $array): \ActiveRecord
123  {
124  $class = get_class($this);
125  $primary = $this->getArFieldList()->getPrimaryFieldName();
126  $primary_value = $array[$primary];
127  if ($primary_value && arObjectCache::isCached($class, $primary_value)) {
128  return arObjectCache::get($class, $primary_value);
129  }
130  foreach ($array as $field_name => $value) {
131  $waked = $this->wakeUp($field_name, $value);
132  $this->{$field_name} = $waked ?? $value;
133  }
134  arObjectCache::store($this);
135 
136  return $this;
137  }
138 
145  public function fixDateField($field_name, $value)
146  {
147  if ($this->getArFieldList()->getFieldByName($field_name)->isDateField()) {
148  return $this->getArConnector()->fixDate($value);
149  }
150 
151  return $value;
152  }
153 
158  public function sleep($field_name)
159  {
160  return null;
161  }
162 
168  public function wakeUp($field_name, $field_value)
169  {
170  return null;
171  }
172 
173  final public function getArrayForConnector(): array
174  {
175  $data = array();
176  foreach ($this->getArFieldList()->getFields() as $field) {
177  $field_name = $field->getName();
178  $sleeped = $this->sleep($field_name);
179  $var = $sleeped ?? ($this->{$field_name});
180  $data[$field_name] = array($field->getFieldType(), $var);
181  }
182 
183  return $data;
184  }
185 
186 
187 
188 
189  //
190  // Collector Modifications
191  //
196  protected static function getCalledClass(): \ActiveRecord
197  {
198  $class = static::class;
199 
200  return arCalledClassCache::get($class);
201  }
202 
208  final public static function installDB(): bool
209  {
210  return self::getCalledClass()->installDatabase();
211  }
212 
216  public function installConnector(): bool
217  {
218  return $this->installDatabase();
219  }
220 
225  final public static function renameDBField(string $old_name, string $new_name): bool
226  {
227  return self::getCalledClass()->getArConnector()->renameField(self::getCalledClass(), $old_name, $new_name);
228  }
229 
230  final public static function tableExists(): bool
231  {
232  return self::getCalledClass()->getArConnector()->checkTableExists(self::getCalledClass());
233  }
234 
238  final public static function fieldExists(string $field_name): bool
239  {
240  return self::getCalledClass()->getArConnector()->checkFieldExists(self::getCalledClass(), $field_name);
241  }
242 
246  final public static function removeDBField(string $field_name): bool
247  {
248  return self::getCalledClass()->getArConnector()->removeField(self::getCalledClass(), $field_name);
249  }
253  final protected function installDatabase(): bool
254  {
255  if (!self::tableExists()) {
256  $fields = array();
257  if (isset($this)) {
258  foreach ($this->getArFieldList()->getFields() as $field) {
259  $fields[$field->getName()] = $field->getAttributesForConnector();
260  }
261  }
262 
263  return $this->getArConnector()->installDatabase($this, $fields);
264  }
265 
266  return $this->getArConnector()->updateDatabase($this);
267  }
268 
272  final public static function updateDB(): bool
273  {
274  if (!self::tableExists()) {
275  self::getCalledClass()->installDatabase();
276 
277  return true;
278  }
279 
280  return self::getCalledClass()->getArConnector()->updateDatabase(self::getCalledClass());
281  }
285  final public static function resetDB(): bool
286  {
287  return self::getCalledClass()->getArConnector()->resetDatabase(self::getCalledClass());
288  }
289 
293  final public static function truncateDB(): void
294  {
295  self::getCalledClass()->getArConnector()->truncateDatabase(self::getCalledClass());
296  }
300  final public static function flushDB(): void
301  {
302  self::truncateDB();
303  }
304 
305  //
306  // CRUD
307  //
308  public function store(): void
309  {
310  $primary_fieldname = arFieldCache::getPrimaryFieldName($this);
311  $primary_value = $this->getPrimaryFieldValue();
312 
313  if (!self::where(array($primary_fieldname => $primary_value))->hasSets()) {
314  $this->create();
315  } else {
316  $this->update();
317  }
318  }
319 
320  public function save(): void
321  {
322  $this->store();
323  }
324 
325  public function create(): void
326  {
327  if ($this->getArFieldList()->getPrimaryField()->getSequence()) {
328  $primary_fieldname = arFieldCache::getPrimaryFieldName($this);
329  $this->{$primary_fieldname} = $this->getArConnector()->nextID($this);
330  }
331 
332  $this->getArConnector()->create($this);
333  arObjectCache::store($this);
334  }
335 
339  public function copy(int $new_id = 0): self
340  {
341  if (self::where(array($this->getArFieldList()->getPrimaryFieldName() => $new_id))->hasSets()) {
343  }
344  $new_obj = clone($this);
345  $new_obj->setPrimaryFieldValue($new_id);
346 
347  return $new_obj;
348  }
349 
350  public function afterObjectLoad(): void
351  {
352  }
353 
357  public function read(): void
358  {
359  $records = $this->getArConnector()->read($this);
360  if ($this->ar_safe_read === true && is_array($records) && count($records) === 0) {
362  } elseif ($this->ar_safe_read === false && is_array($records) && count($records) === 0) {
363  $this->is_new = true;
364  }
365  $records = is_array($records) ? $records : array();
366  foreach ($records as $rec) {
367  foreach ($this->getArrayForConnector() as $k => $v) {
368  $waked = $this->wakeUp($k, $rec->{$k} ?? null);
369  $this->{$k} = $waked ?? $rec->{$k} ?? null;
370  }
371  arObjectCache::store($this);
372  }
373  }
374 
375  public function update()
376  {
377  $this->getArConnector()->update($this);
378  arObjectCache::store($this);
379  }
380 
381  public function delete()
382  {
383  $this->getArConnector()->delete($this);
384  arObjectCache::purge($this);
385  }
386 
387 
388 
389  //
390  // Collection
391  //
395  public static function preloadObjects(): array
396  {
397  return self::get();
398  }
399 
403  public static function additionalParams(array $additional_params): \ActiveRecordList
404  {
405  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
406  $srModelObjectList->additionalParams($additional_params);
407 
408  return $srModelObjectList;
409  }
410 
414  public static function find($primary_key, array $add_constructor_args = array()): ?\ActiveRecord
415  {
419  try {
420  $class_name = static::class;
421  if (!arObjectCache::isCached($class_name, $primary_key)) {
422  $obj = arFactory::getInstance($class_name, $primary_key, $add_constructor_args);
423  $obj->storeObjectToCache();
424 
425  return $obj;
426  }
427  } catch (arException $e) {
428  return null;
429  }
430 
431  try {
432  $obj = arObjectCache::get($class_name, $primary_key);
433  } catch (arException $e) {
434  return null;
435  }
436 
437  return $obj;
438  }
439 
445  public static function findOrFail($primary_key, array $add_constructor_args = array()): \ActiveRecord
446  {
447  $obj = self::find($primary_key, $add_constructor_args);
448  if (is_null($obj)) {
450  }
451 
452  return $obj;
453  }
454 
460  public static function findOrGetInstance($primary_key, array $add_constructor_args = array())
461  {
462  $obj = self::find($primary_key, $add_constructor_args);
463  if ($obj !== null) {
464  return $obj;
465  }
466 
467  $class_name = static::class;
468  $obj = arFactory::getInstance($class_name, 0, $add_constructor_args);
469  $obj->setPrimaryFieldValue($primary_key);
470  $obj->is_new = true;
471  $obj->storeObjectToCache();
472 
473  return $obj;
474  }
475 
480  public static function where($where, $operator = null): \ActiveRecordList
481  {
482  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
483  $srModelObjectList->where($where, $operator);
484 
485  return $srModelObjectList;
486  }
487 
493  public static function innerjoinAR(
494  ActiveRecord $ar,
495  $on_this,
496  $on_external,
497  array $fields = array('*'),
498  string $operator = '=',
499  $both_external = false
500  ): \ActiveRecordList {
501  return self::innerjoin(
503  $on_this,
504  $on_external,
505  $fields,
506  $operator,
507  $both_external
508  );
509  }
510 
517  public static function innerjoin(
518  $tablename,
519  $on_this,
520  $on_external,
521  array $fields = array('*'),
522  string $operator = '=',
523  bool $both_external = false
524  ): \ActiveRecordList {
525  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
526 
527  return $srModelObjectList->innerjoin($tablename, $on_this, $on_external, $fields, $operator, $both_external);
528  }
529 
536  public static function leftjoin(
537  $tablename,
538  $on_this,
539  $on_external,
540  array $fields = array('*'),
541  string $operator = '=',
542  bool $both_external = false
543  ): \ActiveRecordList {
544  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
545 
546  return $srModelObjectList->leftjoin($tablename, $on_this, $on_external, $fields, $operator, $both_external);
547  }
548 
552  public static function orderBy($orderBy, string $orderDirection = 'ASC'): \ActiveRecordList
553  {
554  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
555  $srModelObjectList->orderBy($orderBy, $orderDirection);
556 
557  return $srModelObjectList;
558  }
559 
560  public static function dateFormat(string $date_format = 'd.m.Y - H:i:s'): \ActiveRecordList
561  {
562  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
563  $srModelObjectList->dateFormat($date_format);
564 
565  return $srModelObjectList;
566  }
567 
572  public static function limit($start, $end): \ActiveRecordList
573  {
574  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
575  $srModelObjectList->limit($start, $end);
576 
577  return $srModelObjectList;
578  }
579 
580  public static function affectedRows(): int
581  {
582  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
583 
584  return $srModelObjectList->affectedRows();
585  }
586 
587  public static function count(): int
588  {
589  return self::affectedRows();
590  }
591 
595  public static function get(): array
596  {
597  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
598 
599  return $srModelObjectList->get();
600  }
601 
602  public static function debug(): \ActiveRecordList
603  {
604  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
605 
606  return $srModelObjectList->debug();
607  }
608 
609  public static function first(): ?\ActiveRecord
610  {
611  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
612 
613  return $srModelObjectList->first();
614  }
615 
616  public static function getCollection(): \ActiveRecordList
617  {
618  return new ActiveRecordList(self::getCalledClass());
619  ;
620  }
621 
622  public static function last(): ?\ActiveRecord
623  {
624  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
625 
626  return $srModelObjectList->last();
627  }
628 
632  public static function getFirstFromLastQuery(): ?\ActiveRecord
633  {
634  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
635 
636  return $srModelObjectList->getFirstFromLastQuery();
637  }
638 
639  public static function connector(arConnector $connector): \ActiveRecordList
640  {
641  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
642 
643  return $srModelObjectList->connector($connector);
644  }
645 
646  public static function raw(bool $set_raw = true): \ActiveRecordList
647  {
648  $srModelObjectList = new ActiveRecordList(self::getCalledClass());
649 
650  return $srModelObjectList->raw($set_raw);
651  }
652 
656  public static function getArray(?string $key = null, $values = null): array
657  {
658  $record_list = new ActiveRecordList(self::getCalledClass());
659 
660  return $record_list->getArray($key, $values);
661  }
662 
663  //
664  // Magic Methods & Helpers
665  //
672  public function __call($name, $arguments)
673  {
674  // Getter
675  if (preg_match("/get([a-zA-Z]*)/u", $name, $matches) && count($arguments) === 0) {
676  return $this->{self::fromCamelCase($matches[1])};
677  }
678  // Setter
679  if (preg_match("/set([a-zA-Z]*)/u", $name, $matches) && count($arguments) === 1) {
680  $this->{self::fromCamelCase($matches[1])} = $arguments[0];
681  }
682  }
683 
684  public static function _toCamelCase(string $str, bool $capitalise_first_char = false): ?string
685  {
686  if ($capitalise_first_char) {
687  $str[0] = strtoupper($str[0]);
688  }
689 
690  return preg_replace_callback('/_([a-z])/', fn ($c) => strtoupper($c[1]), $str);
691  }
692 
693  protected static function fromCamelCase(string $str): ?string
694  {
695  $str[0] = strtolower($str[0]);
696 
697  return preg_replace_callback('/([A-Z])/', fn ($c) => "_" . strtolower($c[1]), $str);
698  }
699 }
static limit($start, $end)
static getFirstFromLastQuery()
buildFromArray(array $array)
static get(string $class_name)
static getPrimaryFieldName(ActiveRecord $ar)
const UNKNONWN_EXCEPTION
static orderBy($orderBy, string $orderDirection='ASC')
$c
Definition: cli.php:38
static getArray(?string $key=null, $values=null)
static connector(arConnector $connector)
static leftjoin( $tablename, $on_this, $on_external, array $fields=array(' *'), string $operator='=', bool $both_external=false)
Class ActiveRecord.
static renameDBField(string $old_name, string $new_name)
NullPointerExceptionInspection
static getCalledClass()
Returns an instance of the instatiated calling active record (needs to be done in static methods) : ...
static purge(ActiveRecord $object)
static _toCamelCase(string $str, bool $capitalise_first_char=false)
sleep($field_name)
static returnDbTableName()
__call($name, $arguments)
Class arConnector.
static innerjoinAR(ActiveRecord $ar, $on_this, $on_external, array $fields=array(' *'), string $operator='=', $both_external=false)
static where($where, $operator=null)
static innerjoin( $tablename, $on_this, $on_external, array $fields=array(' *'), string $operator='=', bool $both_external=false)
const COPY_DESTINATION_ID_EXISTS
setPrimaryFieldValue($value)
if($format !==null) $name
Definition: metadata.php:247
__construct($primary_key=0)
static findOrFail($primary_key, array $add_constructor_args=array())
Tries to find the object and throws an Exception if object is not found, instead of returning null...
static store(ActiveRecord $object)
getConnectorContainerName()
Return the Name of your Connector Table
static fromCamelCase(string $str)
string $key
Consumer key/client ID value.
Definition: System.php:193
get(string $key, Refinery\Transformation $t)
Get passed parameter, if not data passed, get key from http request.
Class arFieldList.
static get($class, $id)
wakeUp($field_name, $field_value)
static removeDBField(string $field_name)
static isCached($class, $id)
copy(int $new_id=0)
static fieldExists(string $field_name)
Class arException.
static raw(bool $set_raw=true)
string $connector_container_name
fixDateField($field_name, $value)
static dateFormat(string $date_format='d.m.Y - H:i:s')
static findOrGetInstance($primary_key, array $add_constructor_args=array())
static get(ActiveRecord $ar)
setConnectorContainerName(string $connector_container_name)
static flushDB()
never use in ILIAS Core, Plugins only
static get(ActiveRecord $ar)
static additionalParams(array $additional_params)