]> git.mxchange.org Git - friendica.git/blob - src/BaseRepository.php
Merge pull request #8302 from annando/allowed-chars
[friendica.git] / src / BaseRepository.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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\Database\DBA;
26 use Friendica\Network\HTTPException;
27 use Psr\Log\LoggerInterface;
28
29 /**
30  * Repositories are Factories linked to one or more database tables.
31  *
32  * @see BaseModel
33  * @see BaseCollection
34  */
35 abstract class BaseRepository extends BaseFactory
36 {
37         const LIMIT = 30;
38
39         /** @var Database */
40         protected $dba;
41
42         /** @var string */
43         protected static $table_name;
44
45         /** @var BaseModel */
46         protected static $model_class;
47
48         /** @var BaseCollection */
49         protected static $collection_class;
50
51         public function __construct(Database $dba, LoggerInterface $logger)
52         {
53                 parent::__construct($logger);
54
55                 $this->dba = $dba;
56                 $this->logger = $logger;
57         }
58
59         /**
60          * Fetches a single model record. The condition array is expected to contain a unique index (primary or otherwise).
61          *
62          * Chainable.
63          *
64          * @param array $condition
65          * @return BaseModel
66          * @throws HTTPException\NotFoundException
67          */
68         public function selectFirst(array $condition)
69         {
70                 $data = $this->dba->selectFirst(static::$table_name, [], $condition);
71
72                 if (!$data) {
73                         throw new HTTPException\NotFoundException(static::class . ' record not found.');
74                 }
75
76                 return $this->create($data);
77         }
78
79         /**
80          * Populates a Collection according to the condition.
81          *
82          * Chainable.
83          *
84          * @param array $condition
85          * @param array $params
86          * @return BaseCollection
87          * @throws \Exception
88          */
89         public function select(array $condition = [], array $params = [])
90         {
91                 $models = $this->selectModels($condition, $params);
92
93                 return new static::$collection_class($models);
94         }
95
96         /**
97          * Populates the collection according to the condition. Retrieves a limited subset of models depending on the boundaries
98          * and the limit. The total count of rows matching the condition is stored in the collection.
99          *
100          * Chainable.
101          *
102          * @param array $condition
103          * @param array $params
104          * @param int?  $max_id
105          * @param int?  $since_id
106          * @param int   $limit
107          * @return BaseCollection
108          * @throws \Exception
109          */
110         public function selectByBoundaries(array $condition = [], array $params = [], int $max_id = null, int $since_id = null, int $limit = self::LIMIT)
111         {
112                 $condition = DBA::collapseCondition($condition);
113
114                 $boundCondition = $condition;
115
116                 if (isset($max_id)) {
117                         $boundCondition[0] .= " AND `id` < ?";
118                         $boundCondition[] = $max_id;
119                 }
120
121                 if (isset($since_id)) {
122                         $boundCondition[0] .= " AND `id` > ?";
123                         $boundCondition[] = $since_id;
124                 }
125
126                 $params['limit'] = $limit;
127
128                 $models = $this->selectModels($boundCondition, $params);
129
130                 $totalCount = DBA::count(static::$table_name, $condition);
131
132                 return new static::$collection_class($models, $totalCount);
133         }
134
135         /**
136          * This method updates the database row from the model.
137          *
138          * @param BaseModel $model
139          * @return bool
140          * @throws \Exception
141          */
142         public function update(BaseModel $model)
143         {
144                 if ($this->dba->update(static::$table_name, $model->toArray(), ['id' => $model->id], $model->getOriginalData())) {
145                         $model->resetOriginalData();
146                         return true;
147                 }
148
149                 return false;
150         }
151
152         /**
153          * This method creates a new database row and returns a model if it was successful.
154          *
155          * @param array $fields
156          * @return BaseModel|bool
157          * @throws \Exception
158          */
159         public function insert(array $fields)
160         {
161                 $return = $this->dba->insert(static::$table_name, $fields);
162
163                 if (!$return) {
164                         throw new HTTPException\InternalServerErrorException('Unable to insert new row in table "' . static::$table_name . '"');
165                 }
166
167                 $fields['id'] = $this->dba->lastInsertId();
168                 $return = $this->create($fields);
169
170                 return $return;
171         }
172
173         /**
174          * Deletes the model record from the database.
175          *
176          * @param BaseModel $model
177          * @return bool
178          * @throws \Exception
179          */
180         public function delete(BaseModel &$model)
181         {
182                 if ($success = $this->dba->delete(static::$table_name, ['id' => $model->id])) {
183                         $model = null;
184                 }
185
186                 return $success;
187         }
188
189         /**
190          * Base instantiation method, can be overriden to add specific dependencies
191          *
192          * @param array $data
193          * @return BaseModel
194          */
195         protected function create(array $data)
196         {
197                 return new static::$model_class($this->dba, $this->logger, $data);
198         }
199
200         /**
201          * @param array $condition Query condition
202          * @param array $params    Additional query parameters
203          * @return BaseModel[]
204          * @throws \Exception
205          */
206         protected function selectModels(array $condition, array $params = [])
207         {
208                 $result = $this->dba->select(static::$table_name, [], $condition, $params);
209
210                 /** @var BaseModel $prototype */
211                 $prototype = null;
212
213                 $models = [];
214
215                 while ($record = $this->dba->fetch($result)) {
216                         if ($prototype === null) {
217                                 $prototype = $this->create($record);
218                                 $models[] = $prototype;
219                         } else {
220                                 $models[] = static::$model_class::createFromPrototype($prototype, $record);
221                         }
222                 }
223
224                 return $models;
225         }
226
227         /**
228          * @param BaseCollection $collection
229          */
230         public function saveCollection(BaseCollection $collection)
231         {
232                 $collection->map([$this, 'update']);
233         }
234 }