Installation:
composer require avoo/elo-bundle
Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
return [
// ...
Avoo\EloBundle\AvooEloBundle::class,
Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class,
];
Configure Doctrine (config/packages/doctrine.yaml or config.yml):
doctrine:
orm:
mappings:
gedmo_loggable: ~
Create Entities:
Extend the base classes (EloPlayer, EloVersus) and implement EloUserInterface in your User entity.
First Use Case: Calculate Elo ratings after a match:
$match = $entityManager->getRepository(EloVersus::class)->find($matchId);
$calculator = $this->get('avoo_elo.calculator');
$calculator->calculate($match);
$aggregation = $calculator->getAggregation(); // Updated ratings
Match Creation:
$versus = new EloVersus();
$versus->setPlayerA($playerA);
$versus->setPlayerB($playerB);
$versus->setResultA(true); // Winner
$entityManager->persist($versus);
$entityManager->flush();
Batch Processing:
Use Doctrine’s BATCH_SIZE or queue workers (e.g., Symfony Messenger) to process multiple matches:
$matches = $entityManager->getRepository(EloVersus::class)->findBy(['processed' => false], null, 50);
foreach ($matches as $match) {
$calculator->calculate($match);
$match->setProcessed(true);
}
$entityManager->flush();
Integration with Events: Trigger Elo recalculations post-match via Doctrine lifecycle events:
// src/EventListener/EloListener.php
public function postPersist(LifecycleEventArgs $args)
{
$entity = $args->getObject();
if ($entity instanceof EloVersus && !$entity->isProcessed()) {
$calculator = $this->container->get('avoo_elo.calculator');
$calculator->calculate($entity);
}
}
Custom Aggregation: Extend the default aggregation logic (e.g., weighted averages for tournaments):
$calculator->setAggregationStrategy(new CustomEloAggregation());
Missing Dependencies:
Ensure stof/doctrine-extensions-bundle is installed (required for EloPlayer/EloVersus entities).
Circular References: Avoid bidirectional associations without proper cascade settings. Example:
// EloPlayer.php
@ORM\OneToOne(targetEntity="AppBundle\Entity\User", inversedBy="eloPlayer", cascade={"persist"})
Race Conditions:
Use database transactions or optimistic locking (@ORM\Version) when updating Elo ratings concurrently.
Initial Elo Values:
The bundle assumes initial Elo values are set in EloPlayer. Default to 1200 (standard chess rating) or fetch from a config:
# config/packages/avoo_elo.yaml
avoo_elo:
default_elo: 1500
Verify Calculations:
Log raw inputs/outputs of EloPoint calculations for validation:
$calculator->calculate($match);
$this->logger->info('Elo Update', [
'playerA' => $match->getPlayerA()->getElo(),
'playerB' => $match->getPlayerB()->getElo(),
'result' => $match->getResultA(),
]);
Check Entity States:
Use Doctrine’s getChanges() to debug unsaved Elo updates:
$player = $entityManager->getRepository(EloPlayer::class)->find($id);
$changes = $entityManager->getUnitOfWork()->getEntityChangeSet($player);
Custom K-Factor:
Override the default K=32 (for chess) in your EloPoint service:
services:
avoo_elo.calculator:
arguments:
$kFactor: 20 # Lower for stable ratings
Additional Match Metadata:
Extend EloVersus to store match context (e.g., tournamentId, surfaceType):
/**
* @ORM\Column(type="string", nullable=true)
*/
private $surface;
Asynchronous Processing:
Use Symfony’s Messenger to decouple Elo calculations from match creation:
$message = new CalculateEloMessage($matchId);
$this->messageBus->dispatch($message);
API Integration: Expose Elo ratings via API with serializers:
// src/Serializer/EloPlayerNormalizer.php
public function normalize($eloPlayer, $format = null, array $context = [])
{
return [
'id' => $eloPlayer->getId(),
'elo' => $eloPlayer->getElo(),
'rank' => $eloPlayer->getRank(), // Custom method
];
}
How can I help you explore Laravel packages today?