<repo_name>aurelien-w/games<file_sep>/src/Entity/Season.php
<?php

namespace App\Entity;

use Carbon\Carbon;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\SeasonRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Season
{
    const STATUS_WAITING     = 0;
    const STATUS_IN_PROGRESS = 1;
    const STATUS_COMPLETED   = 2;

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint")
     */
    private $status;

    /**
     * @var \DateTime
     * 
     * @ORM\Column(type="datetime")
     */
    private $startAt;

    /**
     * @var \DateTime
     * 
     * @ORM\Column(type="datetime")
     */
    private $finishAt;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="App\Entity\PlayerSeasonData", mappedBy="season")
     * @ORM\OrderBy({"rank" = "DESC"})
     */
    private $playerSeasonData;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="App\Entity\SeasonDuel", mappedBy="season")
     */
    private $duels;

    /**
     * @var \DateTime
     * 
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    /**
     * @var \DateTime
     * 
     * @ORM\Column(type="datetime")
     */
    private $updatedAt;

    public function __construct()
    {
        $this->playerSeasonData = new ArrayCollection();
        $this->duels = new ArrayCollection();
    }

    public function isCurrentMonthSeason(): bool
    {
        return (new \DateTime())->format('m') === $this->startAt->format('m');
    }

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id): void
    {
        $this->id = $id;
    }

    /**
     * @return int
     */
    public function getStatus(): int
    {
        return $this->status;
    }

    /**
     * @param int $status
     */
    public function setStatus(int $status): void
    {
        $this->status = $status;
    }

    /**
     * @return \DateTime
     */
    public function getStartAt(): \DateTime
    {
        return $this->startAt;
    }

    /**
     * @param \DateTime $startAt
     */
    public function setStartAt(\DateTime $startAt): void
    {
        $this->startAt = $startAt;
    }

    /**
     * @return \DateTime
     */
    public function getFinishAt(): \DateTime
    {
        return $this->finishAt;
    }

    /**
     * @param \DateTime $finishAt
     */
    public function setFinishAt(\DateTime $finishAt): void
    {
        $this->finishAt = $finishAt;
    }

    /**
     * @return ArrayCollection
     */
    public function getPlayerSeasonData()
    {
        return $this->playerSeasonData;
    }

    /**
     * @param ArrayCollection $playerSeasonData
     */
    public function setPlayerSeasonData(ArrayCollection $playerSeasonData): void
    {
        $this->playerSeasonData = $playerSeasonData;
    }

    /**
     * @param Player $player
     * @return bool
     */
    public function hasPlayer(Player $player): bool
    {
        foreach ($this->getPlayerSeasonData() as $seasonDatum) {
            if ($seasonDatum->getPlayer()->getId() === $player->getId()) {
                return true;
            }
        }

        return false;
    }

    /**
     * @return int
     */
    public function getNbPlayers(): int
    {
        return $this->playerSeasonData->count();
    }

    /**
     * @return int
     */
    public function getNbMissingPlayerToStart()
    {
        $diff = 2 - $this->playerSeasonData->count();

        if ($diff < 0) {
            $diff = 0;
        }

        return $diff;
    }

    /**
     * @return ArrayCollection
     */
    public function getDuels()
    {
        return $this->duels;
    }

    /**
     * @param ArrayCollection $duels
     */
    public function setDuels(ArrayCollection $duels): void
    {
        $this->duels = $duels;
    }

    /**
     * @return \DateTime
     */
    public function getCreatedAt(): \DateTime
    {
        return $this->createdAt;
    }

    /**
     * @param \DateTime $createdAt
     */
    public function setCreatedAt(\DateTime $createdAt): void
    {
        $this->createdAt = $createdAt;
    }

    /**
     * @return \DateTime
     */
    public function getUpdatedAt(): \DateTime
    {
        return $this->updatedAt;
    }

    /**
     * @param \DateTime $updatedAt
     */
    public function setUpdatedAt(\DateTime $updatedAt): void
    {
        $this->updatedAt = $updatedAt;
    }

    /**
     * @ORM\PrePersist()
     */
    public function prePersist()
    {
        $this->status = self::STATUS_WAITING;
        $this->setCreatedAt(new \DateTime());
        $this->setUpdatedAt(new \DateTime());
    }

    /**
     * @ORM\PreUpdate()
     */
    public function preUpdate()
    {
        $this->setUpdatedAt(new \DateTime());
    }
}
<file_sep>/src/Entity/PlayerSeasonData.php
<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\PlayerSeasonDataRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class PlayerSeasonData
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(type="integer")
     */
    private $rank = 0;

    /**
     * @var Player
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\Player", inversedBy="playerSeasonData")
     */
    private $player;

    /**
     * @var Season
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\Season", inversedBy="playerSeasonData")
     */
    private $season;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="App\Entity\SeasonDuel", mappedBy="playerSeasonDataA")
     */
    private $duelsA;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="App\Entity\SeasonDuel", mappedBy="playerSeasonDataB")
     */
    private $duelsB;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint")
     */
    private $duelPlayed;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint")
     */
    private $duelWin;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint")
     */
    private $duelNull;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint")
     */
    private $duelLoose;

    /**
     * PlayerSeasonData constructor.
     */
    public function __construct()
    {
        $this->duelsA = new ArrayCollection();
        $this->duelsB = new ArrayCollection();
    }

    /**
     * @ORM\PrePersist()
     */
    public function prePersist()
    {
        $this->rank       = 0;
        $this->duelPlayed = 0;
        $this->duelWin    = 0;
        $this->duelNull   = 0;
        $this->duelLoose  = 0;
    }

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id): void
    {
        $this->id = $id;
    }

    /**
     * @return int
     */
    public function getRank(): int
    {
        return $this->rank;
    }

    /**
     * @param int $rank
     */
    public function setRank(int $rank): void
    {
        $this->rank = $rank;
    }

    /**
     * @return Player
     */
    public function getPlayer(): Player
    {
        return $this->player;
    }

    /**
     * @param Player $player
     */
    public function setPlayer(Player $player): void
    {
        $this->player = $player;
    }

    /**
     * @return Season
     */
    public function getSeason(): Season
    {
        return $this->season;
    }

    /**
     * @param Season $season
     */
    public function setSeason(Season $season): void
    {
        $this->season = $season;
    }

    /**
     * @return ArrayCollection
     */
    public function getDuelsA(): ArrayCollection
    {
        return $this->duelsA;
    }

    /**
     * @param ArrayCollection $duelsA
     */
    public function setDuelsA(ArrayCollection $duelsA): void
    {
        $this->duelsA = $duelsA;
    }

    /**
     * @return ArrayCollection
     */
    public function getDuelsB(): ArrayCollection
    {
        return $this->duelsB;
    }

    /**
     * @param ArrayCollection $duelsB
     */
    public function setDuelsB(ArrayCollection $duelsB): void
    {
        $this->duelsB = $duelsB;
    }

    /**
     * @return int
     */
    public function getDuelPlayed(): int
    {
        return $this->duelPlayed;
    }

    /**
     * @param int $duelPlayed
     */
    public function setDuelPlayed(int $duelPlayed): void
    {
        $this->duelPlayed = $duelPlayed;
    }

    /**
     * @return int
     */
    public function getDuelWin(): int
    {
        return $this->duelWin;
    }

    /**
     * @param int $duelWin
     */
    public function setDuelWin(int $duelWin): void
    {
        $this->duelWin = $duelWin;
    }

    /**
     * @return int
     */
    public function getDuelNull(): int
    {
        return $this->duelNull;
    }

    /**
     * @param int $duelNull
     */
    public function setDuelNull(int $duelNull): void
    {
        $this->duelNull = $duelNull;
    }

    /**
     * @return int
     */
    public function getDuelLoose(): int
    {
        return $this->duelLoose;
    }

    /**
     * @param int $duelLoose
     */
    public function setDuelLoose(int $duelLoose): void
    {
        $this->duelLoose = $duelLoose;
    }
}
<file_sep>/src/Entity/Player.php
<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\PlayerRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Player
{
    public function __construct()
    {
        $this->duelsA           = new ArrayCollection();
        $this->duelsB           = new ArrayCollection();
        $this->playerSeasonData = new ArrayCollection();
    }

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\Column(type="integer")
     */
    private $rank = 1000;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Duel", mappedBy="playerA")
     */
    private $duelsA;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Duel", mappedBy="playerB")
     */
    private $duelsB;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="App\Entity\PlayerSeasonData", mappedBy="player")
     */
    private $playerSeasonData;

    /**
     * @ORM\Column(type="datetime")
     */
    private $updated_at;

    /**
     * @ORM\Column(type="datetime")
     */
    private $created_at;

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param mixed $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getRank()
    {
        return $this->rank;
    }

    /**
     * @param mixed $rank
     */
    public function setRank($rank)
    {
        $this->rank = $rank;
    }

    /**
     * @return mixed
     */
    public function getDuelsA()
    {
        return $this->duelsA;
    }

    /**
     * @param mixed $duelsA
     */
    public function setDuels($duelsA)
    {
        $this->duelsA = $duelsA;
    }

    /**
     * @param Duel $duel
     */
    public function addDuelA(Duel $duel)
    {
        if (!$this->duelsA->contains($duel)) {
            $this->duelsA[] = $duel;
        }
    }

    /**
     * @return mixed
     */
    public function getDuelsB()
    {
        return $this->duelsB;
    }

    /**
     * @param mixed $duelB
     */
    public function setDuelsB($duelB)
    {
        $this->duelsB = $duelB;
    }

    /**
     * @param Duel $duel
     */
    public function addDuelB(Duel $duel)
    {
        if (!$this->duelsB->contains($duel)) {
            $this->duelsB[] = $duel;
        }
    }

    /**
     * @return Duel[]
     */
    public function getDuels()
    {
        return array_merge($this->getDuelsA()->toArray(), $this->getDuelsB()->toArray());
    }

    /**
     * @return array
     */
    public function getDuelsWin()
    {
        $duels = $this->getDuels();
        $duelsWin = [];

        foreach ($duels as $duel) {
            if ($duel->getPlayerA()->getId() === $this->getId() && $duel->getScoreA() > $duel->getScoreB()) {
                $duelsWin[] = $duel;
            } elseif ($duel->getPlayerB()->getId() === $this->getId() && $duel->getScoreB() > $duel->getScoreA()) {
                $duelsWin[] = $duel;
            }
        }

        return $duelsWin;
    }

    /**
     * @return array
     */
    public function getDuelsLose()
    {
        $duels = $this->getDuels();
        $duelsLose = [];

        foreach ($duels as $duel) {
            if ($duel->getPlayerA()->getId() === $this->getId() && $duel->getScoreA() < $duel->getScoreB()) {
                $duelsLose[] = $duel;
            } elseif ($duel->getPlayerB()->getId() === $this->getId() && $duel->getScoreB() < $duel->getScoreA()) {
                $duelsLose[] = $duel;
            }
        }

        return $duelsLose;
    }

    /**
     * @return array
     */
    public function getDuelsEquality()
    {
        $duels = $this->getDuels();
        $duelsEquality = [];

        foreach ($duels as $duel) {
            if ($duel->getScoreA() === $duel->getScoreB()) {
                $duelsEquality[] = $duel;
            }
        }

        return $duelsEquality;
    }

    /**
     * @return ArrayCollection
     */
    public function getPlayerSeasonData(): ArrayCollection
    {
        return $this->playerSeasonData;
    }

    /**
     * @param ArrayCollection $playerSeasonData
     */
    public function setPlayerSeasonData(ArrayCollection $playerSeasonData): void
    {
        $this->playerSeasonData = $playerSeasonData;
    }

    /**
     * @return mixed
     */
    public function getUpdatedAt()
    {
        return $this->updated_at;
    }

    /**
     * @param mixed $updated_at
     */
    public function setUpdatedAt($updated_at)
    {
        $this->updated_at = $updated_at;
    }

    /**
     * @return mixed
     */
    public function getCreatedAt()
    {
        return $this->created_at;
    }

    /**
     * @param mixed $created_at
     */
    public function setCreatedAt($created_at)
    {
        $this->created_at = $created_at;
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function updatedTimestamps()
    {
        $this->setUpdatedAt(new \DateTime('now'));

        if ($this->getCreatedAt() == null) {
            $this->setCreatedAt(new \DateTime('now'));
        }
    }
}
<file_sep>/src/Service/Ranking.php
<?php

