Completed
Push — master ( 21e5ae...265d55 )
by Benedikt
02:39
created

RankingSystemService::getEntitiesQueryBuilder()

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 2
c 0
b 0
f 0
nc 1
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\AutomaticInstanceGeneration;
17
use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity;
18
use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyInterface;
19
use Tfboe\FmLib\Entity\PlayerInterface;
20
use Tfboe\FmLib\Entity\RankingSystemChangeInterface;
21
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...
22
use Tfboe\FmLib\Entity\RankingSystemListEntryInterface;
23
use Tfboe\FmLib\Entity\RankingSystemListInterface;
24
use Tfboe\FmLib\Entity\TournamentInterface;
25
use Tfboe\FmLib\Exceptions\PreconditionFailedException;
26
use Tfboe\FmLib\Service\ObjectCreatorServiceInterface;
27
28
29
/**
30
 * Class RankingSystemService
31
 * @package Tfboe\FmLib\Service\RankingSystemService
32
 * @SuppressWarnings(PHPMD) TODO: refactor this class and remove suppress warnings
33
 */
34
abstract class RankingSystemService implements \Tfboe\FmLib\Service\RankingSystem\RankingSystemInterface
35
{
36
//<editor-fold desc="Fields">
37
  /** @var EntityManagerInterface */
38
  private $entityManager;
39
  /** @var TimeServiceInterface */
40
  private $timeService;
41
  /** @var EntityComparerInterface */
42
  private $entityComparer;
43
  /**
44
   * @var RankingSystemChangeInterface[][][]
45
   * first key: tournament hierarchy entity id
46
   * second key: ranking system id
47
   * third key: player id
48
   */
49
  private $changes;
50
  /**
51
   * @var RankingSystemChangeInterface[][][]
52
   * first key: tournament hierarchy entity id
53
   * second key: ranking system id
54
   * third key: player id
55
   */
56
  private $deletedChanges;
57
  /**
58
   * List of ranking systems for which update ranking got already called, indexed by id
59
   * @var RankingSystemService[]
60
   */
61
  private $updateRankingCalls;
62
63
  /** @var ObjectCreatorServiceInterface */
64
  private $objectCreatorService;
65
//</editor-fold desc="Fields">
66
67
//<editor-fold desc="Constructor">
68
  /**
69
   * RankingSystemService constructor.
70
   * @param EntityManagerInterface $entityManager
71
   * @param TimeServiceInterface $timeService
72
   * @param EntityComparerInterface $entityComparer
73
   * @param ObjectCreatorServiceInterface $objectCreatorService
74
   */
75
  public function __construct(EntityManagerInterface $entityManager, TimeServiceInterface $timeService,
76
                              EntityComparerInterface $entityComparer,
77
                              ObjectCreatorServiceInterface $objectCreatorService)
78
  {
79
    $this->entityManager = $entityManager;
80
    $this->timeService = $timeService;
81
    $this->entityComparer = $entityComparer;
82
    $this->changes = [];
83
    $this->deletedChanges = [];
84
    $this->updateRankingCalls = [];
85
    $this->objectCreatorService = $objectCreatorService;
86
  }
87
//</editor-fold desc="Constructor">
88
89
//<editor-fold desc="Public Methods">
90
  /**
91
   * @inheritDoc
92
   */
93
  public function getEarliestInfluence(RankingSystemInterface $ranking, TournamentInterface $tournament): ?\DateTime
94
  {
95
    return $this->getEarliestEntityInfluence($ranking, $tournament, false);
96
  }
97
98
  /**
99
   * @inheritdoc
100
   */
101
  public function updateRankingForTournament(RankingSystemInterface $ranking, TournamentInterface $tournament,
102
                                             ?\DateTime $oldInfluence)
103
  {
104
    $earliestInfluence = $this->getEarliestInfluence($ranking, $tournament);
105
    if ($oldInfluence !== null &&
106
      ($earliestInfluence === null || $oldInfluence < $earliestInfluence)) {
107
      $earliestInfluence = $oldInfluence;
108
    }
109
    if ($earliestInfluence !== null) {
110
      $this->updateRankingFrom($ranking, $earliestInfluence);
111
    }
112
  }
113
114
  /**
115
   * @inheritDoc
116
   */
117
  public function updateRankingFrom(RankingSystemInterface $ranking, \DateTime $from)
118
  {
119
    // can only be called once per ranking system!!!
120
    if (array_key_exists($ranking->getId(), $this->updateRankingCalls)) {
121
      throw new PreconditionFailedException();
122
    }
123
    $this->updateRankingCalls[$ranking->getId()] = $ranking;
124
    //find first reusable
125
    /** @var RankingSystemListInterface[] $lists */
126
    $lists = array_values($ranking->getLists()->toArray());
127
128
    $current = null;
129
    /** @var RankingSystemListInterface $lastReusable */
130
    $lastReusable = null;
131
    $toUpdate = [];
132
133
    foreach ($lists as $list) {
134
      if ($list->isCurrent()) {
135
        $current = $list;
136
      } else if ($list->getLastEntryTime() >= $from) {
137
        $toUpdate[] = $list;
138
      } else if ($lastReusable === null || $list->getLastEntryTime() > $lastReusable->getLastEntryTime()) {
139
        $lastReusable = $list;
140
      }
141
    }
142
143
    if ($current !== null && $current->getLastEntryTime() < $from) {
144
      $lastReusable = $current;
145
    }
146
147
    if ($lastReusable === null) {
148
      $lastReusable = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class);
149
    }
150
151
    usort($toUpdate, function (RankingSystemListInterface $list1, RankingSystemListInterface $list2) {
152
      return $list1->getLastEntryTime() <=> $list2->getLastEntryTime();
153
    });
154
155
    $entities = $this->getEntities($ranking, $lastReusable->getLastEntryTime());
156
    //sort entities
157
    $this->timeService->clearTimes();
158
    usort($entities, function ($entity1, $entity2) {
159
      return $this->entityComparer->compareEntities($entity1, $entity2);
160
    });
161
162
    $this->deleteOldChanges($ranking, $entities);
163
164
    $nextEntityIndex = 0;
165
    foreach ($toUpdate as $list) {
166
      $lastListTime = $lastReusable->getLastEntryTime();
167
      if (count($entities) > 0) {
168
        $lastListTime = max($lastListTime, $this->timeService->getTime($entities[0]));
169
      }
170
      $this->recomputeBasedOn($list, $lastReusable, $entities, $nextEntityIndex, $lastListTime);
171
      $lastReusable = $list;
172
    }
173
174
    $lastListTime = $lastReusable->getLastEntryTime();
175
    if (count($entities) > 0) {
176
      $lastListTime = max($lastListTime, $this->timeService->getTime($entities[0]));
177
    }
178
179
    if ($current === null) {
180
      /** @var RankingSystemListInterface $current */
181
      $current = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class);
182
      $current->setCurrent(true);
183
      $this->entityManager->persist($current);
184
      $current->setRankingSystem($ranking);
185
    }
