Passed
Push — master ( 78cd0a...f41061 )
by Julito
10:08
created

AbstractLink::set_name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Class AbstractLink
6
 * Defines a gradebook AbstractLink object.
7
 * To implement specific links,
8
 * extend this class and define a type in LinkFactory.
9
 * Use the methods in LinkFactory to create link objects.
10
 *
11
 * @author Bert Steppé
12
 * @author Julio Montoya <[email protected]> security improvements
13
 *
14
 * @package chamilo.gradebook
15
 */
16
abstract class AbstractLink implements GradebookItem
17
{
18
    public $course_id;
19
    public $studentList;
20
    /** @var \Chamilo\CoreBundle\Entity\GradebookLink */
21
    public $entity;
22
    protected $id;
23
    protected $type;
24
    protected $ref_id;
25
    protected $user_id;
26
    protected $course_code;
27
    /** @var Category */
28
    protected $category;
29
    protected $created_at;
30
    protected $weight;
31
    protected $visible;
32
    protected $session_id;
33
34
    /**
35
     * Constructor.
36
     */
37
    public function __construct()
38
    {
39
        $this->course_id = api_get_course_int_id();
40
    }
41
42
    /**
43
     * @return bool
44
     */
45
    abstract public function has_results();
46
47
    /**
48
     * @return string
49
     */
50
    abstract public function get_link();
51
52
    /**
53
     * @return bool
54
     */
55
    abstract public function is_valid_link();
56
57
    /**
58
     * @return string
59
     */
60
    abstract public function get_type_name();
61
62
    /**
63
     * @return bool
64
     */
65
    abstract public function needs_name_and_description();
66
67
    /**
68
     * @return bool
69
     */
70
    abstract public function needs_max();
71
72
    /**
73
     * @return bool
74
     */
75
    abstract public function needs_results();
76
77
    /**
78
     * @return bool
79
     */
80
    abstract public function is_allowed_to_change_name();
81
82
    /**
83
     * @return int
84
     */
85
    public function get_id()
86
    {
87
        return $this->id;
88
    }
89
90
    /**
91
     * @return string
92
     */
93
    public function get_type()
94
    {
95
        return $this->type;
96
    }
97
98
    /**
99
     * @return int
100
     */
101
    public function get_ref_id()
102
    {
103
        return (int) $this->ref_id;
104
    }
105
106
    /**
107
     * @return int
108
     */
109
    public function get_session_id()
110
    {
111
        return $this->session_id;
112
    }
113
114
    /**
115
     * @return int
116
     */
117
    public function get_user_id()
118
    {
119
        return $this->user_id;
120
    }
121
122
    /**
123
     * @return string
124
     */
125
    public function get_course_code()
126
    {
127
        return $this->course_code;
128
    }
129
130
    /**
131
     * @return Category
132
     */
133
    public function getCategory()
134
    {
135
        return $this->category;
136
    }
137
138
    /**
139
     * @param Category $category
140
     */
141
    public function setCategory($category)
142
    {
143
        $this->category = $category;
144
    }
145
146
    /**
147
     * @return int
148
     */
149
    public function get_category_id()
150
    {
151
        return $this->category->get_id();
152
    }
153
154
    /**
155
     * @param int $category_id
156
     */
157
    public function set_category_id($category_id)
158
    {
159
        $categories = Category::load($category_id);
160
        if (isset($categories[0])) {
161
            $this->setCategory($categories[0]);
162
        }
163
    }
164
165
    public function get_date()
166
    {
167
        return $this->created_at;
168
    }
169
170
    public function get_weight()
171
    {
172
        return $this->weight;
173
    }
174
175
    public function is_locked()
176
    {
177
        return isset($this->locked) && $this->locked == 1 ? true : false;
178
    }
179
180
    public function is_visible()
181
    {
182
        return $this->visible;
183
    }
184
185
    public function set_id($id)
186
    {
187
        $this->id = $id;
188
    }
189
190
    public function set_type($type)
191
    {
192
        $this->type = $type;
193
    }
194
195
    public function set_ref_id($ref_id)
196
    {
197
        $this->ref_id = $ref_id;
198
    }
199
200
    public function set_user_id($user_id)
201
    {
202
        $this->user_id = $user_id;
203
    }
204
205
    /**
206
     * @param string $course_code
207
     */
208
    public function set_course_code($course_code)
209
    {
210
        $courseInfo = api_get_course_info($course_code);
211
        if ($courseInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $courseInfo of type array 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...
212
            $this->course_code = $course_code;
213
            $this->course_id = $courseInfo['real_id'];
214
        }
215
    }
216
217
    /**
218
     * @return array
219
     */
220
    public function getStudentList()
221
    {
222
        if (empty($this->studentList)) {
223
            return [];
224
        }
225
226
        return $this->studentList;
227
    }
228
229
    /**
230
     * @param array $list
231
     */
232
    public function setStudentList($list)
233
    {
234
        $this->studentList = $list;
235
    }
236
237
    public function set_date($date)
238
    {
239
        $this->created_at = $date;
240
    }
241
242
    public function set_weight($weight)
243
    {
244
        $this->weight = $weight;
245
    }
246
247
    public function set_visible($visible)
248
    {
249
        $this->visible = $visible;
250
    }
251
252
    /**
253
     * @param int $id
254
     */
255
    public function set_session_id($id)
256
    {
257
        $this->session_id = $id;
258
    }
259
260
    /**
261
     * @param $locked
262
     */
263
    public function set_locked($locked)
264
    {
265
        $this->locked = $locked;
0 ignored issues
show
Bug Best Practice introduced by
The property locked does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
266
    }
267
268
    /**
269
     * @return int
270
     */
271
    public function getCourseId()
272
    {
273
        return $this->course_id;
274
    }
275
276
    /**
277
     * Retrieve links and return them as an array of extensions of AbstractLink.
278
     * To keep consistency, do not call this method but LinkFactory::load instead.
279
     *
280
     * @param int    $id
281
     * @param int    $type
282
     * @param int    $ref_id
283
     * @param int    $user_id
284
     * @param string $course_code
285
     * @param int    $category_id
286
     * @param int    $visible
287
     *
288
     * @return array
289
     */
290
    public static function load(
291
        $id = null,
292
        $type = null,
293
        $ref_id = null,
294
        $user_id = null,
295
        $course_code = null,
296
        $category_id = null,
297
        $visible = null
298
    ) {
299
        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
300
        $sql = 'SELECT * FROM '.$tbl_grade_links;
301
        $paramcount = 0;
302
        if (isset($id)) {
303
            $sql .= ' WHERE id = '.intval($id);
304
            $paramcount++;
305
        }
306
        if (isset($type)) {
307
            if ($paramcount != 0) {
308
                $sql .= ' AND';
309
            } else {
310
                $sql .= ' WHERE';
311
            }
312
            $sql .= ' type = '.intval($type);
313
            $paramcount++;
314
        }
315
        if (isset($ref_id)) {
316
            if ($paramcount != 0) {
317
                $sql .= ' AND';
318
            } else {
319
                $sql .= ' WHERE';
320
            }
321
            $sql .= ' ref_id = '.intval($ref_id);
322
            $paramcount++;
323
        }
324
        if (isset($user_id)) {
325
            if ($paramcount != 0) {
326
                $sql .= ' AND';
327
            } else {
328
                $sql .= ' WHERE';
329
            }
330
            $sql .= ' user_id = '.intval($user_id);
331
            $paramcount++;
332
        }
333
        if (isset($course_code)) {
334
            if ($paramcount != 0) {
335
                $sql .= ' AND';
336
            } else {
337
                $sql .= ' WHERE';
338
            }
339
            $courseInfo = api_get_course_info($course_code);
340
            if ($courseInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $courseInfo of type array 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...
341
                $sql .= " c_id = '".$courseInfo['real_id']."'";
342
                $paramcount++;
343
            }
344
        }
345
        if (isset($category_id)) {
346
            if ($paramcount != 0) {
347
                $sql .= ' AND';
348
            } else {
349
                $sql .= ' WHERE';
350
            }
351
            $sql .= ' category_id = '.intval($category_id);
352
            $paramcount++;
353
        }
354
        if (isset($visible)) {
355
            if ($paramcount != 0) {
356
                $sql .= ' AND';
357
            } else {
358
                $sql .= ' WHERE';
359
            }
360
            $sql .= ' visible = '.intval($visible);
361
        }
362
363
        $result = Database::query($sql);
364
        $links = self::create_objects_from_sql_result($result);
365
366
        return $links;
367
    }
368
369
    /**
370
     * Insert this link into the database.
371
     */
372
    public function add()
373
    {
374
        $this->add_linked_data();
375
        if (isset($this->type) &&
376
            isset($this->ref_id) &&
377
            isset($this->user_id) &&
378
            isset($this->course_code) &&
379
            isset($this->category) &&
380
            isset($this->weight) &&
381
            isset($this->visible)
382
        ) {
383
            $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
384
            $sql = "SELECT count(*) count FROM $table
385
                    WHERE
386
                        ref_id = ".$this->get_ref_id()." AND
387
                        category_id =  ".$this->category->get_id()." AND
388
                        c_id = '".$this->course_id."' AND
389
                        type =  ".$this->type." ";
390
391
            $result = Database::query($sql);
392
            $row = Database::fetch_array($result, 'ASSOC');
393
394
            if ($row['count'] == 0) {
395
                $params = [
396
                    'type' => $this->get_type(),
397
                    'ref_id' => $this->get_ref_id(),
398
                    'user_id' => $this->get_user_id(),
399
                    'c_id' => $this->course_id,
400
                    'category_id' => $this->get_category_id(),
401
                    'weight' => $this->get_weight(),
402
                    'visible' => $this->is_visible(),
403
                    'created_at' => api_get_utc_datetime(),
404
                    'locked' => 0,
405
                ];
406
                $id = Database::insert($table, $params);
407
                $this->set_id($id);
408
409
                return $id;
410
            }
411
        }
412
413
        return false;
414
    }
415
416
    /**
417
     * Update the properties of this link in the database.
418
     */
419
    public function save()
420
    {
421
        $em = Database::getManager();
422
423
        $link = $em->find('ChamiloCoreBundle:GradebookLink', $this->id);
424
425
        if (!$link) {
426
            return;
427
        }
428
429
        self::add_link_log($this->id);
430
431
        $this->save_linked_data();
432
433
        $course = api_get_course_entity($this->getCourseId());
434
435
        $link
436
            ->setType($this->get_type())
437
            ->setRefId($this->get_ref_id())
438
            ->setUserId($this->get_user_id())
439
            ->setCourse($course)
440
            ->setCategoryId($this->get_category_id())
441
            ->setWeight($this->get_weight())
442
            ->setVisible($this->is_visible());
443
444
        $em->merge($link);
445
        $em->flush();
446
    }
447
448
    /**
449
     * @param int $evaluationId
450
     */
451
    public static function add_link_log($evaluationId, $nameLog = null)
452
    {
453
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINKEVAL_LOG);
454
        $dateobject = self::load($evaluationId, null, null, null, null);
455
        $now = api_get_utc_datetime();
456
        $arreval = get_object_vars($dateobject[0]);
457
        $description_log = isset($arreval['description']) ? $arreval['description'] : '';
458
        if (empty($nameLog)) {
459
            if (isset($_POST['name_link'])) {
460
                $name_log = isset($_POST['name_link']) ? $_POST['name_link'] : $arreval['course_code'];
461
            } elseif (isset($_POST['link_'.$evaluationId]) && $_POST['link_'.$evaluationId]) {
462
                $name_log = $_POST['link_'.$evaluationId];
463
            } else {
464
                $name_log = $arreval['course_code'];
465
            }
466
        } else {
467
            $name_log = $nameLog;
468
        }
469
470
        $params = [
471
            'id_linkeval_log' => $arreval['id'],
472
            'name' => $name_log,
473
            'description' => $description_log,
474
            'created_at' => $now,
475
            'weight' => $arreval['weight'],
476
            'visible' => $arreval['visible'],
477
            'type' => 'Link',
478
            'user_id_log' => api_get_user_id(),
479
        ];
480
        Database::insert($table, $params);
481
    }