namespace App\Service;

class Ranking
{
    /**
     * @var int The K Factor used.
     */
    const KFACTOR = 50;

    const WIN = 1;
    const DRAW = 0.5;
    const LOST = 0;

    /**
     * Protected & private variables.
     */
    private $_ratingA;
    private $_ratingB;

    private $_scoreA;
    private $_scoreB;

    private $_expectedA;
    private $_expectedB;

    private $_newRatingA;
    private $_newRatingB;

    /**
     * Set new input data.
     *
     * @param int $ratingA Current rating of A
     * @param int $ratingB Current rating of B
     * @param int $scoreA Score of A
     * @param int $scoreB Score of B
     * @return array
     */
    public function setNewSettings($ratingA, $ratingB, $scoreA, $scoreB)
    {
        $this->_ratingA = $ratingA;
        $this->_ratingB = $ratingB;
        $this->_scoreA = $scoreA;
        $this->_scoreB = $scoreB;

        $expectedScores = $this->getExpectedScores($this->_ratingA, $this->_ratingB);
        $this->_expectedA = $expectedScores['a'];
        $this->_expectedB = $expectedScores['b'];

        $newRatings = $this->getNewRatings($this->_ratingA, $this->_ratingB, $this->_expectedA, $this->_expectedB, $this->_scoreA, $this->_scoreB);
        $this->_newRatingA = $newRatings['a'];
        $this->_newRatingB = $newRatings['b'];

        return array (
            'ratingA' => $this->_newRatingA,
            'diffA' => ($this->_newRatingA - $this->_ratingA),
            'ratingB' => $this->_newRatingB,
            'diffB' => ($this->_newRatingB - $this->_ratingB)
        );
    }