186
    $this->recomputeBasedOn($current, $lastReusable, $entities, $nextEntityIndex, $lastListTime);
187
  }
188
//</editor-fold desc="Public Methods">
189
190
//<editor-fold desc="Protected Final Methods">
191
  /**
192
   * Computes the average rating of the given entries
193
   * @param RankingSystemListEntryInterface[] $entries
194
   * @return float
195
   */
196
  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...
197
  {
198
    $sum = 0.0;
199
    foreach ($entries as $entry) {
200
      $sum += $entry->getPoints();
201
    }
202
    if (count($entries) === 0) {
203
      return 0.0;
204
    } else {
205
      return $sum / count($entries);
206
    }
207
  }
208
209
  /**
210
   * Gets the relevant entities for updating
211
   * @param RankingSystemInterface $ranking the ranking for which to get the entities
212
   * @param \DateTime $from search for entities with a time value LARGER than $from, i.e. don't search for entities with
213
   *                        time value exactly $from
214
   * @return TournamentHierarchyEntity[]
215
   */
216
  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...
217
  {
218
    $query = $this->getEntitiesQueryBuilder($ranking, $from);
219
    return $query->getQuery()->getResult();
220
  }
221
222
  /**
223
   * @return EntityManagerInterface
224
   */
225
  protected final function getEntityManager(): EntityManagerInterface
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
226
  {
227
    return $this->entityManager;
228
  }
229
230
  /**
231
   * @param Collection|PlayerInterface[] $players
232
   * @param RankingSystemListInterface $list
233
   * @return RankingSystemListEntryInterface[] $entries
234
   */
235
  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...
236
  {
237
    $result = [];
238
    foreach ($players as $player) {
239
      $result[] = $this->getOrCreateRankingSystemListEntry($list, $player);
240
    }
241
    return $result;
242
  }
243
244
  /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException
245
  /**
246
   * Gets or creates a tournament system change entry for the given entity, ranking and player.
247
   * @param TournamentHierarchyInterface $entity the tournament hierarchy entity to search for
248
   * @param RankingSystemInterface $ranking the ranking system to search for
249
   * @param PlayerInterface $player the player to search for
250
   * @return RankingSystemChangeInterface the found or newly created ranking system change
251
   */
