Merge branch 'develop' of https://github.com/friendica/friendica into develop
[friendica.git/.git] / src / BaseModel.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2024, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica;
23
24 use Friendica\Database\Database;
25 use Friendica\Network\HTTPException;
26 use Psr\Log\LoggerInterface;
27
28 /**
29  * The Model classes inheriting from this abstract class are meant to represent a single database record.
30  * The associated table name has to be provided in the child class, and the table is expected to have a unique `id` field.
31  *
32  * @property int id
33  */
34 abstract class BaseModel extends BaseDataTransferObject
35 {
36         /** @var Database */
37         protected $dba;
38         /** @var LoggerInterface */
39         protected $logger;
40
41         /**
42          * Model record abstraction.
43          * Child classes never have to interact directly with it.
44          * Please use the magic getter instead.
45          *
46          * @var array
47          */
48         private $data = [];
49
50         /**
51          * Used to limit/avoid updates if no data was changed.
52          *
53          * @var array
54          */
55     private $originalData = [];
56
57         /**
58          * @param Database        $dba
59          * @param LoggerInterface $logger
60          * @param array           $data   Table row attributes
61          */
62         public function __construct(Database $dba, LoggerInterface $logger, array $data = [])
63         {
64                 $this->dba = $dba;
65                 $this->logger = $logger;
66                 $this->data = $data;
67                 $this->originalData = $data;
68         }
69
70         public function getOriginalData(): array
71         {
72                 return $this->originalData;
73         }
74
75         public function resetOriginalData()
76         {
77                 $this->originalData = $this->data;
78         }
79
80         /**
81          * Performance-improved model creation in a loop
82          *
83          * @param BaseModel $prototype
84          * @param array     $data
85          * @return BaseModel
86          */
87         public static function createFromPrototype(BaseModel $prototype, array $data): BaseModel
88         {
89                 $model = clone $prototype;
90                 $model->data = $data;
91                 $model->originalData = $data;
92
93                 return $model;
94         }
95
96         /**
97          * Magic isset method. Returns true if the field exists, either in the data property array or in any of the local properties.
98          * Used by array_column() on an array of objects.
99          *
100          * @param $name
101          * @return bool
102          */
103         public function __isset($name): bool
104         {
105                 return in_array($name, array_merge(array_keys($this->data), array_keys(get_object_vars($this))));
106         }
107
108         /**
109          * Magic getter. This allows to retrieve model fields with the following syntax:
110          * - $model->field (outside of class)
111          * - $this->field (inside of class)
112          *
113          * @param string $name Name of data to fetch
114          * @return mixed
115          * @throws HTTPException\InternalServerErrorException
116          */
117         public function __get(string $name)
118         {
119                 $this->checkValid();
120
121                 if (!array_key_exists($name, $this->data)) {
122                         throw new HTTPException\InternalServerErrorException('Field ' . $name . ' not found in ' . static::class);
123                 }
124
125                 return $this->data[$name];
126         }
127
128         /**
129          * * Magic setter. This allows to set model fields with the following syntax:
130          * - $model->field = $value (outside of class)
131          * - $this->field = $value (inside of class)
132          *
133          * @param string $name
134          * @param mixed  $value
135          */
136         public function __set(string $name, $value)
137         {
138                 $this->data[$name] = $value;
139         }
140
141         public function toArray(): array
142         {
143                 return $this->data;
144         }
145
146         protected function checkValid()
147         {
148                 if (!isset($this->data['id']) || is_null($this->data['id'])) {
149                         throw new HTTPException\InternalServerErrorException(static::class . ' record uninitialized');
150                 }
151         }
152 }