COMMENTLIST   F
last analyzed

Complexity

Total Complexity 65

Size/Duplication

Total Lines 570
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 252
c 0
b 0
f 0
dl 0
loc 570
rs 3.2
ccs 0
cts 215
cp 0
wmc 65

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
C _get_data_by_user() 0 121 12
B _get_data_by_recent() 0 57 9
A render() 0 7 2
B display() 0 39 10
A _get_data_by_dates() 0 20 1
A _get_data_by_search() 0 42 5
A _comment_url() 0 29 2
F _get_comment_data() 0 150 19
A _get_data_by_ep() 0 35 4

How to fix   Complexity   

Complex Class

Complex classes like COMMENTLIST often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use COMMENTLIST, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*	 The class for displaying one or more comments.
4
    (There's also a function for adding a new comment to the DB because I wasn't
5
    sure where else to put it!).
6
7
    This works similarly to the HANSARDLIST class.
8
9
    To display all the comments for an epobject you'll do:
10
11
        $args = array ('epobject_id' => $epobject_id);
12
        $COMMENTLIST = new COMMENTLIST;
13
        $COMMENTLIST->display ('ep', $args);
14
15
    This will call the _get_data_by_ep() function which passes variables to the
16
    _get_comment_data() function. This gets the comments from the DB and returns
17
    an array of comments.
18
19
    The render() function is then called, which includes a template and
20
    goes through the array, displaying the comments. See the HTML comments.php
21
    template for the format.
22
    NOTE: You'll need to pass the 'body' of the comment through filter_user_input()
23
    and linkify() first.
24
25
    You could also just call the $COMMENTLIST->render() array with an array
26
    of comment data and display directly (used for previewing user input).
