Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

mod/likes/classes/Elgg/Likes/Preloader.php (1 issue)

1
<?php
2
3
namespace Elgg\Likes;
4
5
/**
6
 * Likes preloader
7
 */
8
class Preloader {
9
10
	/**
11
	 * @var \Elgg\Likes\DataService
12
	 */
13
	protected $data;
14
15
	/**
16
	 * Create a preloader
17
	 *
18
	 * @param \Elgg\Likes\DataService $data a dataservice
19
	 */
20
	public function __construct(DataService $data) {
21
		$this->data = $data;
22
	}
23
24
	/**
25
	 * Preload likes for a set of items
26
	 *
27
	 * @param \ElggRiverItem[]|\ElggEntity[] $items the items to preload for
28
	 *
29
	 * @return void
30
	 */
31
	public function preloadForList(array $items) {
32
		$guids = $this->getGuidsToPreload($items);
33
		if (count($guids) < 2) {
34
			return;
35
		}
36
37
		$this->preloadCurrentUserLikes($guids);
38
39
		$guids_remaining = $this->preloadCountsFromHook($this->getEntities($guids));
40
		if ($guids_remaining) {
0 ignored issues
show
Bug Best Practice introduced by Steve Clay
The expression $guids_remaining of type integer[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
41
			$this->preloadCountsFromQuery($guids_remaining);
42
		}
43
	}
44
45
	/**
46
	 * Preload likes count based on guids
47
	 *
48
	 * @param int[] $guids the guids to preload
49
	 *
50
	 * @return void
51
	 */
52
	protected function preloadCountsFromQuery(array $guids) {
53
		$count_rows = elgg_get_annotations([
54
			'annotation_names' => 'likes',
55
			'guids' => $guids,
56
			'selects' => ['e.guid', 'COUNT(*) AS cnt'],
57
			'group_by' => 'e.guid',
58
			'limit' => false,
59
			'callback' => false,
60
		]);
61
		foreach ($guids as $guid) {
62
			$this->data->setNumLikes($guid, 0);
63
		}
64
		foreach ($count_rows as $row) {
65
			$this->data->setNumLikes($row->guid, $row->cnt);
66
		}
67
	}
68
69
	/**
70
	 * Preload based of entities
71
	 *
72
	 * @param \ElggEntity[] $entities given entities
73
	 *
74
	 * @return int[]
75
	 */
76
	protected function preloadCountsFromHook(array $entities) {
77
		$guids_not_loaded = [];
78
79
		foreach ($entities as $entity) {
80
			// BC with likes_count(). If this hook is used this preloader may not be of much help.
81
			$type = $entity->getType();
82
			$params = ['entity' => $entity];
83
84
			$num_likes = elgg_trigger_plugin_hook('likes:count', $type, $params, false);
85
			if ($num_likes) {
86
				$this->data->setNumLikes($entity->guid, $num_likes);
87
			} else {
88
				$guids_not_loaded[] = $entity->guid;
89
			}
90
		}
91
92
		return $guids_not_loaded;
93
	}
94
95
	/**
96
	 * Preload likes for given guids for current user
97
	 *
98
	 * @param int[] $guids preload guids
99
	 *
100
	 * @return void
101
	 */
102
	protected function preloadCurrentUserLikes(array $guids) {
103
		$owner_guid = elgg_get_logged_in_user_guid();
104
		if (!$owner_guid) {
105
			return;
106
		}
107
108
		$annotation_rows = elgg_get_annotations([
109
			'annotation_names' => 'likes',
110
			'annotation_owner_guids' => $owner_guid,
111
			'guids' => $guids,
112
			'limit' => false,
113
			'callback' => false,
114
		]);
115
116
		foreach ($guids as $guid) {
117
			$this->data->setLikedByCurrentUser($guid, false);
118
		}
119
		foreach ($annotation_rows as $row) {
120
			$this->data->setLikedByCurrentUser($row->entity_guid, true);
121
		}
122
	}
123
124
	/**
125
	 * Convert river items and/or entities to guids
126
	 *
127
	 * @param \ElggRiverItem[]|\ElggEntity[] $items the items to process
128
	 *
129
	 * @return int[]
130
	 */
131
	protected function getGuidsToPreload(array $items) {
132
		$guids = [];
133
134
		foreach ($items as $item) {
135
			if ($item instanceof \ElggRiverItem) {
136
				// only like group creation #3958
137
				if ($item->type == "group" && $item->view != "river/group/create") {
138
					continue;
139
				}
140
141
				$type = $item->type;
142
				$subtype = $item->subtype;
143
				$likable = (bool) elgg_trigger_plugin_hook('likes:is_likable', "$type:$subtype", [], false);
144
				if (!$likable) {
145
					continue;
146
				}
147
148
				if ($item->annotation_id != 0) {
149
					continue;
150
				}
151
152
				if ($item->object_guid) {
153
					$guids[$item->object_guid] = true;
154
				}
155
			} elseif ($item instanceof \ElggEntity) {
156
				$type = $item->type;
157
				$subtype = $item->getSubtype();
158
				$likable = (bool) elgg_trigger_plugin_hook('likes:is_likable', "$type:$subtype", [], false);
159
				if ($likable) {
160
					$guids[$item->guid] = true;
161
				}
162
			}
163
		}
164
		return array_keys($guids);
165
	}
166
167
	/**
168
	 * Get entities in any order checking cache first
169
	 *
170
	 * @param int[] $guids guids of entities to return
171
	 *
172
	 * @return \ElggEntity[]
173
	 */
174
	protected function getEntities(array $guids) {
175
		// most objects are already preloaded
176
		$entities = [];
177
		$fetch_guids = [];
178
179
		foreach ($guids as $guid) {
180
			$entity = _elgg_services()->entityTable->getFromCache($guid);
181
			if ($entity) {
182
				$entities[] = $entity;
183
			} else {
184
				$fetch_guids[] = $guid;
185
			}
186
		}
187
		if ($fetch_guids) {
188
			$fetched = elgg_get_entities([
189
				'guids' => $fetch_guids,
190
				'limit' => false,
191
			]);
192
			array_splice($entities, count($entities), 0, $fetched);
193
		}
194
		return $entities;
195
	}
196
}
197