Completed
Push — master ( c087ad...47fff7 )
by Benedikt
02:33
created

RankingSystemService   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 466
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
dl 0
loc 466
c 0
b 0
f 0
rs 3.3333
wmc 65
lcom 1
cbo 15

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A getEarliestInfluence() 0 4 1
B updateRankingForTournament() 0 12 5
C updateRankingFrom() 0 58 10
A getAverage() 0 12 3
A getEntities() 0 5 1
A getEntityManager() 0 4 1
A getEntriesOfPlayers() 0 8 2
C getOrCreateChange() 0 47 15
A getOrCreateRankingSystemListEntry() 0 20 3
getAdditionalFields() 0 1 ?
getChanges() 0 1 ?
getEntitiesQueryBuilder() 0 2 ?
getLevel() 0 1 ?
A startPoints() 0 4 1
B cloneInto() 0 37 5
A deleteOldChanges() 0 18 2
C getEarliestEntityInfluence() 0 22 8
C recomputeBasedOn() 0 31 7

How to fix   Complexity   

Complex Class

Complex classes like RankingSystemService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RankingSystemService, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
/**
4
 * Created by PhpStorm.
5
 * User: benedikt
6
 * Date: 1/2/18
7
 * Time: 2:32 PM
8
 */
9
10
namespace Tfboe\FmLib\Service\RankingSystem;
11
12
13
use Doctrine\Common\Collections\Collection;
14
use Doctrine\ORM\EntityManagerInterface;
15
use Doctrine\ORM\QueryBuilder;
16
use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity;
17
use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyInterface;
18
use Tfboe\FmLib\Entity\PlayerInterface;
19
use Tfboe\FmLib\Entity\RankingSystemChangeInterface;
20
use Tfboe\FmLib\Entity\RankingSystemInterface;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Tfboe\FmLib\Service\Rank...\RankingSystemInterface.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
use Tfboe\FmLib\Entity\RankingSystemListEntryInterface;
22
use Tfboe\FmLib\Entity\RankingSystemListInterface;
23
use Tfboe\FmLib\Entity\TournamentInterface;
24
use Tfboe\FmLib\Exceptions\PreconditionFailedException;
25
use Tfboe\FmLib\Service\ObjectCreatorServiceInterface;
26
27
28
/**
29
 * Class RankingSystemService
30
 * @package Tfboe\FmLib\Service\RankingSystemService
31
 * @SuppressWarnings(PHPMD) TODO: refactor this class and remove suppress warnings
32
 */
