Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.30% covered (success)
97.30%
72 / 74
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Tsumego
97.14% covered (success)
97.14%
68 / 70
50.00% covered (danger)
50.00%
1 / 2
16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 loadCommentsData
97.06% covered (success)
97.06%
66 / 68
0.00% covered (danger)
0.00%
0 / 1
15
1<?php
2
3App::uses('TsumegoIssue', 'Model');
4App::uses('TsumegoComment', 'Model');
5App::uses('User', 'Model');
6App::uses('TsumegosController', 'Controller');
7
8class Tsumego extends AppModel
9{
10    public function __construct($id = false, $table = null, $ds = null)
11    {
12        $id['table'] = 'tsumego';
13        parent::__construct($id, $table, $ds);
14    }
15
16    public $validate = [
17        'title' => [
18            'rule' => 'notBlank',
19        ],
20        'sgf1' => [
21            'rule' => 'notBlank',
22        ],
23    ];
24
25    /**
26     * Load all comments and issues data for a tsumego.
27     *
28     * Returns issues with their comments, standalone comments (not associated with any issue),
29     * and parsed Go coordinates from comment messages.
30     *
31     * @param int $tsumegoId The tsumego ID to load comments for
32     * @return array{issues: array, plainComments: array, coordinates: array}
33     */
34    public function loadCommentsData(int $tsumegoId): array
35    {
36        /** @var TsumegoIssue $issueModel */
37        $issueModel = ClassRegistry::init('TsumegoIssue');
38        /** @var TsumegoComment $commentModel */
39        $commentModel = ClassRegistry::init('TsumegoComment');
40
41        // Counter only increments when coordinates are found (matches $fn1 in play.ctp)
42        $counter = 1;
43        $allCoordinates = [];
44
45        // Query 1: Load all issues with their authors
46        $issuesRaw = $issueModel->find('all', [
47            'conditions' => [
48                'tsumego_id' => $tsumegoId,
49                'deleted' => false,
50            ],
51            'order' => 'TsumegoIssue.created DESC',
52            'contain' => ['User'],
53        ]) ?: [];
54
55        // Collect issue IDs for batch comment loading
56        $issueIds = [];
57        $issuesMap = [];
58        foreach ($issuesRaw as $issueRaw)
59        {
60            $issue = $issueRaw['TsumegoIssue'];
61            $issueIds[] = $issue['id'];
62            $issue['author'] = !empty($issueRaw['User']) ? $issueRaw['User'] : ['name' => '[deleted user]'];
63            $issue['comments'] = []; // Will be filled from batch query
64            $issuesMap[$issue['id']] = $issue;
65        }
66
67        // Query 2: Load ALL comments for ALL issues in one query
68        if (!empty($issueIds))
69        {
70            $issueCommentsRaw = $commentModel->find('all', [
71                'conditions' => [
72                    'TsumegoComment.tsumego_issue_id' => $issueIds,
73                    'TsumegoComment.deleted' => false,
74                ],
75                'order' => 'TsumegoComment.created ASC',
76                'contain' => ['User'],
77            ]) ?: [];
78
79            // Group comments by issue_id
80            foreach ($issueCommentsRaw as $commentRaw)
81            {
82                $comment = $commentRaw['TsumegoComment'];
83                $issueId = $comment['tsumego_issue_id'];
84                $comment['user'] = !empty($commentRaw['User']) ? $commentRaw['User'] : ['name' => '[deleted user]', 'isAdmin' => 0];
85
86                // Process message for coordinates with global counter
87                $array = TsumegosController::commentCoordinates($comment['message'], $counter, true);
88                $comment['message'] = $array[0];
89                if (!empty($array[1]))
90                {
91                    $allCoordinates[] = $array[1];
92                    $counter++;
93                }
94
95                $issuesMap[$issueId]['comments'][] = $comment;
96            }
97        }
98
99        // Convert map back to ordered array (preserves original DESC order)
100        $issues = [];
101        foreach ($issuesRaw as $issueRaw)
102        {
103            $issueId = $issueRaw['TsumegoIssue']['id'];
104            if (isset($issuesMap[$issueId]))
105                $issues[] = $issuesMap[$issueId];
106        }
107
108        // Query 3: Load standalone comments (not associated with any issue)
109        $plainCommentsRaw = $commentModel->find('all', [
110            'conditions' => [
111                'TsumegoComment.tsumego_id' => $tsumegoId,
112                'TsumegoComment.tsumego_issue_id' => null,
113                'TsumegoComment.deleted' => false,
114            ],
115            'order' => 'TsumegoComment.created ASC',
116            'contain' => ['User'],
117        ]) ?: [];
118
119        $plainComments = [];
120        foreach ($plainCommentsRaw as $commentRaw)
121        {
122            $comment = $commentRaw['TsumegoComment'];
123            $comment['user'] = !empty($commentRaw['User']) ? $commentRaw['User'] : ['name' => '[deleted user]', 'isAdmin' => 0];
124
125            // Process message for coordinates with global counter
126            $array = TsumegosController::commentCoordinates($comment['message'], $counter, true);
127            $comment['message'] = $array[0];
128            if (!empty($array[1]))
129            {
130                $allCoordinates[] = $array[1];
131                $counter++;
132            }
133
134            $plainComments[] = $comment;
135        }
136
137        return [
138            'issues' => $issues,
139            'plainComments' => $plainComments,
140            'coordinates' => $allCoordinates,
141        ];
142    }
143}