252
  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...
253
                                             PlayerInterface $player)
254
  {
255
    $key1 = $entity->getId();
256
    $key2 = $ranking->getId();
257
    $key3 = $player->getId();
258
    if (!array_key_exists($key1, $this->changes) || !array_key_exists($key2, $this->changes[$key1]) ||
259
      !array_key_exists($key3, $this->changes[$key1][$key2])) {
260
      /** @var RankingSystemChangeInterface[] $changes */
261
      $changes = $this->entityManager->getRepository(RankingSystemChangeInterface::class)->findBy(
262
        ['hierarchyEntity' => $entity]);
263
      $this->changes[$key1] = [];
264
      foreach ($changes as $change) {
265
266
        $newKey2 = $change->getRankingSystem()->getId();
267
        $newKey3 = $change->getPlayer()->getId();
268
        if (!array_key_exists($key1, $this->deletedChanges) || !array_key_exists($key2, $this->deletedChanges[$key1]) ||
269
          !array_key_exists($key3, $this->deletedChanges[$key1][$key2])) {
270
          if (!array_key_exists($newKey2, $this->changes)) {
271
            $this->changes[$key1][$newKey2] = [];
272
          }
273
          $this->changes[$key1][$newKey2][$newKey3] = $change;
274
        }
275
      }
276
    }
277
    if (!array_key_exists($key2, $this->changes[$key1]) || !array_key_exists($key3, $this->changes[$key1][$key2])) {
278
      //create new change
279
      /** @var RankingSystemChangeInterface $change */
280
      $change = $this->objectCreatorService->createObjectFromInterface(RankingSystemChangeInterface::class,
281
        [array_keys($this->getAdditionalFields())]);
282
      foreach ($this->getAdditionalFields() as $field => $value) {
283
        // PropertyNotExistingException => we know for sure that the property exists (see 2 lines above)
284
        /** @noinspection PhpUnhandledExceptionInspection */
285
        $change->setProperty($field, 0);
286
      }
287
      $change->setHierarchyEntity($entity);
288
      $change->setRankingSystem($ranking);
289
      $change->setPlayer($player);
290
      $this->entityManager->persist($change);
291
      $this->changes[$key1][$key2][$key3] = $change;
292
      if (array_key_exists($key1, $this->deletedChanges) && array_key_exists($key2, $this->deletedChanges[$key1]) &&
293
        array_key_exists($key3, $this->deletedChanges[$key1][$key2])) {
294
        unset($this->deletedChanges[$key1][$key2][$key3]);
295
      }
296
    }
297
    return $this->changes[$key1][$key2][$key3];
298
  }
299
300
  /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException
301
  /**
302
   * @param RankingSystemListInterface $list the list in which to search for the entry or in which to add it
303
   * @param PlayerInterface $player the player to search for
304
   * @return RankingSystemListEntryInterface the found or the new entry
305
   */
306
  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...
307
                                                             PlayerInterface $player): RankingSystemListEntryInterface
308
  {
309
    $playerId = $player->getId();
310
    if (!$list->getEntries()->containsKey($playerId)) {
311
      /** @var RankingSystemListEntryInterface $entry */
312
      $entry = $this->objectCreatorService->createObjectFromInterface(RankingSystemListEntryInterface::class,
313
        [array_keys($this->getAdditionalFields())]);
314
      $entry->setPoints($this->startPoints());
315
      $entry->setPlayer($player);
316
      $entry->setRankingSystemList($list);
317
      foreach ($this->getAdditionalFields() as $field => $value) {
318
        // PropertyNotExistingException => we know for sure that the property exists (see 2 lines above)
319
        /** @noinspection PhpUnhandledExceptionInspection */
320
        $entry->setProperty($field, $value);
321
      }
322
      $this->entityManager->persist($entry);
323
    }
324
    return $list->getEntries()->get($playerId);
325
  }
326
//</editor-fold desc="Protected Final Methods">
327
328
//<editor-fold desc="Protected Methods">
329
  /**
330
   * Gets additional fields for this ranking type mapped to its start value
331
   * @return string[] list of additional fields
332
   */
333
  protected abstract function getAdditionalFields(): array;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
334
335
  /**
336
   * Gets all ranking changes for the given entity for the given list. Must return a change for each involved player.
337
   * The field pointsAfterwards get calculated afterwards and can be left empty.
338
   * @param TournamentHierarchyEntity $entity the entity for which to compute the ranking changes
339
   * @param RankingSystemListInterface $list the list for which to compute the ranking changes
340
   * @return RankingSystemChangeInterface[] the changes
341
   */
342
  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...
