Completed
Push — master ( b4e787...2e096b )
by Nazar
04:03
created

Posts::get_total_count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 1
eloc 8
nc 1
nop 0
1
<?php
2
/**
3
 * @package   Blogs
4
 * @category  modules
5
 * @author    Nazar Mokrynskyi <[email protected]>
6
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
7
 * @license   MIT License, see license.txt
8
 */
9
namespace cs\modules\Blogs;
10
use
11
	cs\Event,
12
	cs\Cache\Prefix as Cache_prefix,
13
	cs\Config,
14
	cs\Language,
15
	cs\Language\Prefix as Language_prefix,
16
	cs\User,
17
	cs\CRUD_helpers,
18
	cs\Singleton,
19
	cs\plugins\Json_ld\Json_ld;
20
21
/**
22
 * @method static $this instance($check = false)
23
 */
24
class Posts {
25
	use
26
		CRUD_helpers,
27
		Singleton;
28
	protected $data_model                  = [
29
		'id'       => 'int:0',
30
		'user'     => 'int:0',
31
		'date'     => 'int:0',
32
		'title'    => 'ml:text',
33
		'path'     => 'ml:text',
34
		'content'  => 'ml:html',
35
		'draft'    => 'int:0..1',
36
		'sections' => [
37
			'data_model' => [
38
				'id'      => 'int:0',
39
				'section' => 'int:0'
40
			]
41
		],
42
		'tags'     => [
43
			'data_model'     => [
44
				'id'  => 'int:0',
45
				'tag' => 'int:0'
46
			],
47
			'language_field' => 'lang'
48
		]
49
	];
50
	protected $table                       = '[prefix]blogs_posts';
51
	protected $data_model_ml_group         = 'Blogs/posts';
52
	protected $data_model_files_tag_prefix = 'Blogs/posts';
53
	/**
54
	 * @var Cache_prefix
55
	 */
56
	protected $cache;
57
58
	protected function construct () {
59
		$this->cache = new Cache_prefix('Blogs');
60
		if (Config::instance()->module('Blogs')->allow_iframes_without_content) {
61
			$this->data_model['content'] = 'ml:html_iframe';
62
		}
63
	}
64
	/**
65
	 * Returns database index
66
	 *
67
	 * @return int
68
	 */
69
	protected function cdb () {
70
		return Config::instance()->module('Blogs')->db('posts');
71
	}
72
	/**
73
	 * Get data of specified post
74
	 *
75
	 * @param int|int[] $id
76
	 *
77
	 * @return array|false
78
	 */
79
	function get ($id) {
80
		if (is_array($id)) {
81
			foreach ($id as &$i) {
82
				$i = $this->get($i);
83
			}
84
			return $id;
85
		}
86
		$L        = Language::instance();
87
		$id       = (int)$id;
88
		$data     = $this->cache->get(
89
			"posts/$id/$L->clang",
90
			function () use ($id) {
91
				$data = $this->read($id);
92
				if ($data) {
93
					$data['short_content'] = truncate(explode('<!-- pagebreak -->', $data['content'])[0]);
94
					$data['tags']          = $this->read_tags_processing($data['tags']);
95
				}
96
				return $data;
97
			}
98
		);
99
		$Comments = null;
100
		Event::instance()->fire(
101
			'Comments/instance',
102
			[
103
				'Comments' => &$Comments
104
			]
105
		);
106
		/**
107
		 * @var \cs\modules\Comments\Comments $Comments
108
		 */
109
		$data['comments_count'] =
110
			Config::instance()->module('Blogs')->enable_comments && $Comments
111
				? $Comments->count($data['id'])
112
				: 0;
113
		return $data;
114
	}
115
	/**
116
	 * @param int $page
117
	 * @param int $count
118
	 *
119
	 * @return int[]
1 ignored issue
show
Documentation introduced by
Should the return type not be false|integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
120
	 */
121
	function get_all ($page, $count) {
122
		return $this->search([], $page, $count, 'id');
123
	}
124
	/**
125
	 * Transform tags ids back into array of strings
126
	 *
127
	 * @param int[] $tags
128
	 *
129
	 * @return string[]
130
	 */
131
	protected function read_tags_processing ($tags) {
132
		return array_column(Tags::instance()->get($tags) ?: [], 'text');
133
	}
134
	/**
135
	 * Get data of specified post
136
	 *
137
	 * @param int|int[] $id
138
	 *
139
	 * @return array|false
140
	 */
141
	function get_as_json_ld ($id) {
142
		$post = $this->get($id);
143
		if (!$post) {
144
			return false;
145
		}
146
		return $this->post_to_jsonld($post);
147
	}
148
	/**
149
	 * @param array|array[] $post
150
	 *
151
	 * @return array
152
	 */
153
	function post_to_jsonld ($post) {
154
		$base_structure = [
155
			'@context' =>
156
				[
157
					'content'        => 'articleBody',
158
					'title'          => 'headline',
159
					'comments_count' => 'commentCount',
160
					'tags'           => 'keywords',
161
					'datetime'       => null,
162
					'sections_paths' => null,
163
					'tags_paths'     => null
164
				] + Json_ld::context_stub(isset($post[0]) ? $post[0] : $post)
165
		];
166
		if (isset($post[0])) {
167
			return
168
				$base_structure +
169
				[
170
					'@graph' => array_map(
171
						[$this, 'post_to_jsonld_single_post'],
172
						$post
173
					)
174
				];
175
		}
176
		return
177
			$base_structure +
178
			$this->post_to_jsonld_single_post($post);
179
	}
180
	protected function post_to_jsonld_single_post ($post) {
181
		if (preg_match_all('/<img[^>]src=["\'](.*)["\']/Uims', $post['content'], $images)) {
182
			$images = $images[1];
183
		}
184
		$Sections = Sections::instance();
185
		$sections = [];
186
		if ($post['sections'] != [0]) {
187
			$sections = array_column(
188
				$Sections->get($post['sections']),
189
				'title'
190
			);
191
		}
192
		$L            = new Language_prefix('blogs_');
193
		$base_url     = Config::instance()->base_url();
194
		$module_path  = path($L->Blogs);
195
		$section_path = "$base_url/$module_path/".path($L->section);
196
		$tag_path     = "$base_url/$module_path/".path($L->tag);
197
		$url          = "$base_url/$module_path/$post[path]:$post[id]";
198
		return
199
			[
200
				'@id'            => $url,
201
				'@type'          => 'BlogPosting',
202
				'articleSection' => $sections,
203
				'author'         => Json_ld::Person($post['user']),
204
				'datePublished'  => Json_ld::Date($post['date']),
205
				'image'          => $images,
206
				'inLanguage'     => $L->clang,
207
				'url'            => $url,
208
				'datetime'       => $L->to_locale(date($L->_datetime_long, $post['date'] ?: TIME)),
209
				'sections_paths' => array_map(
210
					function ($section) use ($section_path, $Sections) {
211
						$section = $Sections->get($section);
212
						return "$section_path/$section[full_path]";
213
					},
214
					$post['sections']
215
				),
216
				'tags_paths'     => array_map(
217
					function ($tag) use ($tag_path) {
218
						return "$tag_path/$tag";
219
					},
220
					$post['tags']
221
				)
222
			] + $post;
223
	}
224
	/**
225
	 * Get latest posts
226
	 *
227
	 * @param int $page
228
	 * @param int $count
229
	 *
230
	 * @return int[]
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
231
	 */
232
	function get_latest_posts ($page, $count) {
233
		$search_parameters = [
234
			'draft' => 0
235
		];
236
		return $this->search($search_parameters, $page, $count, 'date', false) ?: [];
237
	}
238
	/**
239
	 * Get posts for section
240
	 *
241
	 * @param int $section
242
	 * @param int $page
243
	 * @param int $count
244
	 *
245
	 * @return int[]
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
246
	 */
247
	function get_for_section ($section, $page, $count) {
248
		$search_parameters = [
249
			'draft'    => 0,
250
			'sections' => [
251
				'section' => $section
252
			]
253
		];
254
		return $this->search($search_parameters, $page, $count, 'date', false) ?: [];
255
	}
256
	/**
257
	 * Get number of posts for section
258
	 *
259
	 * @param int $section
260
	 *
261
	 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be false|integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
262
	 */
263
	function get_for_section_count ($section) {
264
		$search_parameters = [
265
			'draft'       => 0,
266
			'sections'    => [
267
				'section' => $section
268
			],
269
			'total_count' => true
270
		];
271
		return $this->search($search_parameters);
272
	}
273
	/**
274
	 * Get posts for tag
275
	 *
276
	 * @param int    $tag
277
	 * @param string $lang
278
	 * @param int    $page
279
	 * @param int    $count
280
	 *
281
	 * @return int[]
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
282
	 */
283
	function get_for_tag ($tag, $lang, $page, $count) {
284
		$search_parameters = [
285
			'draft' => 0,
286
			'tags'  => [
287
				'tag'  => $tag,
288
				'lang' => $lang
289
			]
290
		];
291
		return $this->search($search_parameters, $page, $count, 'date', false) ?: [];
292
	}
293
	/**
294
	 * Get number of posts for tag
295
	 *
296
	 * @param int    $tag
297
	 * @param string $lang
298
	 *
299
	 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be false|integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
300
	 */
301
	function get_for_tag_count ($tag, $lang) {
302
		$search_parameters = [
303
			'draft'       => 0,
304
			'tags'        => [
305
				'tag'  => $tag,
306
				'lang' => $lang
307
			],
308
			'total_count' => true
309
		];
310
		return $this->search($search_parameters);
311
	}
312
	/**
313
	 * Get drafts
314
	 *
315
	 * @param int $user
316
	 * @param int $page
317
	 * @param int $count
318
	 *
319
	 * @return int[]
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
320
	 */
321
	function get_drafts ($user, $page, $count) {
322
		$search_parameters = [
323
			'user'  => $user,
324
			'draft' => 1
325
		];
326
		return $this->search($search_parameters, $page, $count, 'date', false) ?: [];
327
	}
328
	/**
329
	 * Get number of drafts
330
	 *
331
	 * @param int $user
332
	 *
333
	 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be false|integer|array|double|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
334
	 */
335
	function get_drafts_count ($user) {
336
		$search_parameters = [
337
			'user'        => $user,
338
			'draft'       => 1,
339
			'total_count' => true
340
		];
341
		return $this->search($search_parameters);
342
	}
343
	/**
344
	 * Add new post
345
	 *
346
	 * @param string   $title
347
	 * @param string   $path
348
	 * @param string   $content
349
	 * @param int[]    $sections
350
	 * @param string[] $tags
351
	 * @param bool     $draft
352
	 *
353
	 * @return false|int Id of created post on success of <b>false</> on failure
1 ignored issue
show
Documentation introduced by
Should the return type not be false|integer|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
354
	 */
355
	function add ($title, $path, $content, $sections, $tags, $draft) {
356
		if (!$this->check_arguments($content, $sections, $tags)) {
357
			return false;
358
		}
359
		$id = $this->create(
360
			User::instance()->id,
361
			$draft ? 0 : time(),
362
			$title,
363
			path($path ?: $title),
364
			$content,
365
			$draft,
366
			$sections,
367
			$this->prepare_tags($tags)
368
		);
369
		if ($id) {
370
			$this->cache_cleanups($id);
371
		}
372
		return $id;
373
	}
374
	/**
375
	 * Transform array of string tags into array of their ids
376
	 *
377
	 * @param string[] $tags
378
	 *
379
	 * @return int[]
1 ignored issue
show
Documentation introduced by
Should the return type not be integer|string|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
380
	 */
381
	protected function prepare_tags ($tags) {
382
		return Tags::instance()->add($tags) ?: [];
383
	}
384
	/**
385
	 * @param string   $content
386
	 * @param int[]    $sections
387
	 * @param string[] $tags
388
	 *
389
	 * @return bool
390
	 */
391
	protected function check_arguments ($content, &$sections, $tags) {
392
		if (empty($tags) || empty($content)) {
393
			return false;
394
		}
395
		$sections = array_intersect(
396
			array_column(Sections::instance()->get_all(), 'id'),
397
			$sections
398
		);
399
		return $sections && count($sections) <= Config::instance()->module('Blogs')->max_sections;
400
	}
401
	/**
402
	 * @param int $id
403
	 */
404
	protected function cache_cleanups ($id) {
405
		$Cache = $this->cache;
406
		unset(
407
			$Cache->{"posts/$id"},
408
			$Cache->sections,
409
			$Cache->total_count
410
		);
411
	}
412
	/**
413
	 * Set data of specified post
414
	 *
415
	 * @param int      $id
416
	 * @param string   $title
417
	 * @param string   $path
418
	 * @param string   $content
419
	 * @param int[]    $sections
420
	 * @param string[] $tags
421
	 * @param bool     $draft
422
	 *
423
	 * @return bool
424
	 */
425
	function set ($id, $title, $path, $content, $sections, $tags, $draft) {
426
		if (!$this->check_arguments($content, $sections, $tags)) {
427
			return false;
428
		}
429
		$old_data = $this->get($id);
430
		$result   = $this->update(
431
			$id,
432
			$old_data['user'],
433
			$old_data['draft'] == 1 && $old_data['date'] == 0 && !$draft ? time() : $old_data['date'],
434
			$title,
435
			path($path ?: $title),
436
			$content,
437
			$draft,
438
			$sections,
439
			$this->prepare_tags($tags)
440
		);
441
		$this->cache_cleanups($id);
442
		return $result;
443
	}
444
	/**
445
	 * Delete specified post
446
	 *
447
	 * @param int $id
448
	 *
449
	 * @return bool
450
	 */
451
	function del ($id) {
452
		$id     = (int)$id;
453
		$result = $this->delete($id);
454
		if ($result) {
455
			$Comments = null;
456
			Event::instance()->fire(
457
				'Comments/instance',
458
				[
459
					'Comments' => &$Comments
460
				]
461
			);
462
			/**
463
			 * @var \cs\modules\Comments\Comments $Comments
464
			 */
465
			if ($Comments) {
466
				$Comments->del_all($id);
467
			}
468
			$this->cache_cleanups($id);
469
		}
470
		return $result;
471
	}
472
	/**
473
	 * Get total count of posts
474
	 *
475
	 * @return int
476
	 */
477
	function get_total_count () {
478
		return $this->cache->get(
479
			'total_count',
480
			function () {
481
				$search_parameters = [
482
					'draft'       => 0,
483
					'total_count' => true
484
				];
485
				return $this->search($search_parameters);
486
			}
487
		);
488
	}
489
}
490