ILIAS  trunk Revision v12.0_alpha-1221-g4e438232683
NewsRepository.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
30
36{
37 public function __construct(
38 protected readonly \ilDBInterface $db,
39 protected readonly Factory $factory
40 ) {
41 }
42
43 public function findById(int $news_id): ?NewsItem
44 {
45 $query = "SELECT * FROM il_news_item WHERE id = %s";
46 $result = $this->db->queryF($query, [\ilDBConstants::T_INTEGER], [$news_id]);
47
48 return $result->numRows()
49 ? $this->factory->newsItem($this->db->fetchAssoc($result))
50 : null;
51 }
52
57 public function findByIds(array $news_ids): array
58 {
59 if (empty($news_ids)) {
60 return [];
61 }
62
63 $result = $this->db->query($this->buildFindQuery($news_ids));
64 return array_map(fn($row) => $this->factory->newsItem($row), $this->db->fetchAll($result));
65 }
66
71 public function filterContext(array $contexts, NewsCriteria $criteria): array
72 {
73 $obj_ids = array_map(fn($context) => $context->getObjId(), $contexts);
74
75 $values = [];
76 $types = [];
77 $query = "SELECT DISTINCT (context_obj_id) AS obj_id FROM il_news_item WHERE ";
78 $query .= $this->db->in('context_obj_id', $obj_ids, false, \ilDBConstants::T_INTEGER);
79
80 if ($criteria->getPeriod() > 0) {
81 $query .= " AND creation_date >= %s";
82 $values[] = self::parseTimePeriod($criteria->getPeriod());
84 }
85
86 if ($criteria->getStartDate()) {
87 $query .= " AND creation_date >= %s";
88 $values[] = $criteria->getStartDate()->format('Y-m-d H:i:s');
90 }
91
92 $result = $this->db->queryF($query, $types, $values);
93 $needed_obj_ids = array_column($this->db->fetchAll($result), 'obj_id', 'obj_id');
94
95 return array_filter($contexts, fn($context) => isset($needed_obj_ids[$context->getObjId()]));
96 }
97
98
104 public function loadLazyItems(array $news_ids, array $group_context_types): array
105 {
106 if (empty($news_ids)) {
107 return [];
108 }
109
110 $result = $this->db->query($this->buildFindQuery($news_ids));
111 $news_items = [];
112 $additional_obj_ids = [];
113
114 foreach ($this->db->fetchAll($result) as $row) {
115 $news_item = $this->factory->newsItem($row);
116
117 if (in_array($news_item->getContextObjType(), $group_context_types)) {
118 $additional_obj_ids[] = $news_item->getContextObjId();
119 }
120
121 $news_items[] = $news_item;
122 }
123
124 if (empty($additional_obj_ids)) {
125 return $news_items;
126 }
127
128 // Fetch all additional items with same context_obj_id for grouping
129 $query = $this->buildFindQuery()
130 . " WHERE " . $this->db->in('context_obj_id', $additional_obj_ids, false, \ilDBConstants::T_INTEGER)
131 . " AND " . $this->db->in('id', $news_ids, true, \ilDBConstants::T_INTEGER);
132 $result = $this->db->query($query);
133
134 return array_merge(
135 $news_items,
136 array_map(fn($row) => $this->factory->newsItem($row), $this->db->fetchAll($result))
137 );
138 }
139
140 private function buildFindQuery(?array $news_ids = null): string
141 {
142 $query = "
143 SELECT il_news_item.*,
144 COALESCE(
145 (SELECT ref_id FROM object_reference WHERE object_reference.obj_id = il_news_item.context_obj_id LIMIT 1),
146 0
147 ) AS ref_id
148 FROM il_news_item ";
149
150 if ($news_ids !== null) {
151 $query .= "WHERE " . $this->db->in('id', $news_ids, false, \ilDBConstants::T_INTEGER);
152 }
153
154 return $query;
155 }
156
160 public function findByContextsBatch(array $contexts, NewsCriteria $criteria): NewsCollection
161 {
162 if (empty($contexts)) {
163 return new NewsCollection();
164 }
165
166 $obj_ids = array_map(fn($context) => $context->getObjId(), $contexts);
167 $result = $this->db->queryF(...$this->buildBatchQuery($obj_ids, $criteria));
168
169 $items = [];
170 $user_read = [];
171
172 while ($row = $this->db->fetchAssoc($result)) {
173 $items[] = $this->factory->newsItem($row);
174 $user_read[$row['id']] = isset($row['user_read']) && $row['user_read'] !== 0;
175 }
176
177 $collection = new NewsCollection($items);
178 if ($criteria->isIncludeReadStatus()) {
179 $collection->setUserReadStatus($criteria->getReadUserId(), $user_read);
180 }
181
182 return $collection;
183 }
184
188 public function findByContextsBatchLazy(array $contexts, NewsCriteria $criteria): NewsCollection
189 {
190 if (empty($contexts)) {
191 return new NewsCollection();
192 }
193
194 $obj_ids = array_map(fn($context) => $context->getObjId(), $contexts);
195 $result = $this->db->queryF(...$this->buildBatchQuery($obj_ids, $criteria, true));
196
197 $items = [];
198 $user_read = [];
199 while ($row = $this->db->fetchAssoc($result)) {
200 $items[] = $row['id'];
201 $user_read[$row['id']] = isset($row['user_read']) && $row['user_read'] !== 0;
202 }
203
204 $collection = new LazyNewsCollection($items, fn(...$args) => $this->loadLazyItems(...$args));
205 if ($criteria->isIncludeReadStatus()) {
206 $collection->setUserReadStatus($criteria->getReadUserId(), $user_read);
207 }
208
209 return $collection;
210 }
211
216 public function countByContextsBatch(array $contexts): array
217 {
218 $context_map = [];
219 foreach ($contexts as $context) {
220 $context_map[$context->getObjId()] = $context;
221 }
222
223 $in_clause = $this->db->in('context_obj_id', array_keys($context_map), false, ilDBConstants::T_INTEGER);
224 $query = "SELECT context_obj_id, count(context_obj_id) as count FROM il_news_item WHERE {$in_clause} GROUP BY context_obj_id";
225 $result = $this->db->query($query);
226
227 $count = [];
228 foreach ($this->db->fetchAll($result) as $row) {
229 $count[] = [
230 $context_map[$row['context_obj_id']],
231 $row['count']
232 ];
233 }
234
235 return $count;
236 }
237
238 private function buildBatchQuery(array $obj_ids, NewsCriteria $criteria, bool $only_id = false): array
239 {
240 $values = [];
241 $types = [];
242 $joins = '';
243
244 if ($only_id) {
245 $columns = ['il_news_item.id'];
246 } else {
247 $columns = [
248 'il_news_item.*',
249 'COALESCE((SELECT ref_id FROM object_reference WHERE object_reference.obj_id = il_news_item.context_obj_id LIMIT 1), 0) AS ref_id'
250 ];
251 }
252
253 if ($criteria->isIncludeReadStatus()) {
254 if ($criteria->getReadUserId() === null) {
255 throw new \InvalidArgumentException("Read user id is required for read status");
256 }
257
258 $columns[] = 'il_news_read.user_id AS user_read';
259 $joins .= 'LEFT JOIN il_news_read ON il_news_item.id = il_news_read.news_id AND il_news_read.user_id = %s ';
260
261 $values[] = $criteria->getReadUserId();
262 $types[] = ilDBConstants::T_INTEGER;
263 }
264
265 $query = "SELECT " . join(', ', $columns) . " FROM il_news_item {$joins} WHERE "
266 . $this->db->in('context_obj_id', $obj_ids, false, ilDBConstants::T_INTEGER);
267
268 if ($criteria->getPeriod() > 0) {
269 $query .= " AND creation_date >= %s";
270 $values[] = self::parseTimePeriod($criteria->getPeriod());
272 }
273
274 if ($criteria->getStartDate()) {
275 $query .= " AND creation_date >= %s";
276 $values[] = $criteria->getStartDate()->format('Y-m-d H:i:s');
278 }
279
280 if ($criteria->isNoAutoGenerated()) {
281 $query .= " AND priority = 1 AND content_type = 'text'";
282 }
283
284 if ($criteria->getMinPriority() !== null || $criteria->getMaxPriority() !== null) {
285 $operator = $criteria->getMinPriority() !== null ? '>=' : '<=';
286 $query .= " AND n.priority {$operator} %s";
287 $values[] = $criteria->getMinPriority();
288 $types[] = ilDBConstants::T_INTEGER;
289 }
290
291 if ($criteria->isOnlyPublic()) {
292 $query .= " AND visibility = '" . NEWS_PUBLIC . "'";
293 }
294
295 $query .= " ORDER BY creation_date DESC";
296
297 return [$query, $types, $values];
298 }
299
300 private static function parseTimePeriod(string|int $time_period): string
301 {
302 // time period is a number of days
303 if (is_numeric($time_period) && $time_period > 0) {
304 return date('Y-m-d H:i:s', time() - ($time_period * 24 * 60 * 60));
305 }
306
307 // time period is datetime (string)
308 if (preg_match("/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/", $time_period)) {
309 return $time_period;
310 }
311
312 return '';
313 }
314}
factory()
Factory for creating News DTOs from database results (arrays)
Definition: Factory.php:29
This class is a special implementation of a NewsCollection that is designed to load the complete News...
Optimized News Collection with memory-efficient data structures to support large news feeds.
News Context DTO represents a context where news items can be associated with.
Definition: NewsContext.php:29
News Criteria DTO for querying news items supports caching, JSON serialization, and validation.
News Item DTO for transfer of news items.
Definition: NewsItem.php:29
News Repository provides basic CRUD operations and optimized database access for news operations with...
filterContext(array $contexts, NewsCriteria $criteria)
findByContextsBatchLazy(array $contexts, NewsCriteria $criteria)
__construct(protected readonly \ilDBInterface $db, protected readonly Factory $factory)
static parseTimePeriod(string|int $time_period)
loadLazyItems(array $news_ids, array $group_context_types)
buildBatchQuery(array $obj_ids, NewsCriteria $criteria, bool $only_id=false)
findByContextsBatch(array $contexts, NewsCriteria $criteria)
const NEWS_PUBLIC
Class ilDBConstants.
Interface ilDBInterface.