343
344
  /**
345
   * Gets a query for getting the relevant entities for updating
346
   * @param RankingSystemInterface $ranking the ranking for which to get the entities
347
   * @param \DateTime $from search for entities with a time value LARGER than $from, i.e. don't search for entities with
348
   *                        time value exactly $from
349
   * @return QueryBuilder
350
   */
351
  protected abstract function getEntitiesQueryBuilder(RankingSystemInterface $ranking,
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
352
                                                      \DateTime $from): QueryBuilder;
353
354
  /**
355
   * Gets the level of the ranking system service (see Level Enum)
356
   * @return int
357
   */
358
  protected abstract function getLevel(): int;
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
359
360
  /**
361
   * Gets the start points for a new player in the ranking
362
   * @return float
363
   */
364
  protected function startPoints(): float
365
  {
366
    return 0.0;
367
  }
368
//</editor-fold desc="Protected Methods">
369
370
//<editor-fold desc="Private Methods">
371
  /**
372
   * Clones all ranking values from base and inserts them into list, furthermore removes all remaining ranking values of
373
   * list. After this method was called list and base contain exactly the same rankings.
374
   * @param RankingSystemListInterface $list the ranking list to change
375
   * @param RankingSystemListInterface $base the ranking list to use as base list, this doesn't get changed
376
   */
377
  private function cloneInto(RankingSystemListInterface $list, RankingSystemListInterface $base)
378
  {
379
    /*//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...
380
    foreach($list->getEntries()->toArray() as $entry)
381
    {
382
      $list->getEntries()->removeElement($entry);
383
      $this->entityManager->remove($entry);
384
    }*/
385
386
    $clonedPlayers = [];
387
388
    foreach ($base->getEntries() as $entry) {
389
      $playerId = $entry->getPlayer()->getId();
390
      $clonedPlayers[$playerId] = true;
391
      if (!$list->getEntries()->containsKey($playerId)) {
392
        //create new entry
393
        /** @var RankingSystemListEntryInterface $entry */
394
        $clone = $this->objectCreatorService->createObjectFromInterface(RankingSystemListEntryInterface::class,
395
          [[]]);
396
        $clone->cloneSubClassDataFrom($entry);
397
        $this->entityManager->persist($clone);
398
        $clone->setPlayer($entry->getPlayer());
399
        $clone->setRankingSystemList($list);
400
      }
401
      $foundEntry = $list->getEntries()[$playerId];
402
      $foundEntry->setNumberRankedEntities($entry->getNumberRankedEntities());
403
      $foundEntry->setPoints($entry->getPoints());
404
    }
405
406
    //remove all unused entries from list
407
    foreach ($list->getEntries()->toArray() as $playerId => $entry) {
408
      if (!array_key_exists($playerId, $clonedPlayers)) {
409
        $list->getEntries()->removeElement($entry);
410
        $this->entityManager->remove($entry);
411
      }
412
    }
413
  }
414
415
416
  /**
417
   * @param RankingSystemInterface $ranking
418
   * @param TournamentHierarchyEntity[] $entities
419
   */
420
  private function deleteOldChanges(RankingSystemInterface $ranking, array $entities)
421
  {
422
    //delete old changes
423
    $queryBuilder = $this->entityManager->createQueryBuilder();
424
    /** @var RankingSystemChangeInterface[] $changes */
425
    $changes = $queryBuilder
426
      ->from(RankingSystemChangeInterface::class, 'c')
427
      ->select('c')
428
      ->where($queryBuilder->expr()->eq('c.rankingSystem', $ranking))
429
      ->where($queryBuilder->expr()->in('c.hierarchyEntity', ':entities'))
430
      ->setParameter('entities', $entities)
431
      ->getQuery()->getResult();
432
    foreach ($changes as $change) {
433
      $this->deletedChanges[$change->getHierarchyEntity()->getId()][$ranking
434
        ->getId()][$change->getPlayer()->getId()] = $change;
435
      $this->entityManager->remove($change);
436
    }
437
  }
438
439
  /**
440
   * Gets the earliest influence for the given entity
441
   * @param RankingSystemInterface $ranking the ranking system for which to get the influence
442
   * @param TournamentHierarchyInterface $entity the entity to analyze
443
   * @param bool $parentIsRanked true iff a predecessor contained the given ranking in its ranking systems
444
   * @return \DateTime|null the earliest influence or null if $parentIsRanked is false and the entity and all its
445
   *                        successors do not have the ranking in its ranking systems
446
   */
447
  private function getEarliestEntityInfluence(RankingSystemInterface $ranking, TournamentHierarchyInterface $entity,
448
                                              bool $parentIsRanked): ?\DateTime
