Passed
Pull Request — master (#18)
by Michael
02:21
created

TagHandler::updateByItem()   F

Complexity

Conditions 24
Paths 2105

Size

Total Lines 90
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
eloc 57
nc 2105
nop 4
dl 0
loc 90
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace XoopsModules\Tag;
4
5
/*
6
 You may not change or alter any portion of this comment or credits
7
 of supporting developers from this source code or any supporting source code
8
 which is considered copyrighted (c) material of the original comment or credit authors.
9
10
 This program is distributed in the hope that it will be useful,
11
 but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
*/
14
15
/**
16
 * XOOPS tag management module
17
 *
18
 * @package         tag
19
 * @subpackage      class
20
 * @copyright       {@link http://sourceforge.net/projects/xoops/ The XOOPS Project}
21
 * @license         {@link http://www.fsf.org/copyleft/gpl.html GNU public license}
22
 * @author          Taiwen Jiang <[email protected]>
23
 * @since           1.00
24
 */
25
defined('XOOPS_ROOT_PATH') || die('Restricted access');
26
27
/**
28
 * Class TagHandler
29
 */
30
class TagHandler extends \XoopsPersistableObjectHandler
31
{
32
    public $table_link;
33
    public $table_stats;
34
35
    /**
36
     * Constructor
37
     *
38
     * @param \XoopsDatabase|null $db reference to the {@link XoopsDatabase}
39
     *                                object
40
     */
41
    public function __construct(\XoopsDatabase $db = null)
42
    {
43
        parent::__construct($db, 'tag_tag', Tag::class, 'tag_id', 'tag_term');
44
        $this->table_link  = $this->db->prefix('tag_link');
45
        $this->table_stats = $this->db->prefix('tag_stats');
46
    }
47
48
    /**
49
     * Get tags linked to an item
50
     *
51
     * @access public
52
     * @param  int $itemid item ID
53
     * @param  int $modid  module ID, optional
54
     * @param  int $catid  id of corresponding category, optional
55
     * @return array associative array of tags (id, term)
56
     */
57
    public function getByItem($itemid, $modid = 0, $catid = 0)
58
    {
59
        $ret = [];
60
61
        $itemid = (int)$itemid;
62
        $modid  = (empty($modid) && is_object($GLOBALS['xoopsModule'])
63
                   && 'tag' !== $GLOBALS['xoopsModule']->getVar('dirname')) ? $GLOBALS['xoopsModule']->getVar('mid') : (int)$modid;
64
        if (empty($itemid) || empty($modid)) {
65
            return $ret;
66
        }
67
68
        $sql = 'SELECT o.tag_id, o.tag_term'
69
               . " FROM {$this->table_link} AS l "
70
               . " LEFT JOIN {$this->table} AS o ON o.{$this->keyName} = l.{$this->keyName} "
71
               . " WHERE  l.tag_itemid = {$itemid} AND l.tag_modid = {$modid}"
72
               . (empty($catid) ? '' : (' AND l.tag_catid=' . (int)$catid))
73
               . ' ORDER BY o.tag_count DESC';
74
        if (false === ($result = $this->db->query($sql))) {
75
            return $ret;
76
        }
77
        while (false !== ($myrow = $this->db->fetchArray($result))) {
78
            $ret[$myrow[$this->keyName]] = $myrow['tag_term'];
79
        }
80
81
        return $ret;
82
    }
83
84
    /**
85
     * Update tags linked to an item
86
     *
87
     * @access   public
88
     * @param  array|string $tags   array of $tags or a single tag
89
     * @param  int          $itemid item ID
90
     * @param  int|string   $modid  module ID or module dirname, optional
91
     * @param  int          $catid  id of corresponding category, optional
92
     * @return bool
93
     */
94
    public function updateByItem($tags, $itemid, $modid = '', $catid = 0)
95
    {
96
        $catid  = (int)$catid;
97
        $itemid = (int)$itemid;
98
99
        if (!empty($modid) && !is_numeric($modid)) {
100
            if (($GLOBALS['xoopsModule'] instanceof \XoopsModule)
101
                && ($modid == $GLOBALS['xoopsModule']->getVar('dirname'))) {
102
                $modid = $GLOBALS['xoopsModule']->getVar('mid');
103
            } else {
104
                /** @var \XoopsModuleHandler $moduleHandler */
105
                $moduleHandler = xoops_getHandler('module');
106
                $modid         = ($module_obj = $moduleHandler->getByDirname($modid)) ? $module_obj->getVar('mid') : 0;
107
            }
108
        } elseif ($GLOBALS['xoopsModule'] instanceof XoopsModule) {
0 ignored issues
show
Bug introduced by
The type XoopsModules\Tag\XoopsModule was not found. Did you mean XoopsModule? If so, make sure to prefix the type with \.
Loading history...
109
            $modid = $GLOBALS['xoopsModule']->getVar('mid');
110
        }
111
112
        if (empty($itemid) || empty($modid)) {
113
            return false;
114
        }
115
116
        if (empty($tags)) {
117
            $tags = [];
118
        } elseif (!is_array($tags)) {
119
            require_once $GLOBALS['xoops']->path('/modules/tag/include/functions.php');
120
            $tags = tag_parse_tag(addslashes(stripslashes($tags)));
121
        }
122
123
        $tags_existing = $this->getByItem($itemid, $modid, $catid);
124
        $tags_delete   = array_diff(array_values($tags_existing), $tags);
125
        $tags_add      = array_diff($tags, array_values($tags_existing));
126
        $tags_update   = [];
127
128
        if (!empty($tags_delete)) {
129
            $tags_delete = array_map([$this->db, 'quoteString'], $tags_delete);
130
            if ($tags_id = &$this->getIds(new \Criteria('tag_term', '(' . implode(', ', $tags_delete) . ')', 'IN'))) {
131
                $sql = "DELETE FROM {$this->table_link}" . ' WHERE ' . "     {$this->keyName} IN (" . implode(', ', $tags_id) . ')' . "     AND tag_modid = {$modid} AND tag_catid = {$catid} AND tag_itemid = {$itemid}";
132
                if (false === ($result = $this->db->queryF($sql))) {
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
133
                    //@todo: decide if we should do something here on failure
134
                }
135
                $sql = 'DELETE FROM ' . $this->table . ' WHERE ' . '    tag_count < 2 AND ' . "     {$this->keyName} IN (" . implode(', ', $tags_id) . ')';
136
                if (false === ($result = $this->db->queryF($sql))) {
137
                    //xoops_error($this->db->error());
138
                }
139
140
                $sql = 'UPDATE ' . $this->table . ' SET tag_count = tag_count - 1' . ' WHERE ' . "     {$this->keyName} IN (" . implode(', ', $tags_id) . ')';
141
                if (false === ($result = $this->db->queryF($sql))) {
142
                    //xoops_error($this->db->error());
143
                }
144
                $tags_update = $tags_id;
145
            }
146
        }
147
148
        if (!empty($tags_add)) {
149
            $tag_link  = [];
150
            $tag_count = [];
151
            foreach ($tags_add as $tag) {
152
                if ($tags_id = &$this->getIds(new \Criteria('tag_term', $tag))) {
153
                    $tag_id      = $tags_id[0];
154
                    $tag_count[] = $tag_id;
155
                } else {
156
                    $tag_obj = $this->create();
157
                    $tag_obj->setVar('tag_term', $tag);
158
                    $tag_obj->setVar('tag_count', 1);
159
                    $this->insert($tag_obj);
160
                    $tag_id = $tag_obj->getVar('tag_id');
161
                    unset($tag_obj);
162
                }
163
                $tag_link[]    = "({$tag_id}, {$itemid}, {$catid}, {$modid}, " . time() . ')';
164
                $tags_update[] = $tag_id;
165
            }
166
            $sql = "INSERT INTO {$this->table_link}" . ' (tag_id, tag_itemid, tag_catid, tag_modid, tag_time) ' . ' VALUES ' . implode(', ', $tag_link);
167
            if (false === ($result = $this->db->queryF($sql))) {
168
                //xoops_error($this->db->error());
169
            }
170
            if (!empty($tag_count)) {
171
                $sql = 'UPDATE ' . $this->table . ' SET tag_count = tag_count+1' . ' WHERE ' . "     {$this->keyName} IN (" . implode(', ', $tag_count) . ')';
172
                if (false === ($result = $this->db->queryF($sql))) {
173
                    //xoops_error($this->db->error());
174
                }
175
            }
176
        }
177
        if (is_array($tags_update)) {
178
            foreach ($tags_update as $tag_id) {
179
                $this->update_stats($tag_id, $modid, $catid);
180
            }
181
        }
182
183
        return true;
184
    }
185
186
    /**
187
     * Update count stats sor tag
188
     *
189
     * @access public
190
     * @param  int $tag_id
191
     * @param  int $modid
192
     * @param  int $catid
193
     * @return bool
194
     */
195
    public function update_stats($tag_id, $modid = 0, $catid = 0)
196
    {
197
        $tag_id = (int)$tag_id;
198
        if (empty($tag_id)) {
199
            return true;
200
        }
201
202
        $modid = (int)$modid;
203
        $catid = empty($modid) ? -1 : (int)$catid;
204
        $count = 0;
205
        $sql   = 'SELECT COUNT(*) ' . " FROM {$this->table_link}" . " WHERE tag_id = {$tag_id}" . (empty($modid) ? '' : " AND tag_modid = {$modid}") . (($catid < 0) ? '' : " AND tag_catid = {$catid}");
206
207
        if ($result = $this->db->query($sql)) {
208
            list($count) = $this->db->fetchRow($result);
209
        }
210
        if (empty($modid)) {
211
            $tag_obj = $this->get($tag_id);
212
            if (empty($count)) {
213
                $this->delete($tag_obj);
214
            } else {
215
                $tag_obj->setVar('tag_count', $count);
216
                $this->insert($tag_obj, true);
217
            }
218
        } else {
219
            if (empty($count)) {
220
                $sql = "DELETE FROM {$this->table_stats}" . ' WHERE ' . " {$this->keyName} = {$tag_id}" . " AND tag_modid = {$modid}" . " AND tag_catid = {$catid}";
221
222
                if (false === $result = $this->db->queryF($sql)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
223
                    //xoops_error($this->db->error());
224
                }
225
            } else {
226
                $ts_id = null;
227
                $sql   = 'SELECT ts_id, tag_count ' . " FROM {$this->table_stats}" . " WHERE {$this->keyName} = {$tag_id}" . " AND tag_modid = {$modid}" . " AND tag_catid = {$catid}";
228
                if ($result = $this->db->query($sql)) {
229
                    list($ts_id, $tag_count) = $this->db->fetchRow($result);
230
                }
231
                $sql = '';
232
                if ($ts_id && $tag_count != $count) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tag_count does not seem to be defined for all execution paths leading up to this point.
Loading history...
233
                    $sql = "UPDATE {$this->table_stats}" . " SET tag_count = {$count}" . ' WHERE ' . "     ts_id = {$ts_id}";
234
                } elseif (!$ts_id) {
235
                    $sql = "INSERT INTO {$this->table_stats}" . ' (tag_id, tag_modid, tag_catid, tag_count)' . " VALUES ({$tag_id}, {$modid}, {$catid}, {$count})";
236
                }
237
238
                if (!empty($sql) && false === ($result = $this->db->queryF($sql))) {
239
                    //xoops_error($this->db->error());
240
                }
241
            }
242
        }
243
244
        return true;
245
    }
246
247
    /**
248
     * Get tags with item count
249
     *
250
     * @access         public
251
     * @param int                                  $limit
252
     * @param int                                  $start
253
     * @param null|\CriteriaElement|\CriteriaCompo $criteria  {@link Criteria}
254
     * @param null                                 $fields
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $fields is correct as it would always require null to be passed?
Loading history...
255
     * @param bool                                 $fromStats fetch from tag-stats table
256
     * @return array associative array of tags (id, term, count)
257
     */
258
    public function &getByLimit(
259
        $limit = 0,
260
        $start = 0,
261
        \CriteriaElement $criteria = null,
262
        $fields = null,
263
        $fromStats = true)//&getByLimit($criteria = null, $fromStats = true)
264
    {
265
        $ret = [];
266
        if ($fromStats) {
267
            $sql = "SELECT DISTINCT(o.{$this->keyName}), o.tag_term, o.tag_status, SUM(l.tag_count) AS count, l.tag_modid" . " FROM {$this->table} AS o LEFT JOIN {$this->table_stats} AS l ON l.{$this->keyName} = o.{$this->keyName}";
268
        } else {
269
            $sql = "SELECT DISTINCT(o.{$this->keyName}), o.tag_term, o.tag_status, COUNT(l.tl_id) AS count, l.tag_modid" . " FROM {$this->table} AS o LEFT JOIN {$this->table_link} AS l ON l.{$this->keyName} = o.{$this->keyName}";
270
        }
271
272
        $limit = null;
273
        $start = null;
274
        $sort  = '';
275
        $order = '';
276
        if (null !== $criteria && is_subclass_of($criteria, 'CriteriaCompo')) {
277
            $sql   .= ' ' . $criteria->renderWhere();
0 ignored issues
show
Bug introduced by
The method renderWhere() does not exist on CriteriaElement. Did you maybe mean render()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

277
            $sql   .= ' ' . $criteria->/** @scrutinizer ignore-call */ renderWhere();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
278
            $sort  = $criteria->getSort();
279
            $order = $criteria->getOrder();
280
            $limit = $criteria->getLimit();
281
            $start = $criteria->getStart();
282
        }
283
        $sql .= " GROUP BY o.{$this->keyName}";
284
285
        $order = mb_strtoupper($order);
286
        $sort  = mb_strtolower($sort);
287
        switch ($sort) {
288
            case 'a':
289
            case 'alphabet':
290
                $order = ('DESC' !== $order) ? 'ASC' : 'DESC';
291
                $sql   .= " ORDER BY o.tag_term {$order}";
292
                break;
293
            case 'id':
294
            case 'time':
295
                $order = ('ASC' !== $order) ? 'DESC' : 'ASC';
296
                $sql   .= " ORDER BY o.{$this->keyName} {$order}";
297
                break;
298
            case 'c':
299
            case 'count':
300
            default:
301
                $order = ('ASC' !== $order) ? 'DESC' : 'ASC';
302
                $sql   .= " ORDER BY count {$order}";
303
                break;
304
        }
305
306
        if (false === ($result = $this->db->query($sql, $limit, $start))) {
307
            //xoops_error($this->db->error());
308
            $ret = null;
309
        } else {
310
            while (false !== ($myrow = $this->db->fetchArray($result))) {
311
                $ret[$myrow[$this->keyName]] = [
312
                    'id'     => $myrow[$this->keyName],
313
                    'term'   => htmlspecialchars($myrow['tag_term'], ENT_QUOTES | ENT_HTML5),
314
                    'status' => $myrow['tag_status'],
315
                    'modid'  => $myrow['tag_modid'],
316
                    'count'  => (int)$myrow['count'],
317
                ];
318
            }
319
        }
320
321
        return $ret;
322
    }
323
324
    /**
325
     * Get count of tags
326
     *
327
     * @access public
328
     * @param null|\CriteriaElement|\CriteriaCompo $criteria {@link Criteria)
329
     *
330
     * @return int count
331
     */
332
    public function getCount(\CriteriaElement $criteria = null)
333
    {
334
        /*
335
        $catid    = (int)($catid);
336
        $modid    = (int)($modid);
337
        */
338
        $sql = "SELECT COUNT(DISTINCT o.{$this->keyName})" . "    FROM {$this->table} AS o LEFT JOIN {$this->table_link} AS l ON l.{$this->keyName} = o.{$this->keyName}";
339
        if ((null !== $criteria) && is_subclass_of($criteria, 'CriteriaElement')) {
340
            $sql .= ' ' . $criteria->renderWhere();
341
        }
342
        /*
343
        $sql_where    = "    WHERE 1 = 1";
344
        if (!empty($modid)) {
345
            $sql_where    .= " AND l.tag_modid = {$modid}";
346
        }
347
        if (empty($catid) || $catid > 0) {
348
            $sql_where    .= " AND l.tag_catid = {$catid}";
349
        }
350
351
        $sql =     $sql_select . " " . $sql_from . " " . $sql_where;
352
        */
353
        if (false === ($result = $this->db->query($sql))) {
354
            //xoops_error($this->db->error());
355
            $ret = 0;
356
        } else {
357
            list($ret) = $this->db->fetchRow($result);
358
        }
359
360
        return $ret;
361
    }
362
363
    /**
364
     * Get items linked with a tag
365
     *
366
     * @param \CriteriaElement $criteria {@link Criteria}
367
     *
368
     * @return array associative array of items (id, modid, catid)
369
     */
370
    public function getItems(\CriteriaElement $criteria = null)
371
    {
372
        $ret = [];
373
        $sql = '    SELECT o.tl_id, o.tag_itemid, o.tag_modid, o.tag_catid, o.tag_time';
374
        $sql .= "    FROM {$this->table_link} AS o LEFT JOIN {$this->table} AS l ON l.{$this->keyName} = o.{$this->keyName}";
375
376
        $limit = null;
377
        $start = null;
378
        $sort  = '';
379
        $order = '';
380
        if ((null !== $criteria) && is_subclass_of($criteria, 'CriteriaElement')) {
381
            $sql   .= ' ' . $criteria->renderWhere();
382
            $sort  = $criteria->getSort();
383
            $order = $criteria->getOrder();
384
            $limit = $criteria->getLimit();
385
            $start = $criteria->getStart();
386
        }
387
388
        $order = mb_strtoupper($order);
389
        $sort  = mb_strtolower($sort);
390
        switch ($sort) {
391
            case 'i':
392
            case 'item':
393
                $order = ('DESC' !== $order) ? 'ASC' : 'DESC';
394
                $sql   .= "    ORDER BY o.tag_itemid {$order}, o.tl_id DESC";
395
                break;
396
            case 'm':
397
            case 'module':
398
                $order = ('DESC' !== $order) ? 'ASC' : 'DESC';
399
                $sql   .= "    ORDER BY o.tag_modid {$order}, o.tl_id DESC";
400
                break;
401
            case 't':
402
            case 'time':
403
            default:
404
                $order = ('ASC' !== $order) ? 'DESC' : 'ASC';
405
                $sql   .= "    ORDER BY o.tl_id {$order}";
406
                break;
407
        }
408
409
        if (false === ($result = $this->db->query($sql, $limit, $start))) {
410
            //xoops_error($this->db->error());
411
            $ret = [];
412
        } else {
413
            while (false !== ($myrow = $this->db->fetchArray($result))) {
414
                $ret[$myrow['tl_id']] = [
415
                    'itemid' => $myrow['tag_itemid'],
416
                    'modid'  => $myrow['tag_modid'],
417
                    'catid'  => $myrow['tag_catid'],
418
                    'time'   => $myrow['tag_time'],
419
                ];
420
            }
421
        }
422
423
        return $ret;
424
    }
425
426
    /**
427
     * Get count of items linked with a tag
428
     *
429
     * @access public
430
     * @param  int $tag_id
431
     * @param  int $modid id of corresponding module, optional: 0 for all; >1 for a specific module
432
     * @param  int $catid id of corresponding category, optional
433
     * @return int count
434
     */
435
    public function getItemCount($tag_id, $modid = 0, $catid = 0)
436
    {
437
        if (!$tag_id = (int)$tag_id) {
438
            $ret = 0;
439
        } else {
440
            $catid = (int)$catid;
441
            $modid = (int)$modid;
442
443
            $sql_select = '    SELECT COUNT(DISTINCT o.tl_id)';
444
            $sql_from   = "    FROM {$this->table_link} AS o LEFT JOIN {$this->table} AS l ON l.{$this->keyName} = o.{$this->keyName}";
445
            $sql_where  = "    WHERE o.tag_id = {$tag_id}";
446
            if (!empty($modid)) {
447
                $sql_where .= " AND o.tag_modid = {$modid}";
448
            }
449
            if (empty($catid) || $catid > 0) {
450
                $sql_where .= " AND o.tag_catid = {$catid}";
451
            }
452
453
            $sql = $sql_select . ' ' . $sql_from . ' ' . $sql_where;
454
            if (false === ($result = $this->db->query($sql))) {
455
                //xoops_error($this->db->error());
456
                $ret = 0;
457
            } else {
458
                list($ret) = $this->db->fetchRow($result);
459
            }
460
        }
461
462
        return $ret;
463
    }
464
465
    /**
466
     * delete an object as well as links relying on it
467
     *
468
     * @access public
469
     * @param \XoopsObject $object $object {@link Tag}
470
     * @param  bool        $force  flag to force the query execution despite security settings
471
     * @return bool
472
     */
473
    public function delete(\XoopsObject $object, $force = true)
474
    {
475
        /* {@internal - this isn't needed if we type hint Tag}
476
        if (!is_object($object) || !$object->getVar($this->keyName)) {
477
            return false;
478
        }
479
        */
480
        $queryFunc = empty($force) ? 'query' : 'queryF';
0 ignored issues
show
Unused Code introduced by
The assignment to $queryFunc is dead and can be removed.
Loading history...
481
482
        /*
483
         * Remove item-tag links
484
         */
485
        $sql = 'DELETE' . " FROM {$this->table_link}" . " WHERE  {$this->keyName} = " . $object->getVar($this->keyName);
0 ignored issues
show
Unused Code introduced by
The assignment to $sql is dead and can be removed.
Loading history...
486
        /*
487
                if (false ===  ($result = $this->db->{$queryFunc}($sql))) {
488
                   // xoops_error($this->db->error());
489
                }
490
        */
491
        /*
492
         * Remove stats-tag links
493
         */
494
        $sql = 'DELETE' . " FROM {$this->table_stats}" . " WHERE  {$this->keyName} = " . $object->getVar($this->keyName);
495
496
        /*
497
                if (false === ($result = $this->db->{$queryFunc}($sql))) {
498
                   // xoops_error($this->db->error());
499
                }
500
        */
501
502
        return parent::delete($object, $force);
503
    }
504
505
    /**
506
     * clean orphan links from database
507
     *
508
     * @access public
509
     * @param string $table_link
510
     * @param string $field_link
511
     * @param string $field_object
512
     * @return bool true on success
513
     */
514
    public function cleanOrphan($table_link = '', $field_link = '', $field_object = '')
515
    {
516
        require_once $GLOBALS['xoops']->path('/modules/tag/functions.recon.php');
517
518
        //mod_loadFunctions("recon");
519
        return tag_cleanOrphan();
520
    }
521
522
    /**
523
     * get item Ids {@see XoopsPersistableObjectHandler}
524
     * Overloads default method to provide type hint since
525
     * this is a public function called by plugins
526
     *
527
     * @access public
528
     * @param \CriteriaElement $ids
529
     * @return array|bool      object IDs or false on failure
530
     */
531
    public function &getIds(\CriteriaElement $ids = null)
532
    {
533
        return parent::getIds($ids);
534
    }
535
}
536