Mercurial > hg > isophonics-drupal-site
comparison core/modules/comment/src/CommentStorage.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\comment; | |
4 | |
5 use Drupal\Core\Cache\CacheBackendInterface; | |
6 use Drupal\Core\Database\Connection; | |
7 use Drupal\Core\Entity\EntityManagerInterface; | |
8 use Drupal\Core\Entity\EntityTypeInterface; | |
9 use Drupal\Core\Entity\EntityInterface; | |
10 use Drupal\Core\Entity\FieldableEntityInterface; | |
11 use Drupal\Core\Entity\Sql\SqlContentEntityStorage; | |
12 use Drupal\Core\Session\AccountInterface; | |
13 use Drupal\Core\Language\LanguageManagerInterface; | |
14 use Symfony\Component\DependencyInjection\ContainerInterface; | |
15 | |
16 /** | |
17 * Defines the storage handler class for comments. | |
18 * | |
19 * This extends the Drupal\Core\Entity\Sql\SqlContentEntityStorage class, | |
20 * adding required special handling for comment entities. | |
21 */ | |
22 class CommentStorage extends SqlContentEntityStorage implements CommentStorageInterface { | |
23 | |
24 /** | |
25 * The current user. | |
26 * | |
27 * @var \Drupal\Core\Session\AccountInterface | |
28 */ | |
29 protected $currentUser; | |
30 | |
31 /** | |
32 * Constructs a CommentStorage object. | |
33 * | |
34 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info | |
35 * An array of entity info for the entity type. | |
36 * @param \Drupal\Core\Database\Connection $database | |
37 * The database connection to be used. | |
38 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
39 * The entity manager. | |
40 * @param \Drupal\Core\Session\AccountInterface $current_user | |
41 * The current user. | |
42 * @param \Drupal\Core\Cache\CacheBackendInterface $cache | |
43 * Cache backend instance to use. | |
44 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager | |
45 * The language manager. | |
46 */ | |
47 public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, AccountInterface $current_user, CacheBackendInterface $cache, LanguageManagerInterface $language_manager) { | |
48 parent::__construct($entity_info, $database, $entity_manager, $cache, $language_manager); | |
49 $this->currentUser = $current_user; | |
50 } | |
51 | |
52 /** | |
53 * {@inheritdoc} | |
54 */ | |
55 public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) { | |
56 return new static( | |
57 $entity_info, | |
58 $container->get('database'), | |
59 $container->get('entity.manager'), | |
60 $container->get('current_user'), | |
61 $container->get('cache.entity'), | |
62 $container->get('language_manager') | |
63 ); | |
64 } | |
65 | |
66 /** | |
67 * {@inheritdoc} | |
68 */ | |
69 public function getMaxThread(CommentInterface $comment) { | |
70 $query = $this->database->select('comment_field_data', 'c') | |
71 ->condition('entity_id', $comment->getCommentedEntityId()) | |
72 ->condition('field_name', $comment->getFieldName()) | |
73 ->condition('entity_type', $comment->getCommentedEntityTypeId()) | |
74 ->condition('default_langcode', 1); | |
75 $query->addExpression('MAX(thread)', 'thread'); | |
76 return $query->execute() | |
77 ->fetchField(); | |
78 } | |
79 | |
80 /** | |
81 * {@inheritdoc} | |
82 */ | |
83 public function getMaxThreadPerThread(CommentInterface $comment) { | |
84 $query = $this->database->select('comment_field_data', 'c') | |
85 ->condition('entity_id', $comment->getCommentedEntityId()) | |
86 ->condition('field_name', $comment->getFieldName()) | |
87 ->condition('entity_type', $comment->getCommentedEntityTypeId()) | |
88 ->condition('thread', $comment->getParentComment()->getThread() . '.%', 'LIKE') | |
89 ->condition('default_langcode', 1); | |
90 $query->addExpression('MAX(thread)', 'thread'); | |
91 return $query->execute() | |
92 ->fetchField(); | |
93 } | |
94 | |
95 /** | |
96 * {@inheritdoc} | |
97 */ | |
98 public function getDisplayOrdinal(CommentInterface $comment, $comment_mode, $divisor = 1) { | |
99 // Count how many comments (c1) are before $comment (c2) in display order. | |
100 // This is the 0-based display ordinal. | |
101 $query = $this->database->select('comment_field_data', 'c1'); | |
102 $query->innerJoin('comment_field_data', 'c2', 'c2.entity_id = c1.entity_id AND c2.entity_type = c1.entity_type AND c2.field_name = c1.field_name'); | |
103 $query->addExpression('COUNT(*)', 'count'); | |
104 $query->condition('c2.cid', $comment->id()); | |
105 if (!$this->currentUser->hasPermission('administer comments')) { | |
106 $query->condition('c1.status', CommentInterface::PUBLISHED); | |
107 } | |
108 | |
109 if ($comment_mode == CommentManagerInterface::COMMENT_MODE_FLAT) { | |
110 // For rendering flat comments, cid is used for ordering comments due to | |
111 // unpredictable behavior with timestamp, so we make the same assumption | |
112 // here. | |
113 $query->condition('c1.cid', $comment->id(), '<'); | |
114 } | |
115 else { | |
116 // For threaded comments, the c.thread column is used for ordering. We can | |
117 // use the sorting code for comparison, but must remove the trailing | |
118 // slash. | |
119 $query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) - 1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) - 1))'); | |
120 } | |
121 | |
122 $query->condition('c1.default_langcode', 1); | |
123 $query->condition('c2.default_langcode', 1); | |
124 | |
125 $ordinal = $query->execute()->fetchField(); | |
126 | |
127 return ($divisor > 1) ? floor($ordinal / $divisor) : $ordinal; | |
128 } | |
129 | |
130 /** | |
131 * {@inheritdoc} | |
132 */ | |
133 public function getNewCommentPageNumber($total_comments, $new_comments, FieldableEntityInterface $entity, $field_name) { | |
134 $field = $entity->getFieldDefinition($field_name); | |
135 $comments_per_page = $field->getSetting('per_page'); | |
136 | |
137 if ($total_comments <= $comments_per_page) { | |
138 // Only one page of comments. | |
139 $count = 0; | |
140 } | |
141 elseif ($field->getSetting('default_mode') == CommentManagerInterface::COMMENT_MODE_FLAT) { | |
142 // Flat comments. | |
143 $count = $total_comments - $new_comments; | |
144 } | |
145 else { | |
146 // Threaded comments. | |
147 | |
148 // 1. Find all the threads with a new comment. | |
149 $unread_threads_query = $this->database->select('comment_field_data', 'comment') | |
150 ->fields('comment', ['thread']) | |
151 ->condition('entity_id', $entity->id()) | |
152 ->condition('entity_type', $entity->getEntityTypeId()) | |
153 ->condition('field_name', $field_name) | |
154 ->condition('status', CommentInterface::PUBLISHED) | |
155 ->condition('default_langcode', 1) | |
156 ->orderBy('created', 'DESC') | |
157 ->orderBy('cid', 'DESC') | |
158 ->range(0, $new_comments); | |
159 | |
160 // 2. Find the first thread. | |
161 $first_thread_query = $this->database->select($unread_threads_query, 'thread'); | |
162 $first_thread_query->addExpression('SUBSTRING(thread, 1, (LENGTH(thread) - 1))', 'torder'); | |
163 $first_thread = $first_thread_query | |
164 ->fields('thread', ['thread']) | |
165 ->orderBy('torder') | |
166 ->range(0, 1) | |
167 ->execute() | |
168 ->fetchField(); | |
169 | |
170 // Remove the final '/'. | |
171 $first_thread = substr($first_thread, 0, -1); | |
172 | |
173 // Find the number of the first comment of the first unread thread. | |
174 $count = $this->database->query('SELECT COUNT(*) FROM {comment_field_data} WHERE entity_id = :entity_id | |
175 AND entity_type = :entity_type | |
176 AND field_name = :field_name | |
177 AND status = :status | |
178 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < :thread | |
179 AND default_langcode = 1', [ | |
180 ':status' => CommentInterface::PUBLISHED, | |
181 ':entity_id' => $entity->id(), | |
182 ':field_name' => $field_name, | |
183 ':entity_type' => $entity->getEntityTypeId(), | |
184 ':thread' => $first_thread, | |
185 ])->fetchField(); | |
186 } | |
187 | |
188 return $comments_per_page > 0 ? (int) ($count / $comments_per_page) : 0; | |
189 } | |
190 | |
191 /** | |
192 * {@inheritdoc} | |
193 */ | |
194 public function getChildCids(array $comments) { | |
195 return $this->database->select('comment_field_data', 'c') | |
196 ->fields('c', ['cid']) | |
197 ->condition('pid', array_keys($comments), 'IN') | |
198 ->condition('default_langcode', 1) | |
199 ->execute() | |
200 ->fetchCol(); | |
201 } | |
202 | |
203 /** | |
204 * {@inheritdoc} | |
205 * | |
206 * To display threaded comments in the correct order we keep a 'thread' field | |
207 * and order by that value. This field keeps this data in | |
208 * a way which is easy to update and convenient to use. | |
209 * | |
210 * A "thread" value starts at "1". If we add a child (A) to this comment, | |
211 * we assign it a "thread" = "1.1". A child of (A) will have "1.1.1". Next | |
212 * brother of (A) will get "1.2". Next brother of the parent of (A) will get | |
213 * "2" and so on. | |
214 * | |
215 * First of all note that the thread field stores the depth of the comment: | |
216 * depth 0 will be "X", depth 1 "X.X", depth 2 "X.X.X", etc. | |
217 * | |
218 * Now to get the ordering right, consider this example: | |
219 * | |
220 * 1 | |
221 * 1.1 | |
222 * 1.1.1 | |
223 * 1.2 | |
224 * 2 | |
225 * | |
226 * If we "ORDER BY thread ASC" we get the above result, and this is the | |
227 * natural order sorted by time. However, if we "ORDER BY thread DESC" | |
228 * we get: | |
229 * | |
230 * 2 | |
231 * 1.2 | |
232 * 1.1.1 | |
233 * 1.1 | |
234 * 1 | |
235 * | |
236 * Clearly, this is not a natural way to see a thread, and users will get | |
237 * confused. The natural order to show a thread by time desc would be: | |
238 * | |
239 * 2 | |
240 * 1 | |
241 * 1.2 | |
242 * 1.1 | |
243 * 1.1.1 | |
244 * | |
245 * which is what we already did before the standard pager patch. To achieve | |
246 * this we simply add a "/" at the end of each "thread" value. This way, the | |
247 * thread fields will look like this: | |
248 * | |
249 * 1/ | |
250 * 1.1/ | |
251 * 1.1.1/ | |
252 * 1.2/ | |
253 * 2/ | |
254 * | |
255 * we add "/" since this char is, in ASCII, higher than every number, so if | |
256 * now we "ORDER BY thread DESC" we get the correct order. However this would | |
257 * spoil the reverse ordering, "ORDER BY thread ASC" -- here, we do not need | |
258 * to consider the trailing "/" so we use a substring only. | |
259 */ | |
260 public function loadThread(EntityInterface $entity, $field_name, $mode, $comments_per_page = 0, $pager_id = 0) { | |
261 $query = $this->database->select('comment_field_data', 'c'); | |
262 $query->addField('c', 'cid'); | |
263 $query | |
264 ->condition('c.entity_id', $entity->id()) | |
265 ->condition('c.entity_type', $entity->getEntityTypeId()) | |
266 ->condition('c.field_name', $field_name) | |
267 ->condition('c.default_langcode', 1) | |
268 ->addTag('entity_access') | |
269 ->addTag('comment_filter') | |
270 ->addMetaData('base_table', 'comment') | |
271 ->addMetaData('entity', $entity) | |
272 ->addMetaData('field_name', $field_name); | |
273 | |
274 if ($comments_per_page) { | |
275 $query = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender') | |
276 ->limit($comments_per_page); | |
277 if ($pager_id) { | |
278 $query->element($pager_id); | |
279 } | |
280 | |
281 $count_query = $this->database->select('comment_field_data', 'c'); | |
282 $count_query->addExpression('COUNT(*)'); | |
283 $count_query | |
284 ->condition('c.entity_id', $entity->id()) | |
285 ->condition('c.entity_type', $entity->getEntityTypeId()) | |
286 ->condition('c.field_name', $field_name) | |
287 ->condition('c.default_langcode', 1) | |
288 ->addTag('entity_access') | |
289 ->addTag('comment_filter') | |
290 ->addMetaData('base_table', 'comment') | |
291 ->addMetaData('entity', $entity) | |
292 ->addMetaData('field_name', $field_name); | |
293 $query->setCountQuery($count_query); | |
294 } | |
295 | |
296 if (!$this->currentUser->hasPermission('administer comments')) { | |
297 $query->condition('c.status', CommentInterface::PUBLISHED); | |
298 if ($comments_per_page) { | |
299 $count_query->condition('c.status', CommentInterface::PUBLISHED); | |
300 } | |
301 } | |
302 if ($mode == CommentManagerInterface::COMMENT_MODE_FLAT) { | |
303 $query->orderBy('c.cid', 'ASC'); | |
304 } | |
305 else { | |
306 // See comment above. Analysis reveals that this doesn't cost too | |
307 // much. It scales much much better than having the whole comment | |
308 // structure. | |
309 $query->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torder'); | |
310 $query->orderBy('torder', 'ASC'); | |
311 } | |
312 | |
313 $cids = $query->execute()->fetchCol(); | |
314 | |
315 $comments = []; | |
316 if ($cids) { | |
317 $comments = $this->loadMultiple($cids); | |
318 } | |
319 | |
320 return $comments; | |
321 } | |
322 | |
323 /** | |
324 * {@inheritdoc} | |
325 */ | |
326 public function getUnapprovedCount() { | |
327 return $this->database->select('comment_field_data', 'c') | |
328 ->condition('status', CommentInterface::NOT_PUBLISHED, '=') | |
329 ->condition('default_langcode', 1) | |
330 ->countQuery() | |
331 ->execute() | |
332 ->fetchField(); | |
333 } | |
334 | |
335 } |