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
|
|||
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 |
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.