Completed
Push — master ( d55d81...7c5112 )
by
unknown
12s
created

RecordHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 11
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 4
crap 1
1
<?php
2
3
namespace eXpansion\Bundle\LocalRecords\Services;
4
5
use eXpansion\Bundle\LocalRecords\Model\Map\RecordTableMap;
6
use eXpansion\Bundle\LocalRecords\Model\Record;
7
use eXpansion\Bundle\LocalRecords\Model\RecordQueryBuilder;
8
use eXpansion\Framework\PlayersBundle\Model\Map\PlayerTableMap;
9
use eXpansion\Framework\PlayersBundle\Storage\PlayerDb;
10
use Propel\Runtime\Propel;
11
12
/**
13
 * Class RecordHandler
14
 *
15
 * @package eXpansion\Bundle\LocalRecords\Model;
16
 * @author  oliver de Cramer <[email protected]>
17
 */
18
class RecordHandler
19
{
20
    /**
21
     * Available order logic for score.
22
     */
23
    const ORDER_ASC = "ASC";
24
    const ORDER_DESC = "DESC";
25
26
    /**
27
     * List of event types
28
     */
29
    const EVENT_TYPE_FIRST_TIME = 'first_time';
30
    const EVENT_TYPE_SAME_SCORE = 'same_score';
31
    const EVENT_TYPE_SAME_POS = 'same_position';
32
    const EVENT_TYPE_BETTER_POS = 'better_position';
33
34
    /**
35
     * List of data in the associative array returned.
36
     */
37
    const COL_EVENT = 'event';
38
    const COL_RECORD = 'record';
39
    const COL_OLD_RECORD = 'old_record';
40
    const COL_POS = 'position';
41
    const COL_OLD_POS = 'old_position';
42
    const COL_RECORDS = 'records';
43
44
    /** @var int */
45
    protected $nbRecords;
46
47
    /** @var string */
48
    protected $ordering;
49
50
    /** @var RecordQueryBuilder */
51
    protected $recordQueryBuilder;
52
53
    /** @var PlayerDb */
54
    protected $playerDb;
55
56
    /** @var Record[] */
57
    protected $records = [];
58
59
    /** @var Record[] */
60
    protected $recordsPerPlayer = [];
61
62
    /** @var int[] */
63
    protected $positionPerPlayer = [];
64
65
    /** @var int */
66
    protected $currentNbLaps;
67
68
    /** @var string */
69
    protected $currentMapUid;
70
71
    /**
72
     * RecordHandler constructor.
73
     *
74
     * @param RecordQueryBuilder $recordQueryBuilder
75
     * @param PlayerDb           $playerDb
76
     * @param int                $nbRecords
77
     * @param string             $ordering
78
     */
79 14 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
        RecordQueryBuilder $recordQueryBuilder,
81
        PlayerDb $playerDb,
82
        $nbRecords,
83
        $ordering = self::ORDER_ASC
84
    ) {
85 14
        $this->recordQueryBuilder = $recordQueryBuilder;
86 14
        $this->nbRecords = $nbRecords;
87 14
        $this->ordering = $ordering;
88 14
        $this->playerDb = $playerDb;
89 14
    }
90
91
    /**
92
     * @return Record[]
93
     */
94 7
    public function getRecords()
95
    {
96 7
        return $this->records;
97
    }
98
99
    /**
100
     * Get the position of a player
101
     *
102
     * @param string $login
103
     *
104
     * @return integer|null
105
     */
106 5
    public function getPlayerPosition($login)
107
    {
108 5
        return isset($this->positionPerPlayer[$login]) ? $this->positionPerPlayer[$login] : null;
109
    }
110
111
    /**
112
     * Get a players record information.
113
     *
114
     * @param $login
115
     *
116
     * @return Record|null
117
     */
118 2
    public function getPlayerRecord($login)
119
    {
120 2
        return isset($this->recordsPerPlayer[$login]) ? $this->recordsPerPlayer[$login] : null;
121
    }
122
123
    /**
124
     * Load records for a certain map.
125
     *
126
     * @param string  $mapUid
127
     * @param integer $nbLaps
128
     * @throws \Propel\Runtime\Exception\PropelException
129
     */
130 12
    public function loadForMap($mapUid, $nbLaps)