482
483
    /**
484
     * Delete this link from the database.
485
     */
486
    public function delete()
487
    {
488
        $this->delete_linked_data();
489
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
490
        $sql = 'DELETE FROM '.$table.'
491
                WHERE id = '.intval($this->id);
492
        Database::query($sql);
493
    }
494
495
    /**
496
     * Generate an array of possible categories where this link can be moved to.
497
     * Notice: its own parent will be included in the list: it's up to the frontend
498
     * to disable this element.
499
     *
500
     * @return array 2-dimensional array - every element contains 3 subelements (id, name, level)
501
     */
502
    public function get_target_categories()
503
    {
504
        // links can only be moved to categories inside this course
505
        $targets = [];
506
        $level = 0;
507
        $categories = Category::load(null, null, $this->get_course_code(), 0);
508
        foreach ($categories as $cat) {
509
            $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
510
            $targets = $this->addTargetSubcategories(
511
                $targets,
512
                $level + 1,
513
                $cat->get_id()
514
            );
515
        }
516
517
        return $targets;
518
    }
519
520
    /**
521
     * Move this link to the given category.
522
     * If this link moves to outside a course, delete it.
523
     */
524
    public function move_to_cat($cat)
525
    {
526
        if ($this->get_course_code() != $cat->get_course_code()) {
527
            $this->delete();
528
        } else {
529
            $this->set_category_id($cat->get_id());
530
            $this->save();
531
        }
532
    }