33
abstract class RankingSystemService implements \Tfboe\FmLib\Service\RankingSystem\RankingSystemInterface
34
{
35
//<editor-fold desc="Fields">
36
  /** @var EntityManagerInterface */
37
  private $entityManager;
38
  /** @var TimeServiceInterface */
39
  private $timeService;
40
  /** @var EntityComparerInterface */
41
  private $entityComparer;
42
  /**
43
   * @var RankingSystemChangeInterface[][][]
44
   * first key: tournament hierarchy entity id
45
   * second key: ranking system id
46
   * third key: player id
47
   */
48
  private $changes;
49
  /**
50
   * @var RankingSystemChangeInterface[][][]
51
   * first key: tournament hierarchy entity id
52
   * second key: ranking system id
53
   * third key: player id
54
   */
55
  private $deletedChanges;
56
  /**
57
   * List of ranking systems for which update ranking got already called, indexed by id
58
   * @var RankingSystemService[]
59
   */
60
  private $updateRankingCalls;
61
62
  /** @var ObjectCreatorServiceInterface */
63
  private $objectCreatorService;
64
//</editor-fold desc="Fields">
65
66
//<editor-fold desc="Constructor">
67
  /**
68
   * RankingSystemService constructor.
69
   * @param EntityManagerInterface $entityManager
70
   * @param TimeServiceInterface $timeService
71
   * @param EntityComparerInterface $entityComparer
72
   * @param ObjectCreatorServiceInterface $objectCreatorService
73
   */
74
  public function __construct(EntityManagerInterface $entityManager, TimeServiceInterface $timeService,
75
                              EntityComparerInterface $entityComparer,
76
                              ObjectCreatorServiceInterface $objectCreatorService)
77
  {
78
    $this->entityManager = $entityManager;
79
    $this->timeService = $timeService;
80
    $this->entityComparer = $entityComparer;
81
    $this->changes = [];
82
    $this->deletedChanges = [];
83
    $this->updateRankingCalls = [];
84
    $this->objectCreatorService = $objectCreatorService;
85
  }
86
//</editor-fold desc="Constructor">
87
88
//<editor-fold desc="Public Methods">
89
  /**
90
   * @inheritDoc
91
   */
92
  public function getEarliestInfluence(RankingSystemInterface $ranking, TournamentInterface $tournament): ?\DateTime
93
  {
94
    return $this->getEarliestEntityInfluence($ranking, $tournament, false);
95
  }
96
97
  /**
98
   * @inheritdoc
99
   */
100
  public function updateRankingForTournament(RankingSystemInterface $ranking, TournamentInterface $tournament,
101
                                             ?\DateTime $oldInfluence)
102
  {
103
    $earliestInfluence = $this->getEarliestInfluence($ranking, $tournament);
104
    if ($oldInfluence !== null &&
105
      ($earliestInfluence === null || $oldInfluence < $earliestInfluence)) {
106
      $earliestInfluence = $oldInfluence;
107
    }
108
    if ($earliestInfluence !== null) {
109
      $this->updateRankingFrom($ranking, $earliestInfluence);
110
    }
111
  }
112
113
  /**
114
   * @inheritDoc
115
   */
116
  public function updateRankingFrom(RankingSystemInterface $ranking, \DateTime $from)
117
  {
118
    // can only be called once per ranking system!!!
119
    if (array_key_exists($ranking->getId(), $this->updateRankingCalls)) {
120
      throw new PreconditionFailedException();
121
    }
122
    $this->updateRankingCalls[$ranking->getId()] = $ranking;
123
    //find first reusable
124
    /** @var RankingSystemListInterface[] $lists */
125
    $lists = array_values($ranking->getLists()->toArray());
126
127
    $current = null;
128
    /** @var RankingSystemListInterface $lastReusable */
129
    $lastReusable = null;
130
    $toUpdate = [];
131
132
    foreach ($lists as $list) {
133
      if ($list->isCurrent()) {
134
        $current = $list;
135
      } else if ($list->getLastEntryTime() >= $from) {
136
        $toUpdate[] = $list;
137
      } else if ($lastReusable === null || $list->getLastEntryTime() > $lastReusable->getLastEntryTime()) {
138
        $lastReusable = $list;
139
      }
140
    }
141
142
    if ($lastReusable === null) {
143
      $lastReusable = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class);
144
    }
145
146
    usort($toUpdate, function (RankingSystemListInterface $list1, RankingSystemListInterface $list2) {
147
      return $list1->getLastEntryTime() <=> $list2->getLastEntryTime();
148
    });
149
150
    $entities = $this->getEntities($ranking, $lastReusable->getLastEntryTime());
151
    //sort entities
152
    $this->timeService->clearTimes();
153
    usort($entities, function ($entity1, $entity2) {
154
      return $this->entityComparer->compareEntities($entity1, $entity2);
155
    });
156
157
    $this->deleteOldChanges($ranking, $entities);
158
159
    $nextEntityIndex = 0;
160
    foreach ($toUpdate as $list) {
161
      $this->recomputeBasedOn($list, $lastReusable, $entities, $nextEntityIndex);
162
      $lastReusable = $list;
163
    }
164
165
    if ($current === null) {
166
      /** @var RankingSystemListInterface $current */
167
      $current = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class);
168
      $current->setCurrent(true);
169
      $this->entityManager->persist($current);
170
      $current->setRankingSystem($ranking);
171
    }
172
    $this->recomputeBasedOn($current, $lastReusable, $entities, $nextEntityIndex);
173
  }
174
//</editor-fold desc="Public Methods">
175
176
//<editor-fold desc="Protected Final Methods">
177
  /**
178
   * Computes the average rating of the given entries
179
   * @param RankingSystemListEntryInterface[] $entries
180
   * @return float
181
   */
182
  protected final function getAverage(array $entries): float
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
183
  {
184
    $sum = 0.0;
185
    foreach ($entries as $entry) {
186
      $sum += $entry->getPoints();
187
    }
188
    if (count($entries) === 0) {
189
      return 0.0;
190
    } else {
191
      return $sum / count($entries);
192
    }
193
  }
194
195
  /**
196
   * Gets the relevant entities for updating
197
   * @param RankingSystemInterface $ranking the ranking for which to get the entities
198
   * @param \DateTime $from search for entities with a time value LARGER than $from, i.e. don't search for entities with
199
   *                        time value exactly $from
200
   * @return TournamentHierarchyEntity[]
201
   */
202
  protected final function getEntities(RankingSystemInterface $ranking, \DateTime $from): array
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
203
  {
204
    $query = $this->getEntitiesQueryBuilder($ranking, $from);
205
    return $query->getQuery()->getResult();
206
  }