131
    {
132 12
        foreach ($this->records as $record) {
133
            unset($record);
134
        }
135
136 12
        foreach ($this->recordsPerPlayer as $record) {
137
            unset($record);
138
        }
139
140 12
        RecordTableMap::clearInstancePool();
141
142 12
        $this->recordsPerPlayer = [];
143 12
        $this->positionPerPlayer = [];
144
145 12
        $this->currentMapUid = $mapUid;
146 12
        $this->currentNbLaps = $nbLaps;
147
148 12
        $this->records = $this->recordQueryBuilder
149 12
            ->getMapRecords($mapUid, $nbLaps, $this->getScoreOrdering(), $this->nbRecords);
150
151 12
        $position = 1;
152 12
        foreach ($this->records as $record) {
153 11
            $this->recordsPerPlayer[$record->getPlayer()->getLogin()] = $record;
154 11
            $this->positionPerPlayer[$record->getPlayer()->getLogin()] = $position++;
155
        }
156 12
    }
157
158
    /**
159
     * Load records for certain players only.
160
     *
161
     * @param $mapUid
162
     * @param $nbLaps
163
     * @param $logins
164
     * @throws \Propel\Runtime\Exception\PropelException
165
     */
166 1
    public function loadForPlayers($mapUid, $nbLaps, $logins)
167
    {
168 1
        $logins = array_diff($logins, array_keys($this->recordsPerPlayer));
169
170 1
        if (!empty($logins)) {
171 1
            $records = $this->recordQueryBuilder->getPlayerMapRecords($mapUid, $nbLaps, $logins);
172
173 1
            foreach ($records as $record) {
174 1
                $this->recordsPerPlayer[$record->getPlayer()->getLogin()] = $record;
175
            }
176
        }
177 1
    }
178
179
    /**
180
     * Save all new records.
181
     * @param bool $releaseRecords
182
     * @throws \Propel\Runtime\Exception\PropelException
183
     */
184
    public function save($releaseRecords = true)
185
    {
186
187
        $con = Propel::getWriteConnection(RecordTableMap::DATABASE_NAME);
188
        $con->beginTransaction();
189
190
        foreach ($this->recordsPerPlayer as $record) {
191
            $record->save();
192
            if ($releaseRecords) {
193
                unset($record);
194
            }
195
196
        }
197
198
        foreach ($this->records as $record) {
199
            if ($releaseRecords) {
200
                unset($record);
201
            }
202
        }
203
204
        $con->commit();
205
206
        RecordTableMap::clearInstancePool();
207
        PlayerTableMap::clearInstancePool();
208
    }
209
210
    /**
211
     * Add a new record
212
     *
213
     * @param string $login
214
     * @param int    $score
215
     * @param int[]  $checkpoints
216
     *
217
     * @return array|null Data for the new records.
218
     * @throws \Propel\Runtime\Exception\PropelException
219
     */
220
    public function addRecord($login, $score, $checkpoints)