533
534
    /**
535
     * Find links by name
536
     * To keep consistency, do not call this method but LinkFactory::find_links instead.
537
     *
538
     * @todo can be written more efficiently using a new (but very complex) sql query
539
     *
540
     * @param string $name_mask
541
     *
542
     * @return array
543
     */
544
    public function find_links($name_mask, $selectcat)
545
    {
546
        $rootcat = Category::load($selectcat);
547
        $links = $rootcat[0]->get_links((api_is_allowed_to_edit() ? null : api_get_user_id()), true);
548
        $foundlinks = [];
549
        foreach ($links as $link) {
550
            if (!(api_strpos(api_strtolower($link->get_name()), api_strtolower($name_mask)) === false)) {
551
                $foundlinks[] = $link;
552
            }
553
        }
554
555
        return $foundlinks;
556
    }
557
558
    /**
559
     * @return string
560
     */
561
    public function get_item_type()
562
    {
563
        return 'L';
564
    }
565
566
    /**
567
     * @return string
568
     */
569
    public function get_icon_name()
570
    {
571
        return 'link';
572
    }
573
574
    public function get_all_links()
575
    {
576
        return [];
577
    }
578
579
    public function add_linked_data()
580
    {
581
    }
582
583
    public function save_linked_data()
584
    {
585
    }