27
28
*/
29
30
class COMMENTLIST {
31
    public function __construct() {
32
        global $this_page;
33
34
        $this->db = new ParlDB();
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
35
36
        // We use this to create permalinks to comments. For the moment we're
37
        // assuming they're on the same page we're currently looking at:
38
        // debate, wran, etc.
39
        $this->page = $this_page;
0 ignored issues
show
Bug Best Practice introduced by
The property page does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
40
41
    }
42
43
44
    public function display($view, $args = [], $format = 'html') {
45
        // $view is what we're viewing by:
46
        //	'ep' is all the comments attached to an epobject.
47
        //	'user' is all the comments written by a user.
48
        //	'recent' is the most recent comments.
49
50
        // $args is an associative array of stuff like
51
        //	'epobject_id' => '37'
52
        // Where 'epobject_id' is an epobject_id.
53
        // Or 'gid' is a hansard item gid.
54
55
        // Replace a hansard object gid with an epobject_id.
56
        //		$args = $this->_fix_gid($args);
57
58
        // $format is the format the data should be rendered in.
59
60
        if ($view == 'ep' || $view == 'user' || $view == 'recent' || $view == 'search' || $view == 'dates') {
61
            // What function do we call for this view?
62
            $function = '_get_data_by_' . $view;
63
            // Get all the dta that's to be rendered.
64
            $data = $this->$function($args);
65
66
        } else {
67
            // Don't have a valid $view;
68
            $PAGE->error_message("You haven't specified a view type.");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $PAGE seems to be never defined.
Loading history...
69
            return false;
70
        }
71
72
        if ($view == 'user') {
73
            $template = 'comments_user';
74
        } elseif ($view == 'recent' or $view == 'dates') {
75
            $template = 'comments_recent';
76
        } elseif ($view == 'search') {
77
            $template = 'comments_search';
78
        } else {
79
            $template = 'comments';
80
        }
81
82
        return $this->render($data, $format, $template);
83
    }
84
85
    public function render($data, $format = 'html', $template = 'comments') {
86
        if ($format == 'none') {
87
            return $data;
88
        }
89
90
        include(INCLUDESPATH . "easyparliament/templates/$format/$template.php");
91
        return true;
92
    }
93
94
    public function _get_data_by_ep($args) {
95
        // Get all the data attached to an epobject.
96
        global $PAGE;
97
98
        twfy_debug(get_class($this), "getting data by epobject");
99
100
        // What we return.
101
        $data = [];
102
        if (!is_numeric($args['epobject_id'])) {
103
            $PAGE->error_message("Sorry, we don't have a valid epobject id");
104
            return $data;
105
        }
106
107
        // For getting the data.
108
        $input =  [
109
            'amount' =>  [
110
                'user' => true,
111
            ],
112
            'where' =>  [
113
                'comments.epobject_id=' => $args['epobject_id'],
114
                'visible=' => '1',
115
            ],
116
            'order' => 'posted ASC',
117
        ];
118
119
        $commentsdata = $this->_get_comment_data($input);
120
121
        $data['comments'] = $commentsdata;
122
123
        if (isset($args['user_id']) && $args['user_id'] != '') {
124
            // We'll pass this on to the template so it can highlight the user's comments.
125
            $data['info']['user_id'] = $args['user_id'];
126
        }
127
128
        return $data;
129
130
    }
131
132
133
134
    public function _get_data_by_user($args) {
135
        // Get a user's most recent comments.
136
        // Could perhaps be modified to get different lists of a user's
137
        // comments by things in $args?
138
        global $PAGE;
139
140
        twfy_debug(get_class($this), "getting data by user");
141
142
        // What we return.
143
        $data = [];
144
145
        if (!is_numeric($args['user_id'])) {
146
            $PAGE->error_message("Sorry, we don't have a valid user id");
147
            return $data;
148
        }
149
150
        if (isset($args['num']) && is_numeric($args['num'])) {
151
            $num = $args['num'];
152
        } else {
153
            $num = 10;
154
        }
155
156
        if (isset($args['page']) && is_numeric($args['page']) && $args['page'] > 1) {
157
            $page = $args['page'];
158
        } else {
159
            $page = 1;
160
        }
161
162
        $limit = $num * ($page - 1) . ',' . $num;
163
164
        // We're getting the most recent comments posted to epobjects.
165
        // We're grouping them by epobject so we can just link to each hansard thing once.
166
        // When there are numerous comments on an epobject we're getting the most recent
167
        // 		comment_id and posted date.
168
        // We're getting the body details for the epobject.
169
        // We're NOT getting the comment bodies. Why? Because adding them to this query
170
        // would fetch the text for the oldest comment on an epobject group, rather
171
        // than the most recent. So we'll get the comment bodies later...
172
        $q = $this->db->query(
173
            "SELECT MAX(comments.comment_id) AS comment_id,
174
                                MAX(comments.posted) AS posted,
175
                                COUNT(*) AS total_comments,
176
                                comments.epobject_id,
177
                                hansard.major,
178
                                hansard.gid,
179
                                users.firstname,
180
                                users.lastname,
181
                                epobject.body
182
                        FROM 	comments
183
                            join hansard  on comments.epobject_id = hansard.epobject_id
184
                            join users    on comments.user_id = users.user_id
185
                            join epobject on comments.epobject_id = epobject.epobject_id
186
                        where	users.user_id=:user_id
187
                        AND 	visible='1'
188
                        GROUP BY epobject_id
189
                        ORDER BY posted DESC
190
                        LIMIT " . $limit,
191
            [':user_id' => $args['user_id']]
192
        );
193
194
        $comments = [];
195
        $comment_ids = [];
196
197
        if ($q->rows() > 0) {
198
199
            foreach ($q as $row) {
200
201
                $urldata = [
202
                    'major' => $row['major'],
203
                    'gid' => $row['gid'],
204
                    'comment_id' => $row['comment_id'],
205
                    'user_id' => $args['user_id'],
206
                ];
207
208
                $comments[] = [
209
                    'comment_id' => $row['comment_id'],
210
                    'posted' => $row['posted'],
211
                    'total_comments' => $row['total_comments'],
212
                    'epobject_id' => $row['epobject_id'],
213
                    'firstname' => $row['firstname'],
214
                    'lastname' => $row['lastname'],
215
                    // Hansard item body, not comment body.
216
                    'hbody' => $row['body'],
217
                    'url' => $this->_comment_url($urldata),
218
                ];
219
220
                // We'll need these for getting the comment bodies.
221
                $comment_ids[] = $row['comment_id'];
222
223
            }
224
225
            $in = implode(', ', $comment_ids);
226
227
            $r = $this->db->query("SELECT comment_id,
228
                                    body
229
                            FROM	comments
230
                            WHERE	comment_id IN ($in)
231
                            ");
232
233
            if ($r->rows() > 0) {
234
235
                $commentbodies = [];
236
237
                foreach ($r as $row2) {
238
                    $commentbodies[$row2['comment_id']] = $row2['body'];
239
                }
240
241
                // This does rely on both this and the previous query returning
242
                // stuff in the same order...
243
                foreach ($comments as $n => $commentdata) {
244
                    $comments[$n]['body'] = $commentbodies[ $comments[$n]['comment_id'] ];
245
                }
246
            }
247
        }
248
249
        $data['comments'] = $comments;
250
        $data['results_per_page'] = $num;
251
        $data['page'] = $page;
252
        $q = $this->db->query('SELECT COUNT(DISTINCT(epobject_id)) AS count FROM comments WHERE visible=1 AND user_id=' . $args['user_id'])->first();
253
        $data['total_results'] = $q['count'];
254
        return $data;
255
256
    }
257
258
259
260
    public function _get_data_by_recent($args) {
261
        // $args should contain 'num', indicating how many to get.
262
        // and perhaps pid too, for a particular person
263
264
        twfy_debug(get_class($this), "getting data by recent");
265
266
        // What we return.
267
        $data = [];
268
269
        if (isset($args['num']) && is_numeric($args['num'])) {
270
            $num = $args['num'];
271
        } else {
272
            $num = 25;
273
        }
274
275
        if (isset($args['page']) && is_numeric($args['page'])) {
276
            $page = $args['page'];
277
        } else {
278
            $page = 1;
279
        }
280
281
        $limit = $num * ($page - 1) . ',' . $num;
282
283
        $where = [
284
            'visible=' => '1',
285
        ];
286
        if (isset($args['pid']) && is_numeric($args['pid'])) {
287
            $where['person_id='] = $args['pid'];
288
        }
289
        $input =  [
290
            'amount' =>  [
291
                'user' => true,
292
            ],
293
            'where'  => $where,
294
            'order' => 'posted DESC',
295
            'limit' => $limit,
296
        ];
297
298
        $commentsdata = $this->_get_comment_data($input);
299
300
        $data['comments'] = $commentsdata;
301
        $data['results_per_page'] = $num;
302
        $data['page'] = $page;
303
        $params = [];
304
        if (isset($args['pid']) && is_numeric($args['pid'])) {
305
            $data['pid'] = $args['pid'];
306
            $q = 'SELECT title, given_name, family_name, lordofname, house FROM member m, person_names p WHERE m.person_id=p.person_id AND p.type="name" AND left_house="9999-12-31" AND m.person_id = :pid';
307
            $q = $this->db->query($q, [':pid' => $args['pid']])->first();
308
            $data['full_name'] = member_full_name($q['house'], $q['title'], $q['given_name'], $q['family_name'], $q['lordofname']);
309
            $q = 'SELECT COUNT(*) AS count FROM comments,hansard WHERE visible=1 AND comments.epobject_id = hansard.epobject_id and hansard.person_id = :pid';
310
            $params[':pid'] = $args['pid'];
311
        } else {
312
            $q = 'SELECT COUNT(*) AS count FROM comments WHERE visible=1';
313
        }
314
        $q = $this->db->query($q, $params)->first();
315
        $data['total_results'] = $q['count'];
316
        return $data;
317
    }
318
319
    public function _get_data_by_dates($args) {
320
        // $args should contain start_date and end_date
321
322
        twfy_debug(get_class($this), "getting data by recent");
323
        $data = [];
324
        $where = [
325
            'visible=' => '1',
326
            'date(posted)>=' => $args['start_date'],
327
            'date(posted)<=' => $args['end_date'],
328
        ];
329
        $input =  [
330
            'amount' =>  [
331
                'user' => true,
332
            ],
333
            'where'  => $where,
334
            'order'  => 'posted DESC',
335
        ];
336
        $commentsdata = $this->_get_comment_data($input);
337
        $data['comments'] = $commentsdata;
338
        return $data;
339
    }
340
341
    public function _get_data_by_search($args) {
342
        // $args should contain 'num', indicating how many to get.
343
344
        twfy_debug(get_class($this), "getting data by search");
345
346
        // What we return.
347
        $data = [];
348
349
        if (isset($args['num']) && is_numeric($args['num'])) {
350
            $num = $args['num'];
351
        } else {
352
            $num = 10;
353
        }
354
355
        if (isset($args['page']) && is_numeric($args['page'])) {
356
            $page = $args['page'];
357
        } else {
358
            $page = 1;
359
        }
360
361
        $limit = $num * ($page - 1) . ',' . $num;
362
363
        $input =  [
364
            'amount' =>  [
365
                'user' => true,
366
            ],
367
            'where'  =>  [
368
                'comments.body LIKE' => "%$args[s]%",
369
            ],
370
            'order' => 'posted DESC',
371
            'limit' => $limit,
372
        ];
373
374
        $commentsdata = $this->_get_comment_data($input);
375
376
        $data['comments'] = $commentsdata;
377
        $data['search'] = $args['s'];
378
        #		$data['results_per_page'] = $num;
379
        #		$data['page'] = $page;
380
        #		$q = $this->db->query('SELECT COUNT(*) AS count FROM comments WHERE visible=1')->first();
381
        #		$data['total_results'] = $q['count'];
382
        return $data;
383
    }
384
385
386
    public function _comment_url($urldata) {
387
        global $hansardmajors;
388
389
        // Pass it the major and gid of the comment's epobject and the comment_id.
390
        // And optionally the user's id, for highlighting the comments on the destination page.
391
        // It returns the URL for the comment.
392
393
        $major 		= $urldata['major'];
394
        $gid 		= $urldata['gid'];
395
        $comment_id = $urldata['comment_id'];
396
        $user_id = $urldata['user_id'] ?? false;
397
398
        // If you change stuff here, you might have to change it in
399
        // $COMMENT->_set_url() too...
400
401
        // We'll generate permalinks for each comment.
402
        // Assuming every comment is from the same major...
403
        $page = $hansardmajors[$major]['page'];
404
405
        $URL = new \MySociety\TheyWorkForYou\Url($page);
406
407
        $gid = fix_gid_from_db($gid); // In includes/utility.php
408
        $URL->insert(['id' => $gid ]);
409
        if ($user_id) {
410
            $URL->insert(['u' => $user_id]);
411
        }
412
        $url = $URL->generate() . '#c' . $comment_id;
413
414
        return $url;
415
    }
416
417
418
419
    /*	function _fix_gid($args) {
420
421
            // Replace a hansard object gid with an epobject_id.
422
            // $args may have a 'gid' element. If so, we replace it
423
            // with the hansard object's epobject_id as 'epobject_id', because
424
            // comments are tied to epobject_ids.
425
            // Returns the corrected $args array.
426
427
            global $this_page;
428
429
            if (isset($args['gid']) && !isset($args['epobject_id'])) {
430
431
                if ($this_page == 'wran' || $this_page == 'wrans') {
432
                    $gidextra = 'wrans';
433
                } else {
434
                    $gidextra = 'debate';
435
                }
436
437
                $q = $this->db->query ("SELECT epobject_id FROM hansard WHERE gid = 'uk.org.publicwhip/" . $gidextra . '/' . addslashes($args['gid']) . "'")->first();
438
439
                if ($q) {
440
                    unset($args['gid']);
441
                    $args['epobject_id'] = $q['epobject_id'];
442
                }
443
            }
444
445
            return $args;
446
447
        }
448
        */
449
450
    public function _get_comment_data($input) {
451
        // Generic function for getting hansard data from the DB.
452
        // It returns an empty array if no data was found.
453
        // It returns an array of items if 1 or more were found.
454
        // Each item is an array of key/value pairs.
455
        // eg:
456
        /*
457
            array (
458
                0	=> array (
459
                    'comment_id'	=> '2',
460
                    'user_id'		=> '10',
461
                    'body'			=> 'The text of the comment is here.',
462
                    etc...
463
                ),
464
                1	=> array (
465
                    'comment_id'	=> '3',
466
                    etc...
467
                )
468
            );
469
        */
470
471
        // $input is an array of things needed for the SQL query:
472
        // 'amount' has one or more of :
473
        //		'user'=>true - Users' names.
474
        //  	'hansard'=>true - Body text from the hansard items.
475
        // 'where' is an associative array of stuff for the WHERE clause, eg:
476
        // 		array ('id=' => '37', 'posted>' => '2003-12-31 00:00:00');
477
        // 'order' is a string for the $order clause, eg 'hpos DESC'.
478
        // 'limit' as a string for the $limit clause, eg '21,20'.
479
480
        $amount = $input['amount'] ?? [];
481
        $wherearr = $input['where'];
482
        $order = $input['order'] ?? '';
483
        $limit = $input['limit'] ?? '';
484
485
        // The fields to fetch from db. 'table' => array ('field1', 'field2').
486
        $fieldsarr =  [
487
            'comments' =>  ['comment_id', 'user_id', 'epobject_id', 'body', 'posted', 'modflagged', 'visible'],
488
            'hansard' =>  ['major', 'gid'],
489
        ];
490
491
        // Yes, we need the gid of a comment's associated hansard object
492
        // to make the comment's URL. And we have to go via the epobject
493
        // table to do that.
494
        $join = 'INNER JOIN epobject ON comments.epobject_id = epobject.epobject_id
495
                    INNER JOIN hansard ON comments.epobject_id = hansard.epobject_id';
496
497
        // Add on the stuff for getting a user's details.
498
        if (isset($amount['user']) && $amount['user'] == true) {
499
            $fieldsarr['users'] =  ['firstname', 'lastname', 'user_id'];
500
            // Like doing "FROM comments, users" but it's easier to add
501
            // an "INNER JOIN..." automatically to the query.
502
            $join .= ' INNER JOIN users ON comments.user_id = users.user_id ';
503
        }
504
505
        // Add on that we need to get the hansard item's body.
506
        if (isset($amount['hansard']) && $amount['hansard'] == true) {
507
            $fieldsarr['epobject'] = ['body'];
508
        }
509
510
        $fieldsarr2 =  [];
511
        // Construct the $fields clause.
512
        foreach ($fieldsarr as $table => $tablesfields) {
513
            foreach ($tablesfields as $n => $field) {
514
                // HACK.
515
                // If we're getting the body of a hansard object, we need to
516
                // get it AS 'hbody', so we don't confuse with the comment's 'body'
517
                // element.
518
                if ($table == 'epobject' && $field == 'body') {
519
                    $field .= ' AS hbody';
520
                }
521
                $fieldsarr2[] = $table . '.' . $field;
522
            }
523
        }
524
        $fields = implode(', ', $fieldsarr2);
525
526
527
        $wherearr2 =  [];
528
        $params = [];
529
        $i = 0;
530
        // Construct the $where clause.
531
        foreach ($wherearr as $key => $val) {
532
            $wherearr2[] = "$key :where$i";
533
            $params[":where$i"] = $val;
534
            $i++;
535
        }
536
        $where = implode(" AND ", $wherearr2);
537
538
        # limit to either old comments or comments from people who have annotate flag
539
        if (isset($amount['user']) && $amount['user'] == true) {
540
            $where .= " AND (users.can_annotate = 1 OR comments.posted < '2025-01-01 00:00:00')";
541
        }
542
543
        if ($order != '') {
544
            $order = "ORDER BY $order";
545
        }
546
        if ($limit != '') {
547
            # Can't use parameter as >1 argument
548
            $limit = "LIMIT $limit";
549
        }
550
551
        // Finally, do the query!
552
        $q = $this->db->query("SELECT $fields
553
                        FROM 	comments
554
                        $join
555
                        WHERE $where
556
                        $order
557
                        $limit
558
                        ", $params);
559
560
        // Format the data into an array for returning.
561
        $data =  [];
562
563
        // If you change stuff here, you might have to change it in
564
        // $COMMENT->_set_url() too...
565
566
        // We'll generate permalinks for each comment.
567
        // Assuming every comment is from the same major...
568
569
        foreach ($q as $row) {
570
571
            $out = [];
572
573
            // Put each row returned into its own array in $data.
574
            foreach ($fieldsarr as $table => $tablesfields) {
575
                foreach ($tablesfields as $m => $field) {
576
577
                    // HACK 2.
578
                    // If we're getting the body of a hansard object, we have
579
                    // got it AS 'hbody', so we didn't duplicate the comment's 'body'
580
                    // element.
581
                    if ($table == 'epobject' && $field == 'body') {
582
                        $field = 'hbody';
583
                    }
584
585
                    $out[$field] = $row[$field];
586
                }
587
            }
588
589
            $urldata = [
590
                'major' => $row['major'],
591
                'gid' => $out['gid'],
592
                'comment_id' => $out['comment_id'],
593
                #					'user_id' =>
594
            ];
595
            $out['url'] = $this->_comment_url($urldata);
596
            $data[] = $out;
597
        }
598
599
        return $data;
600
601
    }
602
}
603