207
208
  /**
209
   * @return EntityManagerInterface
210
   */
211
  protected final function getEntityManager(): EntityManagerInterface
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
212
  {
213
    return $this->entityManager;
214
  }
215
216
  /**
217
   * @param Collection|PlayerInterface[] $players
218
   * @param RankingSystemListInterface $list
219
   * @return RankingSystemListEntryInterface[] $entries
220
   */
221
  protected final function getEntriesOfPlayers(Collection $players, RankingSystemListInterface $list): array
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
222
  {
223
    $result = [];
224
    foreach ($players as $player) {
225
      $result[] = $this->getOrCreateRankingSystemListEntry($list, $player);
226
    }
227
    return $result;
228
  }
229
230
  /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException
231
  /**
232
   * Gets or creates a tournament system change entry for the given entity, ranking and player.
233
   * @param TournamentHierarchyInterface $entity the tournament hierarchy entity to search for
234
   * @param RankingSystemInterface $ranking the ranking system to search for
235
   * @param PlayerInterface $player the player to search for
236
   * @return RankingSystemChangeInterface the found or newly created ranking system change
237
   */
238
  protected final function getOrCreateChange(TournamentHierarchyInterface $entity, RankingSystemInterface $ranking,
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
239
                                             PlayerInterface $player)
240
  {
241
    $key1 = $entity->getId();
242
    $key2 = $ranking->getId();
243
    $key3 = $player->getPlayerId();
244
    if (!array_key_exists($key1, $this->changes) || !array_key_exists($key2, $this->changes[$key1]) ||
245
      !array_key_exists($key3, $this->changes[$key1][$key2])) {
246
      /** @var RankingSystemChangeInterface[] $changes */
247
      $changes = $this->entityManager->getRepository(RankingSystemChangeInterface::class)->findBy(
248
        ['hierarchyEntity' => $entity]);
249
      $this->changes[$key1] = [];
250
      foreach ($changes as $change) {
251
252
        $newKey2 = $change->getRankingSystem()->getId();
253
        $newKey3 = $change->getPlayer()->getPlayerId();
254
        if (!array_key_exists($key1, $this->deletedChanges) || !array_key_exists($key2, $this->deletedChanges[$key1]) ||
255
          !array_key_exists($key3, $this->deletedChanges[$key1][$key2])) {
256
          if (!array_key_exists($newKey2, $this->changes)) {
257
            $this->changes[$key1][$newKey2] = [];
258
          }
259
          $this->changes[$key1][$newKey2][$newKey3] = $change;
260
        }
261
      }
262
    }
263
    if (!array_key_exists($key2, $this->changes[$key1]) || !array_key_exists($key3, $this->changes[$key1][$key2])) {
264
      //create new change
265
      /** @var RankingSystemChangeInterface $change */
266
      $change = $this->objectCreatorService->createObjectFromInterface(RankingSystemChangeInterface::class,
267
        [array_keys($this->getAdditionalFields())]);
268
      foreach ($this->getAdditionalFields() as $field => $value) {
269
        // PropertyNotExistingException => we know for sure that the property exists (see 2 lines above)
270
        /** @noinspection PhpUnhandledExceptionInspection */
271
        $change->setProperty($field, 0);
272
      }
273
      $change->setHierarchyEntity($entity);
274
      $change->setRankingSystem($ranking);
275
      $change->setPlayer($player);
276
      $this->entityManager->persist($change);
277
      $this->changes[$key1][$key2][$key3] = $change;
278
      if (array_key_exists($key1, $this->deletedChanges) && array_key_exists($key2, $this->deletedChanges[$key1]) &&
279
        array_key_exists($key3, $this->deletedChanges[$key1][$key2])) {
280
        unset($this->deletedChanges[$key1][$key2][$key3]);
281
      }
282
    }
283
    return $this->changes[$key1][$key2][$key3];
284
  }
285
286
  /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException
287
  /**
288
   * @param RankingSystemListInterface $list the list in which to search for the entry or in which to add it
289
   * @param PlayerInterface $player the player to search for
290
   * @return RankingSystemListEntryInterface the found or the new entry
291
   */
292
  protected final function getOrCreateRankingSystemListEntry(RankingSystemListInterface $list,
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
293
                                                             PlayerInterface $player): RankingSystemListEntryInterface