    /**
     * @param int $ratingA The Rating of Player A
     * @param int $ratingB The Rating of Player B
     * @return array
     */
    private function getExpectedScores($ratingA, $ratingB)
    {
        $expectedScoreA = 1 / (1 + ( pow( 10 , ($ratingB - $ratingA) / 400)));
        $expectedScoreB = 1 / (1 + ( pow( 10 , ($ratingA - $ratingB) / 400)));

        return array (
            'a' => $expectedScoreA,
            'b' => $expectedScoreB
        );
    }

    /**
     * @param int $ratingA The Rating of Player A
     * @param int $ratingB The Rating of Player A
     * @param int $expectedA The expected score of Player A
     * @param int $expectedB The expected score of Player B
     * @param int $scoreA The score of Player A
     * @param int $scoreB The score of Player B
     * @return array
     */
    private function getNewRatings($ratingA, $ratingB, $expectedA, $expectedB, $scoreA, $scoreB)
    {
        $newRatingA = $ratingA + (self::KFACTOR * ($scoreA - $expectedA));
        $newRatingB = $ratingB + (self::KFACTOR * ($scoreB - $expectedB));

        return array (
            'a' => $newRatingA,
            'b' => $newRatingB
        );
    }
}<file_sep>/webpack.config.js
var Encore = require('@symfony/webpack-encore');