586
587
    public function delete_linked_data()
588
    {
589
    }
590
591
    /**
592
     * @param string $name
593
     */
594
    public function set_name($name)
0 ignored issues
show
Unused Code introduced by
The parameter $name is not used and could be removed. ( Ignorable by Annotation )

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

594
    public function set_name(/** @scrutinizer ignore-unused */ $name)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
595
    {
596
    }
597
598
    /**
599
     * @param string $description
600
     */
601
    public function set_description($description)
0 ignored issues
show
Unused Code introduced by
The parameter $description is not used and could be removed. ( Ignorable by Annotation )

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

601
    public function set_description(/** @scrutinizer ignore-unused */ $description)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
602
    {
603
    }
604
605
    /**
606
     * @param int $max
607
     */
608
    public function set_max($max)
0 ignored issues
show
Unused Code introduced by
The parameter $max is not used and could be removed. ( Ignorable by Annotation )

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

608
    public function set_max(/** @scrutinizer ignore-unused */ $max)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
609
    {
610
    }
611
612
    public function get_view_url($stud_id)
0 ignored issues
show
Unused Code introduced by
The parameter $stud_id is not used and could be removed. ( Ignorable by Annotation )

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

612
    public function get_view_url(/** @scrutinizer ignore-unused */ $stud_id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
613
    {
614
        return null;
615
    }
616
617
    /**
618
     * Locks a link.
619
     *
620
     * @param int $locked 1 or unlocked 0
621
     *
622
     * */
623
    public function lock($locked)
624
    {
625
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
626
        $sql = "UPDATE $table SET locked = '".intval($locked)."'
627
                WHERE id='".$this->id."'";
628
        Database::query($sql);
629
    }
630
631
    /**
632
     * Get current user ranking.
633
     *
634
     * @param int   $userId
635
     * @param array $studentList Array with user id and scores
636
     *                           Example: [1 => 5.00, 2 => 8.00]
637
     *
638
     * @return array
639
     */
640
    public static function getCurrentUserRanking($userId, $studentList)
641
    {
642
        $ranking = null;
643
        $currentUserId = $userId;
644
        if (!empty($studentList) && !empty($currentUserId)) {
645
            $studentList = array_map('floatval', $studentList);
646
            asort($studentList);
647
            $ranking = $count = count($studentList);
648
649
            foreach ($studentList as $userId => $position) {
0 ignored issues
show
introduced by
$userId is overwriting one of the parameters of this function.
Loading history...
650
                if ($currentUserId == $userId) {
651
                    break;
652
                }
653
                $ranking--;
654
            }
655
656
            // If no ranking was detected.
657
            if ($ranking == 0) {
658
                return [];
659
            }
660
661
            return [$ranking, $count];
662
        }
663
664
        return [];
665
    }
666
667
    /**
668
     * @return string
669
     */
670
    public function getSkillsFromItem()
671
    {
672
        $toolType = '';
673
        switch ($this->type) {
674
            case LINK_ATTENDANCE:
675
                $toolType = ITEM_TYPE_ATTENDANCE;
676
                break;
677
            case LINK_EXERCISE:
678
                $toolType = ITEM_TYPE_EXERCISE;
679
                break;
680
            case LINK_FORUM_THREAD:
681
                $toolType = ITEM_TYPE_FORUM_THREAD;
682
                break;
683
            case LINK_LEARNPATH:
684
                $toolType = ITEM_TYPE_LEARNPATH;
685
                break;
686
            case LINK_HOTPOTATOES:
687
                $toolType = ITEM_TYPE_HOTPOTATOES;
688
                break;
689
            case LINK_STUDENTPUBLICATION:
690
                $toolType = ITEM_TYPE_STUDENT_PUBLICATION;
691
                break;
692
            case LINK_SURVEY:
693
                $toolType = ITEM_TYPE_SURVEY;
694
                break;
695
        }
696
697
        $skillToString = Skill::getSkillRelItemsToString($toolType, $this->get_ref_id());
0 ignored issues
show
Bug introduced by
It seems like $toolType can also be of type string; however, parameter $typeId of Skill::getSkillRelItemsToString() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

697
        $skillToString = Skill::getSkillRelItemsToString(/** @scrutinizer ignore-type */ $toolType, $this->get_ref_id());
Loading history...
698
699
        return $skillToString;
700
    }
701
702
    /**
703
     * @param int    $itemId
704
     * @param int    $linkType
705
     * @param string $courseCode
706
     * @param int    $sessionId
707
     *
708
     * @return array|bool|\Doctrine\DBAL\Driver\Statement
709
     */
710
    public static function getGradebookLinksFromItem($itemId, $linkType, $courseCode, $sessionId = 0)
711
    {
712
        if (empty($courseCode) || empty($itemId) || empty($linkType)) {
713
            return false;
714
        }
715
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
716
        $tableCategory = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
717
        $itemId = (int) $itemId;
718
        $linkType = (int) $linkType;
719
        $sessionId = (int) $sessionId;
720
        $courseCode = Database::escape_string($courseCode);
721
722
        $sql = "SELECT DISTINCT l.* 
723
                FROM $table l INNER JOIN $tableCategory c 
724
                ON (c.course_code = l.course_code AND c.id = l.category_id)
725
                WHERE 
726
                    ref_id = $itemId AND 
727
                    type = $linkType AND 
728
                    l.course_code = '$courseCode' AND 
729
                    c.session_id = $sessionId";
730
731
        $result = Database::query($sql);
732
        if (Database::num_rows($result)) {
733
            $result = Database::store_result($result);
734
735
            return $result;
736
        }
737
738
        return false;
739
    }
740
741
    /**
742
     * @param Doctrine\DBAL\Driver\Statement|null $result
743
     *
744
     * @return array
745
     */
746
    private static function create_objects_from_sql_result($result)
747
    {
748
        $links = [];
749
        $allow = api_get_configuration_value('allow_gradebook_stats');
750
        if ($allow) {
751
            $em = Database::getManager();
752
            $repo = $em->getRepository('ChamiloCoreBundle:GradebookLink');
753
        }
754
755
        while ($data = Database::fetch_array($result)) {
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type null; however, parameter $result of Database::fetch_array() does only seem to accept Doctrine\DBAL\Driver\Statement, maybe add an additional type check? ( Ignorable by Annotation )

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

755
        while ($data = Database::fetch_array(/** @scrutinizer ignore-type */ $result)) {
Loading history...
756
            $link = LinkFactory::create($data['type']);
757
            $link->set_id($data['id']);
758
            $link->set_type($data['type']);
759
            $link->set_ref_id($data['ref_id']);
760
            $link->set_user_id($data['user_id']);
761
            $link->set_course_code(api_get_course_id());
762
            $link->set_category_id($data['category_id']);
763
            $link->set_date($data['created_at']);
764
            $link->set_weight($data['weight']);
765
            $link->set_visible($data['visible']);
766
            $link->set_locked($data['locked']);
767
768
            //session id should depend of the category --> $data['category_id']
769
            $session_id = api_get_session_id();
770
            $link->set_session_id($session_id);
771
772
            if ($allow) {
773
                $link->entity = $repo->find($data['id']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $repo does not seem to be defined for all execution paths leading up to this point.
Loading history...
774
            }
775
            $links[] = $link;
776
        }
777
778
        return $links;
779
    }
780
781
    /**
782
     * Internal function used by get_target_categories().
783
     *
784
     * @param array $targets
785
     * @param int   $level
786
     * @param int   $catid
787
     *
788
     * @return array
789
     */
790
    private function addTargetSubcategories($targets, $level, $catid)
791
    {
792
        $subcats = Category::load(null, null, null, $catid);
793
        foreach ($subcats as $cat) {
794
            $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
795
            $targets = $this->addTargetSubcategories(
796
                $targets,
797
                $level + 1,
798
                $cat->get_id()
799
            );
800
        }
801
802
        return $targets;
803
    }
804
}
805