294
  {
295
    $playerId = $player->getPlayerId();
296
    if (!$list->getEntries()->containsKey($playerId)) {
297
      /** @var RankingSystemListEntryInterface $entry */
298
      $entry = $this->objectCreatorService->createObjectFromInterface(RankingSystemListEntryInterface::class,
299
        [array_keys($this->getAdditionalFields())]);
300
      $entry->setPoints($this->startPoints());
301
      $entry->setPlayer($player);
302
      $entry->setRankingSystemList($list);
303
      foreach ($this->getAdditionalFields() as $field => $value) {
304
        // PropertyNotExistingException => we know for sure that the property exists (see 2 lines above)
305
        /** @noinspection PhpUnhandledExceptionInspection */
306
        $entry->setProperty($field, $value);
307
      }
308
      $this->entityManager->persist($entry);
309
    }
310
    return $list->getEntries()->get($playerId);
311
  }
312
//</editor-fold desc="Protected Final Methods">
313
314
//<editor-fold desc="Protected Methods">
315
  /**
316
   * Gets additional fields for this ranking type mapped to its start value
317
   * @return string[] list of additional fields
318
   */
319
  protected abstract function getAdditionalFields(): array;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
320
321
  /**
322
   * Gets all ranking changes for the given entity for the given list. Must return a change for each involved player.
323
   * The field pointsAfterwards get calculated afterwards and can be left empty.
324
   * @param TournamentHierarchyEntity $entity the entity for which to compute the ranking changes
325
   * @param RankingSystemListInterface $list the list for which to compute the ranking changes
326
   * @return RankingSystemChangeInterface[] the changes
327
   */
328
  protected abstract function getChanges(TournamentHierarchyEntity $entity, RankingSystemListInterface $list): array;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
329
330
  /**
331
   * Gets a query for getting the relevant entities for updating
332
   * @param RankingSystemInterface $ranking the ranking for which to get the entities
333
   * @param \DateTime $from search for entities with a time value LARGER than $from, i.e. don't search for entities with
334
   *                        time value exactly $from
335
   * @return QueryBuilder
336
   */
337
  protected abstract function getEntitiesQueryBuilder(RankingSystemInterface $ranking,
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
338
                                                      \DateTime $from): QueryBuilder;
339
340
  /**
341
   * Gets the level of the ranking system service (see Level Enum)
342
   * @return int
343
   */
344
  protected abstract function getLevel(): int;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
345
346
  /**
347
   * Gets the start points for a new player in the ranking
348
   * @return float
349
   */
350
  protected function startPoints(): float
351
  {
352
    return 0.0;
353
  }
354
//</editor-fold desc="Protected Methods">
355
356
//<editor-fold desc="Private Methods">
357
  /**
358
   * Clones all ranking values from base and inserts them into list, furthermore removes all remaining ranking values of
359
   * list. After this method was called list and base contain exactly the same rankings.
360
   * @param RankingSystemListInterface $list the ranking list to change
361
   * @param RankingSystemListInterface $base the ranking list to use as base list, this doesn't get changed
362
   */
363
  private function cloneInto(RankingSystemListInterface $list, RankingSystemListInterface $base)
364
  {
365
    /*//first remove all entries from list
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
366
    foreach($list->getEntries()->toArray() as $entry)
367
    {
368
      $list->getEntries()->removeElement($entry);
369
      $this->entityManager->remove($entry);
370
    }*/
371
372
    $clonedPlayers = [];
373
374
    foreach ($base->getEntries() as $entry) {
375
      $playerId = $entry->getPlayer()->getPlayerId();
376
      $clonedPlayers[$playerId] = true;
377
      if (!$list->getEntries()->containsKey($playerId)) {
378
        //create new entry
379
        /** @var RankingSystemListEntryInterface $entry */
380
        $clone = $this->objectCreatorService->createObjectFromInterface(RankingSystemListEntryInterface::class,
381
          [[]]);
382
        $clone->cloneSubClassDataFrom($entry);
383
        $this->entityManager->persist($clone);
384
        $clone->setPlayer($entry->getPlayer());
385
        $clone->setRankingSystemList($list);
386
      }
387
      $foundEntry = $list->getEntries()[$playerId];
388
      $foundEntry->setNumberRankedEntities($entry->getNumberRankedEntities());
389
      $foundEntry->setPoints($entry->getPoints());
390
    }
391
392
    //remove all unused entries from list
393
    foreach ($list->getEntries()->toArray() as $playerId => $entry) {
394
      if (!array_key_exists($playerId, $clonedPlayers)) {
395
        $list->getEntries()->removeElement($entry);
396
        $this->entityManager->remove($entry);
397
      }
398
    }
399
  }
400
401
402
  /**
403
   * @param RankingSystemInterface $ranking
404
   * @param TournamentHierarchyEntity[] $entities
405
   */