Encore
    // the project directory where compiled assets will be stored
    .setOutputPath('public/build/')

    // the public path used by the web server to access the previous directory
    .setPublicPath('/build')

    // will create public/build/main.js and public/build/main.css
    .addEntry('app', './assets/js/app.js')

    .enableVueLoader()

    // this creates a 'vendor.js' file with jquery and the bootstrap JS module
    // these modules will *not* be included in page1.js or page2.js anymore
    .createSharedEntry('vendor', [
        'jquery',
        'bootstrap',
        'bootstrap/scss/bootstrap.scss'
    ])

    // allow sass/scss files to be processed
    .enableSassLoader(function(sassOptions) {}, {
        resolveUrlLoader: false
    })

    // allow legacy applications to use $/jQuery as a global variable
    .autoProvidejQuery()

    .enableSourceMaps(!Encore.isProduction())

    // empty the outputPath dir before each build
    .cleanupOutputBeforeBuild()

    // show OS notifications when builds finish/fail
    .enableBuildNotifications()

    // create hashed filenames (e.g. app.abc123.css)
    .enableVersioning()
;

module.exports = Encore.getWebpackConfig();
<file_sep>/src/Controller/DefaultController.php
<?php

namespace App\Controller;

use App\Entity\Duel;
use App\Entity\Player;
use App\Form\DuelType;
use App\Service\Ranking;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="index")
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function index(Request $request)
    {
        $players = $this->getDoctrine()->getRepository(Player::class)->findBy([], ['rank' => 'DESC']);
        $duels = $this->getDoctrine()->getRepository(Duel::class)->findBy([], ['created_at' => 'DESC'], 10);

        $duel = new Duel();
        $formDuel = $this->createForm(DuelType::class, $duel);
        $formDuel->handleRequest($request);

        if ($formDuel->isSubmitted() && $formDuel->isValid()) {
            $scoreA = Ranking::DRAW;
            $scoreB = Ranking::DRAW;

            if ($duel->getScoreA() > $duel->getScoreB()) {
                $scoreA = Ranking::WIN;
                $scoreB = Ranking::LOST;
            } elseif ($duel->getScoreB() > $duel->getScoreA()) {
                $scoreA = Ranking::LOST;
                $scoreB = Ranking::WIN;
            }

            // Ranking
            $ranking = $this->get('App\Service\Ranking')->setNewSettings(
                $duel->getPlayerA()->getRank(),
                $duel->getPlayerB()->getRank(),
                $scoreA,
                $scoreB
            );
            $duel->getPlayerA()->setRank($ranking['ratingA']);
            $duel->setRankA($ranking['diffA']);
            $duel->getPlayerB()->setRank($ranking['ratingB']);
            $duel->setRankB($ranking['diffB']);

            // Save
            $em = $this->getDoctrine()->getManager();
            $em->persist($duel);
            $em->flush();

            // Prevent refresh
            return $this->redirectToRoute('index');
        }

        return $this->render('default/index.html.twig', [
            'players' => $players,
            'duels' => $duels,
            'formDuel' => $formDuel->createView()
        ]);
    }
}<file_sep>/src/Repository/DuelRepository.php
<?php

