Completed
Pull Request — master (#186)
by Vladimir
28:22 queued 13:18
created

Team::getTeams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * This file contains functionality relating to the teams belonging to the current league
4
 *
5
 * @package    BZiON\Models
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * A league team
11
 * @package    BZiON\Models
12
 */
13
class Team extends AvatarModel implements TeamInterface, DuplexUrlInterface, EloInterface
14
{
15
    /**
16
     * The description of the team written in markdown
17
     *
18
     * @var string
19
     */
20
    protected $description;
21
22
    /**
23
     * The creation date of the team
24
     *
25
     * @var TimeDate
26
     */
27
    protected $created;
28
29
    /**
30
     * The team's current elo
31
     *
32
     * @var int
33
     */
34
    protected $elo;
35
36
    /**
37
     * The team's activity
38
     *
39
     * null if we haven't calculated it yet
40
     *
41
     * @var float|null
42
     */
43
    protected $activity = null;
44
45
    /**
46
     * The id of the team leader
47
     *
48
     * @var int
49
     */
50
    protected $leader;
51
52
    /**
53
     * The number of matches won
54
     *
55
     * @var int
56
     */
57
    protected $matches_won;
58
59
    /**
60
     * The number of matches lost
61
     *
62
     * @var int
63
     */
64
    protected $matches_lost;
65
66
    /**
67
     * The number of matches tied
68
     *
69
     * @var int
70
     */
71
    protected $matches_draw;
72
73
    /**
74
     * The total number of matches
75
     *
76
     * @var int
77
     */
78
    protected $matches_total;
79
80
    /**
81
     * The number of members
82
     *
83
     * @var int
84
     */
85
    protected $members;
86
87
    /**
88
     * Whether or not the team is closed. A closed team only allows new members through invitations.
89
     *
90
     * @var bool
91
     */
92
    protected $is_closed;
93
94
    const DEFAULT_STATUS = 'closed';
95
96
    const DELETED_COLUMN = 'is_deleted';
97
    const TABLE = "teams";
98
99
    /**
100
     * The location where avatars will be stored
101
     */
102
    const AVATAR_LOCATION = "/web/assets/imgs/avatars/teams/";
103
104
    const CREATE_PERMISSION = Permission::CREATE_TEAM;
105
    const EDIT_PERMISSION = Permission::EDIT_TEAM;
106
    const SOFT_DELETE_PERMISSION = Permission::SOFT_DELETE_TEAM;
107
    const HARD_DELETE_PERMISSION = Permission::HARD_DELETE_TEAM;
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    protected function assignResult($team)
113
    {
114
        $this->name = $team['name'];
115
        $this->alias = $team['alias'];
116
        $this->avatar = $team['avatar'];
117
        $this->created = TimeDate::fromMysql($team['created']);
118
        $this->elo = $team['elo'];
119 55
        $this->leader = $team['leader'];
120
        $this->matches_won = $team['matches_won'];
121 55
        $this->matches_lost = $team['matches_lost'];
122 55
        $this->matches_draw = $team['matches_draw'];
123 55
        $this->members = $team['members'];
124 55
        $this->is_closed = $team['is_closed'];
125 55
        $this->is_deleted = $team['is_deleted'];
126 55
127 55
        $this->matches_total = $this->matches_won + $this->matches_lost + $this->matches_draw;
128 55
129 55
        $this->activity = isset($team['activity']) ? $team['activity'] : 0;
130 55
    }
131 55
132 55
    protected function assignLazyResult($result)
133 55
    {
134
        $this->description = $result['description'];
135 55
    }
136 55
137
    /**
138
     * Adds a new member to the team
139
     *
140
     * @param int $id The id of the player to add to the team
141
     *
142
     * @return bool|null True if both the player was added to the team AND the team member count was incremented
143
     */
144 View Code Duplication
    public function addMember($id)
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...
145 55
    {
146
        $player = Player::get($id);
147 55
148
        if (!$player->isTeamless()) {
149 55
            throw new Exception("The player already belongs in a team");
150
        }
151
152
        $player->setTeam($this->getId());
153 55
        $this->update('members', ++$this->members);
154 55
    }
155 55
156
    /**
157
     * Increase or decrease the ELO of the team
158
     *
159
     * @param int   $adjust The value to be added to the current ELO (negative to subtract)
160
     * @param Match $match  The match where this Elo change took place
161
     */
162
    public function adjustElo($adjust, Match $match = null)
163 33
    {
164
        $this->elo += $adjust;
165 33
        $this->update("elo", $this->elo);
166 33
    }
167 33
168
    /**
169
     * Change the ELO of the team
170
     *
171
     * @param int $elo The new team ELO
172
     */
173
    public function setElo($elo)
174 6
    {
175
        $this->updateProperty($this->elo, "elo", $elo);
176 6
    }
177 6
178
    /**
179
     * Increment the team's match count
180
     *
181
     * @param int    $adjust The number to add to the current matches number (negative to substract)
182
     * @param string $type   The match count that should be changed. Can be 'win', 'draw' or 'loss'
183
     */
184
    public function changeMatchCount($adjust, $type)
185 33
    {
186
        $this->matches_total += $adjust;
187 33
188
        switch ($type) {
189
            case "win":
190 33
            case "won":
191 31
                $this->update("matches_won", $this->matches_won += $adjust);
192 15
193
                return;
194 15
            case "loss":
195 31
            case "lost":
196 16
                $this->update("matches_lost", $this->matches_lost += $adjust);
197 16
198
                return;
199 16
            default:
200
                $this->update("matches_draw", $this->matches_draw += $adjust);
201 16
202
                return;
203 16
        }
204
    }
205
206
    /**
207
     * Decrement the team's match count by one
208
     *
209
     * @param string $type The type of the match. Can be 'win', 'draw' or 'loss'
210
     */
211
    public function decrementMatchCount($type)
212
    {
213
        $this->changeMatchCount(-1, $type);
214
    }
215
216
    /**
217
     * Get the activity of the team
218
     *
219
     * @return float The team's activity
220
     */
221
    public function getActivity()
222 1
    {
223
        return $this->activity;
224 1
    }
225
226 1
    /**
227
     * Get the creation date of the team
228
     *
229
     * @return TimeDate The creation date of the team
230
     */
231
    public function getCreationDate()
232
    {
233
        return $this->created->copy();
234 1
    }
235 1
236 1
    /**
237 1
     * Get the description of the team
238
     *
239
     * @return string  The description of the team
240
     */
241
    public function getDescription()
242 1
    {
243
        $this->lazyLoad();
244
245
        return $this->description;
246
    }
247
248
    /**
249
     * Get the current elo of the team
250 2
     *
251
     * @return int The elo of the team
252 2
     */
253
    public function getElo()
254
    {
255
        return $this->elo;
256
    }
257
258
    /**
259
     * Get the leader of the team
260 1
     *
261
     * @return Player The object representing the team leader
262 1
     */
263
    public function getLeader()
264
    {
265
        return Player::get($this->leader);
266
    }
267
268
    /**
269
     * Get the matches this team has participated in
270 34
     *
271
     * @param string $matchType The filter for match types: "all", "wins", "losses", or "draws"
272 34
     * @param int    $count     The amount of matches to be retrieved
273
     * @param int    $page      The number of the page to return
274
     *
275
     * @return Match[] The array of match IDs this team has participated in
276
     */
277
    public function getMatches($matchType = "all", $count = 5, $page = 1)
278
    {
279
        return Match::getQueryBuilder()
280 2
             ->active()
281
             ->with($this, $matchType)
282 2
             ->sortBy('time')->reverse()
283
             ->limit($count)->fromPage($page)
284
             ->getModels($fast = true);
285
    }
286
287
    /**
288
     * Get the number of matches that resulted as a draw
289
     *
290
     * @return int The number of matches, respectively
291
     */
292
    public function getMatchesDraw()
293
    {
294 2
        return $this->matches_draw;
295
    }
296 2
297 2
    /**
298 2
     * Get the number of matches that the team has lost
299 2
     *
300 2
     * @return int The number of matches, respectively
301 2
     */
302
    public function getMatchesLost()
303
    {
304
        return $this->matches_lost;
305
    }
306
307
    /**
308
     * Get the URL that points to the team's list of matches
309 1
     *
310
     * @return string The team's list of matches
311 1
     */
312
    public function getMatchesURL()
313
    {
314
        return Service::getGenerator()->generate("match_by_team_list", array("team" => $this->getAlias()));
315
    }
316
317
    /**
318
     * Get the number of matches that the team has won
319 1
     *
320
     * @return int The number of matches, respectively
321 1
     */
322
    public function getMatchesWon()
323
    {
324
        return $this->matches_won;
325
    }
326
327
    /**
328
     * Get the members on the team
329
     *
330
     * @return Player[] The members on the team
331
     */
332
    public function getMembers()
333
    {
334
        $leader = $this->leader;
335
        $members = Player::getTeamMembers($this->id);
336
337
        usort($members, function ($a, $b) use ($leader) {
338
            // Leader always goes first
339 1
            if ($a->getId() == $leader) {
340
                return -1;
341 1
            }
342
            if ($b->getId() == $leader) {
343
                return 1;
344
            }
345
346
            // Sort the rest of the players alphabetically
347
            $sort = Player::getAlphabeticalSort();
348
349 2
            return $sort($a, $b);
350
        });
351 2
352 2
        return $members;
353
    }
354
355
    /**
356 2
     * Get the name of the team
357 2
     *
358
     * @return string The name of the team
359 1
     */
360 1
    public function getName()
361
    {
362
        if ($this->name === null) {
363
            return "None";
364
        }
365
        return $this->name;
366
    }
367 2
368
    /**
369 2
     * Get the name of the team, safe for use in your HTML
370
     *
371
     * @return string The name of the team
372
     */
373
    public function getEscapedName()
374
    {
375
        if (!$this->valid) {
376
            return "<em>None</em>";
377 55
        }
378
        return $this->escape($this->name);
379 55
    }
380
381
    /**
382 55
     * Get the number of members on the team
383
     *
384
     * @return int The number of members on the team
385
     */
386
    public function getNumMembers()
387
    {
388
        return $this->members;
389
    }
390 1
391
    /**
392 1
     * Get the total number of matches this team has played
393
     *
394
     * @return int The total number of matches this team has played
395 1
     */
396
    public function getNumTotalMatches()
397
    {
398
        return $this->matches_total;
399
    }
400
401
    /**
402
     * Get the rank category a team belongs too based on their ELO
403 2
     *
404
     * This value is always a multiple of 100 and less than or equal to 2000
405 2
     *
406
     * @return int The rank category a team belongs to
407
     */
408
    public function getRankValue()
409
    {
410
        return min(2000, floor($this->getElo() / 100) * 100);
411
    }
412
413 2
    /**
414
     * Get the HTML for an image with the rank symbol
415 2
     *
416
     * @return string The HTML for a rank image
417
     */
418
    public function getRankImageLiteral()
419
    {
420
        return '<div class="c-rank c-rank--' . $this->getRankValue() . '" aria-hidden="true"></div>';
421
    }
422
423
    /**
424
     * Increment the team's match count by one
425 1
     *
426
     * @param string $type The type of the match. Can be 'win', 'draw' or 'loss'
427 1
     */
428
    public function incrementMatchCount($type)
429
    {
430
        $this->changeMatchCount(1, $type);
431
    }
432
433
    /**
434
     * Check if a player is part of this team
435 1
     *
436
     * @param int $playerID The player to check
437 1
     *
438
     * @return bool True if the player belongs to this team
439
     */
440
    public function isMember($playerID)
441
    {
442
        $player = Player::get($playerID);
443
444
        return $player->getTeam()->isSameAs($this);
445
    }
446
447
    /**
448
     * Removes a member from the team
449
     *
450
     * @param  int  $id The id of the player to remove
451
     * @return void
452
     */
453 View Code Duplication
    public function removeMember($id)
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...
454
    {
455
        if (!$this->isMember($id)) {
456
            throw new Exception("The player is not a member of that team");
457 2
        }
458
459 2
        $player = Player::get($id);
460
461 2
        $player->update("team", null);
462
        $this->update('members', --$this->members);
463
    }
464
465
    /**
466
     * Update the description of the team
467
     *
468
     * @param  string $description The description of the team written as markdown
469
     * @return void
470 2
     */
471
    public function setDescription($description)
472 2
    {
473
        $this->update("description", $description);
474
    }
475
476 2
    /**
477
     * Change the status of the team
478 2
     *
479 2
     * @param  string $newStatus The new status of the team (open, closed, disabled or deleted)
480 2
     * @return self
481
     */
482
    public function setStatus($newStatus)
483
    {
484
        return $this->updateProperty($this->status, 'status', $newStatus);
485
    }
486
487
    /**
488
     * Change the leader of the team
489
     *
490
     * @param  int  $leader The ID of the new leader of the team
491
     * @return self
492
     */
493
    public function setLeader($leader)
494
    {
495
        return $this->updateProperty($this->leader, 'leader', $leader);
496
    }
497
498
    /**
499
     * Find if a specific match is the team's last one
500
     *
501
     * @param  int|Match $match The match
502
     * @return bool
503
     */
504
    public function isLastMatch($match)
505
    {
506
        // Find if this team participated in any matches after the current match
507
        return !Match::getQueryBuilder()
508
            ->with($this)
509
            ->where('status')->notEquals('deleted')
510
            ->where('time')->isAfter(Match::get($match)->getTimestamp())
511
            ->any();
512
    }
513
514
    /**
515
     * {@inheritdoc}
516
     */
517
    public function delete()
518
    {
519
        parent::delete();
520
521 1
        // Remove all the members of a deleted team
522
        $this->updateProperty($this->members, 'members', 0);
523
        $this->db->execute('UPDATE players SET team = NULL WHERE team = ?', $this->id);
524 1
    }
525 1
526 1
    /**
527 1
     * {@inheritdoc}
528 1
     */
529
    public function supportsMatchCount()
530
    {
531
        return $this->isValid();
532
    }
533
534 1
    /**
535
     * Create a new team
536 1
     *
537
     * @param  string           $name        The name of the team
538
     * @param  int              $leader      The ID of the person creating the team, also the leader
539 1
     * @param  string           $avatar      The URL to the team's avatar
540 1
     * @param  string           $description The team's description
541 1
     * @param  string           $status      The team's status (open, closed, disabled or deleted)
542
     * @param  string|\TimeDate $created     The date the team was created
543
     *
544
     * @return Team   An object that represents the newly created team
545
     */
546 42
    public static function createTeam($name, $leader, $avatar, $description, $status = 'closed', $created = "now")
547
    {
548 42
        $created = TimeDate::from($created);
549
550
        $team = self::create(array(
551
            'name'         => $name,
552
            'alias'        => self::generateAlias($name),
553
            'description'  => $description,
554
            'elo'          => 1200,
555
            'matches_won'  => 0,
556
            'matches_draw' => 0,
557
            'matches_lost' => 0,
558
            'members'      => 0,
559
            'avatar'       => $avatar,
560
            'leader'       => $leader,
561
            'status'       => $status,
562
            'created'      => $created->toMysql(),
563 56
        ));
564
565 56
        $team->addMember($leader);
566
        $team->getIdenticon($team->getId());
567 56
568 56
        return $team;
569 56
    }
570 56
571 56
    /**
572 56
     * Get all the teams in the database that are not disabled or deleted
573 56
     *
574 56
     * @return Team[] An array of Team IDs
575 56
     */
576 56
    public static function getTeams()
577 56
    {
578 56
        return self::arrayIdToModel(
579 56
            self::fetchIdsFrom(
580 56
                "status", array("disabled", "deleted"),
581
                true, "ORDER BY elo DESC"
582
            )
583 55
        );
584 55
    }
585
586 55
    /**
587
     * Get a single team by its name
588
     *
589
     * @param  string $name The team name to look for
590
     * @return Team
591
     */
592
    public static function getFromName($name)
593
    {
594
        $team = static::get(self::fetchIdFrom($name, 'name'));
595
596
        return $team->inject('name', $name);
597
    }
598
599
    /**
600
     * Alphabetical order function for use in usort (case-insensitive)
601
     * @return Closure The sort function
602
     */
603
    public static function getAlphabeticalSort()
604
    {
605
        return function (Team $a, Team $b) {
606
            return strcasecmp($a->getName(), $b->getName());
607
        };
608
    }
609
610 1
    /**
611
     * {@inheritdoc}
612 1
     */
613
    public static function getEagerColumnsList()
614 1
    {
615
        return [
616
            'id',
617
            'name',
618
            'alias',
619
            'avatar',
620
            'created',
621
            'elo',
622
            'leader',
623 1
            'matches_won',
624
            'matches_lost',
625 1
            'matches_draw',
626
            'members',
627
            'is_closed',
628
            'is_deleted',
629
        ];
630
    }
631 1
632
    /**
633 1
     * {@inheritdoc}
634
     */
635
    public static function getLazyColumnsList()
636
    {
637
        return [
638
            'description',
639
        ];
640 1
    }
641
642 1
    /**
643 1
     * {@inheritdoc}
644
     *
645
     * @return TeamQueryBuilder
646
     */
647
    public static function getQueryBuilder()
648
    {
649
        return TeamQueryBuilder::createForModel(Team::class)
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
650
            ->setNameColumn('name')
651
        ;
652
    }
653
654
    /**
655
     * {@inheritdoc}
656 1
     */
657
    protected function isEditor($player)
658 1
    {
659
        return $player->isSameAs($this->getLeader());
660
    }
661
}
662