406
  private function deleteOldChanges(RankingSystemInterface $ranking, array $entities)
407
  {
408
    //delete old changes
409
    $queryBuilder = $this->entityManager->createQueryBuilder();
410
    /** @var RankingSystemChangeInterface[] $changes */
411
    $changes = $queryBuilder
412
      ->from(RankingSystemChangeInterface::class, 'c')
413
      ->select('c')
414
      ->where($queryBuilder->expr()->eq('c.rankingSystem', $ranking))
415
      ->where($queryBuilder->expr()->in('c.hierarchyEntity', ':entities'))
416
      ->setParameter('entities', $entities)
417
      ->getQuery()->getResult();
418
    foreach ($changes as $change) {
419
      $this->deletedChanges[$change->getHierarchyEntity()->getId()][$ranking
420
        ->getId()][$change->getPlayer()->getPlayerId()] = $change;
421
      $this->entityManager->remove($change);
422
    }
423
  }
424
425
  /**
426
   * Gets the earliest influence for the given entity
427
   * @param RankingSystemInterface $ranking the ranking system for which to get the influence
428
   * @param TournamentHierarchyInterface $entity the entity to analyze
429
   * @param bool $parentIsRanked true iff a predecessor contained the given ranking in its ranking systems
430
   * @return \DateTime|null the earliest influence or null if $parentIsRanked is false and the entity and all its
431
   *                        successors do not have the ranking in its ranking systems
432
   */
433
  private function getEarliestEntityInfluence(RankingSystemInterface $ranking, TournamentHierarchyInterface $entity,
434
                                              bool $parentIsRanked): ?\DateTime
435
  {
436
    $this->timeService->clearTimes();
437
    $entityIsRanked = $parentIsRanked || $entity->getRankingSystems()->containsKey($ranking->getId());
438
    if ($entity->getLevel() === $this->getLevel()) {
439
      if ($entityIsRanked) {
440
        return $this->timeService->getTime($entity);
441
      } else {
442
        return null;
443
      }
444
    }
445
    $result = null;
446
447
    foreach ($entity->getChildren() as $child) {
448
      $earliest = $this->getEarliestEntityInfluence($ranking, $child, $entityIsRanked);
449
      if ($result === null || ($earliest !== null && $earliest < $result)) {
450
        $result = $earliest;
451
      }
452
    }
453
    return $result;
454
  }
455
456
  /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException
457
  /**
458
   * Recomputes the given ranking list by using base as base list and applying the changes for the given entities
459
   * starting from the given index. If list is not the current list only the entities up to $list->getLastEntryTime()
460
   * are applied and the index gets changed accordingly.
461
   * @param RankingSystemListInterface $list the list to recompute
462
   * @param RankingSystemListInterface $base the list to use as base
463
   * @param TournamentHierarchyEntity[] $entities the list of entities to use for the computation
464
   * @param int $nextEntityIndex the first index in the entities list to consider
465
   */
466
  private function recomputeBasedOn(RankingSystemListInterface $list, RankingSystemListInterface $base, array $entities,
467
                                    int &$nextEntityIndex)
468
  {
469
    $this->cloneInto($list, $base);
470
    for ($i = $nextEntityIndex; $i < count($entities); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
471
      $time = $this->timeService->getTime($entities[$i]);
472
      if (!$list->isCurrent() && $time > $list->getLastEntryTime()) {
473
        $nextEntityIndex = $i;
474
        return;
475
      }
476
      $changes = $this->getChanges($entities[$nextEntityIndex], $list);
477
      foreach ($changes as $change) {
478
        $entry = $this->getOrCreateRankingSystemListEntry($list, $change->getPlayer());
479
        $entry->setNumberRankedEntities($entry->getNumberRankedEntities() + 1);
480
        $pointsAfterwards = $entry->getPoints() + $change->getPointsChange();
481
        $entry->setPoints($pointsAfterwards);
482
        $change->setPointsAfterwards($pointsAfterwards);
483
        //apply further changes
484
        foreach ($this->getAdditionalFields() as $field => $value) {
485
          // PropertyNotExistingException => entry and field have exactly the static properties from getAdditionalFields
486
          /** @noinspection PhpUnhandledExceptionInspection */
487
          $entry->setProperty($field, $entry->getProperty($field) + $change->getProperty($field));
488
        }
489
        if ($time > $list->getLastEntryTime()) {
490
          $list->setLastEntryTime($time);
491
        }
492
        $this->entityManager->persist($change);
493
      }
494
    }
495
    $nextEntityIndex = count($entities);
496
  }
497
//</editor-fold desc="Private Methods">
498
}