namespace App\Repository;

use App\Entity\Duel;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;

class DuelRepository extends ServiceEntityRepository
{
    /**
     * DuelRepository constructor.
     * @param RegistryInterface $registry
     */
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, Duel::class);
    }
}
<file_sep>/src/Controller/SeasonController.php
<?php

namespace App\Controller;


use App\Entity\PlayerSeasonData;
use App\Entity\Season;
use App\Entity\SeasonDuel;
use Carbon\Carbon;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * Fuck PSR-2 ! ;)
 * 
 * @package 
 */
class SeasonController extends Controller {

    /**
     * @Route("/saisons", name="season_index")
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function index()
    {
        return $this->render('season/index.html.twig', [
            'seasons'        => $this->getDoctrine()->getRepository('App:Season')->findBy([], ['startAt' => 'DESC']),
            'current_season' => $this->getDoctrine()->getRepository('App:Season')->findOneBy(['status' => Season::STATUS_IN_PROGRESS]),
            'players'        => $this->getDoctrine()->getRepository('App:Player')->findAll()
        ]);
    }

    /**
     * @Route("/saisons/{id}", requirements={ "id" : "\d+" }, name="season_show")
     *
     * @param $id
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function show($id)
    {
        return $this->render('season/show.html.twig', [
            'current_season' => $this->getDoctrine()->getRepository('App:Season')->find($id)
        ]);
    }

    /**
     * @Route("/saisons/nouveau", name="season_create")
     *
     * @param TranslatorInterface $translator
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function create(TranslatorInterface $translator)
    {
        $em = $this->getDoctrine()->getManager();

        $lastCreatedSeason = $em->getRepository('App:Season')->findOneBy([], ['startAt' => 'DESC']);
        $today = Carbon::now();

        if (
            null === $lastCreatedSeason ||
            $today->greaterThan($lastCreatedSeason->getFinishAt()) ||
            $today->format('m') === $lastCreatedSeason->getStartAt()->format('m')
        )
        {
            $season = new Season();

            $start = $today;

            if (
                null !== $lastCreatedSeason &&
                $today->format('m') === $lastCreatedSeason->getStartAt()->format('m')
            )
            {
                $start->addMonth();
            }

            $start->day = 1;
            $start->setTime(0,0, 0, 0);
            $season->setStartAt($start);

            $finish = clone $start;
            $finish->endOfMonth();
            $finish->setTime(23,59, 59);
            $season->setFinishAt($finish);

            $em->persist($season);
            $em->flush();

            $this->addFlash('success', "La saison du mois de ".$translator->trans($start->format('F'))." a été ajoutée.");
        } else {
            $this->addFlash('danger', "Vous ne pouvez pas ajouter d'autres saisons pour le moment.");
        }

        return $this->redirectToRoute('season_index');
    }

    /**
     * @Route("/saison/inscription", name="season_player_register")
     *
     * @param Request             $request
     * @param TranslatorInterface $translator
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function registerPlayer(Request $request, TranslatorInterface $translator)
    {
        $em = $this->getDoctrine()->getManager();

        $season = $em->getRepository('App:Season')->find($request->query->get('season'));
        $player = $em->getRepository('App:Player')->find($request->query->get('player'));

        if (!$season->hasPlayer($player)) {
            $playerSeasonData = new PlayerSeasonData();
            $playerSeasonData->setSeason($season);
            $playerSeasonData->setPlayer($player);
            $em->persist($playerSeasonData);

            foreach ($season->getPlayerSeasonData() as $seasonDatum) {
                for ($i = 0; $i < 2; $i++) {
                    $seasonDuel = new SeasonDuel();
                    $seasonDuel->setSeason($season);

                    if ($i % 2 === 0) {
                        $seasonDuel->setPlayerSeasonDataA($playerSeasonData);
                        $seasonDuel->setPlayerSeasonDataB($seasonDatum);
                    } else {
                        $seasonDuel->setPlayerSeasonDataA($seasonDatum);
                        $seasonDuel->setPlayerSeasonDataB($playerSeasonData);
                    }

                    $em->persist($seasonDuel);
                }
            }

            $em->flush();

            $this->addFlash('success', 'Vous êtes maintenant inscrit à la saison : ' . $translator->trans($season->getStartAt()->format('F')));
        } else {
            $this->addFlash('danger', 'Vous êtes déjà inscrit à cette saison.');
        }


        return $this->redirectToRoute('season_index');
    }

    /**
     * @Route("/saisons/commencer/{id}", requirements={ "id" : "\d+" }, name="season_start")
     *
     * @param TranslatorInterface $translator
     * @param                     $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function startSeason(TranslatorInterface $translator, $id)
    {
        $em = $this->getDoctrine()->getManager();

        $season = $em->getRepository('App:Season')->find($id);

        $today = new \DateTime();

        if ($today->format('m') === $season->getStartAt()->format('m')) {
            $season->setStatus(Season::STATUS_IN_PROGRESS);
            $em->persist($season);
            $em->flush();
            $this->addFlash('success', 'La saison ' . $translator->trans($season->getStartAt()->format('F')) . ' a commencé.');
        } else {
            $this->addFlash('danger', 'Cette saison ne peut pas commencer.');
        }

        return $this->redirectToRoute('season_index');
    }

    /**
     * @Route("/saisons/matches/score/{id}", requirements={ "id" : "\d+" }, name="season_save_duel_score")
     *
     * @param Request $request
     * @param         $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function saveScore(Request $request, $id)
    {
        if ("" !== $request->query->get('score-a') && "" !== $request->query->get('score-b')) {
            $em = $this->getDoctrine()->getManager();

            $duel = $em->getRepository('App:SeasonDuel')->find($id);

            $duel->setScoreA($request->query->get('score-a'));
            $duel->setScoreB($request->query->get('score-b'));

            $em->persist($duel);

            $playerA = $duel->getPlayerSeasonDataA();
            $playerB = $duel->getPlayerSeasonDataB();

            $playerA->setDuelPlayed($playerA->getDuelPlayed() + 1);
            $playerB->setDuelPlayed($playerB->getDuelPlayed() + 1);

            if ((int) $request->query->get('score-a') > (int) $request->query->get('score-b')) {
                $playerA->setDuelWin($playerA->getDuelWin() + 1);
                $playerA->setRank($playerA->getRank() + 3);
                $playerB->setDuelLoose($playerB->getDuelLoose() + 1);
            } elseif ((int) $request->query->get('score-a') < (int) $request->query->get('score-b')) {
                $playerA->setDuelLoose($playerA->getDuelLoose() + 1);
                $playerB->setDuelWin($playerB->getDuelWin() + 1);
                $playerB->setRank($playerB->getRank() + 3);
            } else {
                $playerA->setDuelNull($playerA->getDuelNull() + 1);
                $playerA->setRank($playerA->getRank() + 1);
                $playerB->setDuelNull($playerB->getDuelNull() + 1);
                $playerB->setRank($playerB->getRank() + 1);
            }

            $em->persist($playerA);
            $em->persist($playerB);

            $em->flush();

            $this->addFlash('success', 'Les scores ont été enregistrés');
        } else {
            $this->addFlash('danger', 'Les scores n\'ont pas pu être enregistrés');
        }

        return $this->redirectToRoute('season_index');
    }

    /**
     * @Route("/saisons/terminer/{id}", requirements={ "id" : "\d+" }, name="season_end")
     *
     * @param $id
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function endSeason($id)
    {
        $em = $this->getDoctrine()->getManager();

        $season = $em->getRepository('App:Season')->find($id);

        $season->setStatus(Season::STATUS_COMPLETED);

        $em->persist($season);
        $em->flush();

        return $this->redirectToRoute('season_index');
    }
}<file_sep>/src/Entity/SeasonDuel.php
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\SeasonDuelRepository")
 */
class SeasonDuel
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var Season
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\Season", inversedBy="duels")
     */
    private $season;