221
    {
222
        $oldPosition = isset($this->positionPerPlayer[$login]) ? $this->positionPerPlayer[$login] : count($this->records) + 1;
223
        $record = isset($this->recordsPerPlayer[$login]) ? $this->recordsPerPlayer[$login] : $this->getNewRecord($login);
224 12
        $this->recordsPerPlayer[$login] = $record;
225
226 12
        $oldRecord = clone $record;
227 12
        $this->updateRecordStats($record, $score);
228 12
229
        if (empty($this->records)) {
230 12
            $record->setScore($score);
231 12
            $record->setCreatedAt(new \DateTime());
232
            $record->setCheckpoints($checkpoints);
233 12
234 2
            $this->records[0] = $record;
235 2
            $this->positionPerPlayer[$record->getPlayer()->getLogin()] = 1;
236 2
            $this->recordsPerPlayer[$record->getPlayer()->getLogin()] = $record;
237
238 2
            return [
239 2
                self::COL_EVENT => self::EVENT_TYPE_FIRST_TIME,
240 2
                self::COL_RECORD => $record,
241
                self::COL_RECORDS => $this->records,
242
                self::COL_POS => 1,
243 2
            ];
244 2
        }
245 2
246 2
        // Check if first time of this player.
247
        $firstTime = is_null($record->getScore());
248
249
        if ($score == $record->getScore()) {
250
            return [
251 12
                self::COL_EVENT => self::EVENT_TYPE_SAME_SCORE,
252
                self::COL_RECORD => $record,
253 12
                self::COL_OLD_RECORD => $oldRecord,
254
                self::COL_RECORDS => $this->records,
255 1
            ];
256 1
        }
257 1
258 1
        if ($firstTime || $this->compareNewScore($record, $score)) {
259
            $recordIndex = $oldPosition - 1;
260
            $newPosition = $oldPosition;
261
262 11
            $this->records[$recordIndex] = $record;
263 9
            $this->positionPerPlayer[$record->getPlayer()->getLogin()] = $oldPosition;
264 9
265
            while ($recordIndex > 0 && $this->compareNewScore($this->records[$recordIndex - 1], $score)) {
266 9
                $previousRecord = $this->records[$recordIndex - 1];
267 9
268
                $this->records[$recordIndex - 1] = $record;
269 9
                $this->records[$recordIndex] = $previousRecord;
270 5
271
                $newPosition = $recordIndex;
272 5
                $this->positionPerPlayer[$record->getPlayer()->getLogin()] = $recordIndex;
273 5
                $this->positionPerPlayer[$previousRecord->getPlayer()->getLogin()] = $recordIndex + 1;
274
275 5
                $recordIndex--;
276 5
            }
277 5
278
            $record->setScore($score);
279 5
            $record->setUpdatedAt(new \DateTime());
280
            $record->setCheckpoints($checkpoints);
281
282 9
            // Remove entries whose position is superior to the limit.
283 9
            $this->records = array_slice($this->records, 0, $this->nbRecords);
284 9
285
            if ($newPosition <= $this->nbRecords) {
286
                if ($newPosition != $oldPosition || $firstTime) {
287 9
                    return [
288
                        self::COL_EVENT => self::EVENT_TYPE_BETTER_POS,
289 9
                        self::COL_RECORD => $record,
290 8
                        self::COL_OLD_RECORD => $oldRecord,
291
                        self::COL_RECORDS => $this->records,
292 6
                        self::COL_POS => $newPosition,
293 6
                        self::COL_OLD_POS => $firstTime ? null : $oldPosition,
294 6
                    ];
295 6
                }
296 6
297 6
                return [
298
                    self::COL_EVENT => self::EVENT_TYPE_SAME_POS,
299
                    self::COL_RECORD => $record,
300
                    self::COL_OLD_RECORD => $oldRecord,
301
                    self::COL_RECORDS => $this->records,
302 2
                    self::COL_POS => $newPosition,
303 2
                ];
304 2
            }
305 2
        }
306 2
307
        return null;
308
    }
309
310
    /**
311 5
     * Get a new record instance.
312
     *
313
     * @param string $login
314
     *
315
     * @return Record
316
     */
317
    protected function getNewRecord($login)
318
    {
319
        $record = new Record();
320
        $record->setPlayer($this->playerDb->get($login));
321 6
        $record->setNbLaps($this->currentNbLaps);
322
        $record->setNbFinish(0);
323 6
        $record->setMapUid($this->currentMapUid);
324 6
325 6
        return $record;
326 6
    }
327 6
328
    /**
329 6
     * Update Records statistics.
330
     *
331
     * @param Record         $record
332
     * @param        integer $score
333
     */
334
    protected function updateRecordStats(Record $record, $score)
335
    {
336
        $record->setAvgScore(
337
            (($record->getAvgScore() * $record->getNbFinish()) + $score) / ($record->getNbFinish() + 1)
338 12
        );
339
        $record->setNbFinish($record->getNbFinish() + 1);
340 12
    }
341 12
342
343 12
    /**
344 12
     * Get ordering use for sorting.
345
     *
346
     * @return string
347
     */
348
    protected function getScoreOrdering()
349
    {
350
        return $this->ordering;
351
    }
352 13
353
    /**
354 13
     * @param int    $newScore
355
     * @param Record $record
356
     *
357
     * @return bool
358
     */
359
    protected function compareNewScore($record, $newScore)
360
    {
361
        if ($this->getScoreOrdering() == self::ORDER_ASC) {
362
            return $newScore <= $record->getScore();
363 11
        } else {
364
            return $newScore >= $record->getScore();
365 11
        }
366 10
    }
367
}
368