449
  {
450
    $this->timeService->clearTimes();
451
    $entityIsRanked = $parentIsRanked || $entity->getRankingSystems()->containsKey($ranking->getId());
452
    if ($entity->getLevel() === $this->getLevel()) {
453
      if ($entityIsRanked) {
454
        return $this->timeService->getTime($entity);
455
      } else {
456
        return null;
457
      }
458
    }
459
    $result = null;
460
461
    foreach ($entity->getChildren() as $child) {
462
      $earliest = $this->getEarliestEntityInfluence($ranking, $child, $entityIsRanked);
463
      if ($result === null || ($earliest !== null && $earliest < $result)) {
464
        $result = $earliest;
465
      }
466
    }
467
    return $result;
468
  }
469
470
  /**
471
   * @param \DateTime $time the time of the last list
472
   * @param int $generationLevel the list generation level
473
   * @return \DateTime the time of the next list generation
474
   */
475
  private function getNextGenerationTime(\DateTime $time, int $generationLevel): \DateTime
476
  {
477
    $year = (int)$time->format('Y');
478
    $month = (int)$time->format('m');
479
    if ($generationLevel === AutomaticInstanceGeneration::MONTHLY) {
480
      $month += 1;
481
      if ($month == 13) {
482
        $month = 1;
483
        $year += 1;
484
      }
485
    } else if ($generationLevel === AutomaticInstanceGeneration::OFF) {
486
      return (new \DateTime())->add(new \DateInterval('P100Y'));
487
    } else {
488
      $year += 1;
489
    }
490
    return (new \DateTime())->setDate($year, $month, 1)->setTime(0, 0, 0);
491
  }
492
493
  /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException
494
  /**
495
   * Recomputes the given ranking list by using base as base list and applying the changes for the given entities
496
   * starting from the given index. If list is not the current list only the entities up to $list->getLastEntryTime()
497
   * are applied and the index gets changed accordingly.
498
   * @param RankingSystemListInterface $list the list to recompute
499
   * @param RankingSystemListInterface $base the list to use as base
500
   * @param TournamentHierarchyEntity[] $entities the list of entities to use for the computation
501
   * @param int $nextEntityIndex the first index in the entities list to consider
502
   * @param \DateTime $lastListTime the time of the last list or the first entry
503
   */
504
  private function recomputeBasedOn(RankingSystemListInterface $list, RankingSystemListInterface $base, array $entities,
505
                                    int &$nextEntityIndex, \DateTime $lastListTime)
506
  {
507
    $nextGeneration = $this->getNextGenerationTime($lastListTime, $list->getRankingSystem()->getGenerationInterval());
508
    $this->cloneInto($list, $base);
509
    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...
510
      $time = $this->timeService->getTime($entities[$i]);
511
      if (!$list->isCurrent() && $time > $list->getLastEntryTime()) {
512
        $nextEntityIndex = $i;
513
        return;
514
      }
515
      if ($nextGeneration < $time) {
516
        /** @var RankingSystemListInterface $newList */
517
        $newList = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class);
518
        $newList->setCurrent(false);
519
        $newList->setLastEntryTime($nextGeneration);
520
        $this->entityManager->persist($newList);
521
        $newList->setRankingSystem($list->getRankingSystem());
522
        $this->cloneInto($newList, $list);
523
        $nextGeneration = $this->getNextGenerationTime($nextGeneration,
524
          $list->getRankingSystem()->getGenerationInterval());
525
      }
526
      $changes = $this->getChanges($entities[$i], $list);
527
      foreach ($changes as $change) {
528
        $entry = $this->getOrCreateRankingSystemListEntry($list, $change->getPlayer());
529
        $entry->setNumberRankedEntities($entry->getNumberRankedEntities() + 1);
530
        $pointsAfterwards = $entry->getPoints() + $change->getPointsChange();
531
        $entry->setPoints($pointsAfterwards);
532
        $change->setPointsAfterwards($pointsAfterwards);
533
        //apply further changes
534
        foreach ($this->getAdditionalFields() as $field => $value) {
535
          // PropertyNotExistingException => entry and field have exactly the static properties from getAdditionalFields
536
          /** @noinspection PhpUnhandledExceptionInspection */
537
          $entry->setProperty($field, $entry->getProperty($field) + $change->getProperty($field));
538
        }
539
        if ($time > $list->getLastEntryTime()) {
540
          $list->setLastEntryTime($time);
541
        }
542
        $this->entityManager->persist($change);
543
      }
544
    }
545
    $nextEntityIndex = count($entities);
546
  }
547
//</editor-fold desc="Private Methods">
548
}