    /**
     * @var PlayerSeasonData
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\PlayerSeasonData", inversedBy="duelsA")
     */
    private $playerSeasonDataA;

    /**
     * @var PlayerSeasonData
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\PlayerSeasonData", inversedBy="duelsB")
     */
    private $playerSeasonDataB;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint", nullable=true)
     */
    private $scoreA;

    /**
     * @var integer
     *
     * @ORM\Column(type="smallint", nullable=true)
     */
    private $scoreB;

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id): void
    {
        $this->id = $id;
    }

    /**
     * @return Season
     */
    public function getSeason(): Season
    {
        return $this->season;
    }

    /**
     * @param Season $season
     */
    public function setSeason(Season $season): void
    {
        $this->season = $season;
    }

    /**
     * @return PlayerSeasonData
     */
    public function getPlayerSeasonDataA(): PlayerSeasonData
    {
        return $this->playerSeasonDataA;
    }

    /**
     * @param PlayerSeasonData $playerSeasonDataA
     */
    public function setPlayerSeasonDataA(PlayerSeasonData $playerSeasonDataA): void
    {
        $this->playerSeasonDataA = $playerSeasonDataA;
    }

    /**
     * @return PlayerSeasonData
     */
    public function getPlayerSeasonDataB(): PlayerSeasonData
    {
        return $this->playerSeasonDataB;
    }

    /**
     * @param PlayerSeasonData $playerSeasonDataB
     */
    public function setPlayerSeasonDataB(PlayerSeasonData $playerSeasonDataB): void
    {
        $this->playerSeasonDataB = $playerSeasonDataB;
    }

    /**
     * @return int
     */
    public function getScoreA()
    {
        return $this->scoreA;
    }

    /**
     * @param int $scoreA
     */
    public function setScoreA(int $scoreA): void
    {
        $this->scoreA = $scoreA;
    }

    /**
     * @return int
     */
    public function getScoreB()
    {
        return $this->scoreB;
    }

    /**
     * @param int $scoreB
     */
    public function setScoreB(int $scoreB): void
    {
        $this->scoreB = $scoreB;
    }
}
<file_sep>/src/Entity/Duel.php
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="App\Repository\DuelRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Duel
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Player", inversedBy="duels", cascade={"persist"})
     * @ORM\JoinColumn(nullable=true)
     * @Assert\NotBlank()
     */
    private $playerA;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Player", inversedBy="duels", cascade={"persist"})
     * @ORM\JoinColumn(nullable=true)
     * @Assert\NotBlank()
     */
    private $playerB;

    /**
     * @ORM\Column(type="integer")
     * @Assert\NotBlank()
     * @Assert\GreaterThanOrEqual(0)
     */
    private $scoreA;

    /**
     * @ORM\Column(type="integer")
     * @Assert\NotBlank()
     * @Assert\GreaterThanOrEqual(0)
     */
    private $scoreB;

    /**
     * @ORM\Column(type="integer")
     */
    private $rankA;

    /**
     * @ORM\Column(type="integer")
     */
    private $rankB;

    /**
     * @ORM\Column(type="datetime")
     */
    private $updated_at;

    /**
     * @ORM\Column(type="datetime")
     */
    private $created_at;

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * @return mixed
     */
    public function getPlayerA()
    {
        return $this->playerA;
    }

    /**
     * @param mixed $playerA
     */
    public function setPlayerA($playerA)
    {
        $this->playerA = $playerA;
    }

    /**
     * @return mixed
     */
    public function getPlayerB()
    {
        return $this->playerB;
    }

    /**
     * @param mixed $playerB
     */
    public function setPlayerB($playerB)
    {
        $this->playerB = $playerB;
    }

    /**
     * @return mixed
     */
    public function getScoreA()
    {
        return $this->scoreA;
    }

    /**
     * @param mixed $scoreA
     */
    public function setScoreA($scoreA)
    {
        $this->scoreA = $scoreA;
    }

    /**
     * @return mixed
     */
    public function getScoreB()
    {
        return $this->scoreB;
    }

    /**
     * @param mixed $scoreB
     */
    public function setScoreB($scoreB)
    {
        $this->scoreB = $scoreB;
    }

    /**
     * @return mixed
     */
    public function getRankA()
    {
        return $this->rankA;
    }

    /**
     * @param mixed $rankA
     */
    public function setRankA($rankA)
    {
        $this->rankA = $rankA;
    }

    /**
     * @return mixed
     */
    public function getRankB()
    {
        return $this->rankB;
    }

    /**
     * @param mixed $rankB
     */
    public function setRankB($rankB)
    {
        $this->rankB = $rankB;
    }

    /**
     * @return mixed
     */
    public function getUpdatedAt()
    {
        return $this->updated_at;
    }

    /**
     * @param mixed $updated_at
     */
    public function setUpdatedAt($updated_at)
    {
        $this->updated_at = $updated_at;
    }

    /**
     * @return mixed
     */
    public function getCreatedAt()
    {
        return $this->created_at;
    }

    /**
     * @param mixed $created_at
     */
    public function setCreatedAt($created_at)
    {
        $this->created_at = $created_at;
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function updatedTimestamps()
    {
        $this->setUpdatedAt(new \DateTime('now'));

        if ($this->getCreatedAt() == null) {
            $this->setCreatedAt(new \DateTime('now'));
        }
    }
}
