Generate instant wins depending on Prize objects in your database
# app/config/config.yml
parameters:
# Dates must respect this format: Y-m-d H:i:s
game_start: '2016-02-01 11:00:00'
game_end: '2016-03-31 23:59:59'
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Prize
*
* @ORM\Table(name="prize")
* @ORM\Entity()
*/
class Prize
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
protected $name;
/**
* @var float
*
* @ORM\Column(name="amount", type="float")
*/
protected $amount;
/**
* @var string
*
* @ORM\Column(name="slug", type="string", length=255, unique=true)
*/
protected $slug;
/**
* @var int
*
* @ORM\Column(name="quantity", type="integer")
*/
protected $quantity;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param int $id
* @return Prize
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*
* @return Prize
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return float
*/
public function getAmount()
{
return $this->amount;
}
/**
* @param float $amount
*
* @return Prize
*/
public function setAmount($amount)
{
$this->amount = $amount;
return $this;
}
/**
* @return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* @param string $slug
*
* @return Prize
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* @return int
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* @param int $quantity
* @return Prize
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* InstantWin
*
* @ORM\Table(name="instant_win")
* @ORM\Entity()
*/
class InstantWin
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var \DateTime
*
* @ORM\Column(name="winDate", type="datetime")
*/
protected $winDate;
/**
* @var Prize
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Prize")
*/
protected $prize;
/**
* @var Participation
*
* @ORM\OneToOne(targetEntity="AppBundle\Entity\Participation", mappedBy="instantWin")
* @ORM\JoinColumn(nullable=true)
*/
protected $participation;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param \DateTime $winDate
*
* @return InstantWin
*/
public function setWinDate($winDate)
{
$this->winDate = $winDate;
return $this;
}
/**
* @return \DateTime
*/
public function getWinDate()
{
return $this->winDate;
}
/**
* Set prize
*
* @param \AppBundle\Entity\Prize $prize
*
* @return InstantWin
*/
public function setPrize(\AppBundle\Entity\Prize $prize = null)
{
$this->prize = $prize;
return $this;
}
/**
* Get prize
*
* @return \AppBundle\Entity\Prize
*/
public function getPrize()
{
return $this->prize;
}
/**
* Set participation
*
* @param Participation $participation
*
* @return InstantWin
*/
public function setParticipation(Participation $participation = null)
{
$this->participation = $participation;
return $this;
}
/**
* Get participation
*
* @return Participation
*/
public function getParticipation()
{
return $this->participation;
}
}
<?php
namespace AppBundle\Command;
use AppBundle\Entity\InstantWin;
use AppBundle\Entity\Prize;
use AppBundle\EventListener\GameEndListener;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Helper\Table;
class GenerateInstantWinsCommand extends ContainerAwareCommand
{
/**
* @var SymfonyStyle
*/
private $io;
protected function configure()
{
$this
->setName('app:generate:instantwins')
->setDescription('Generates random InstantWin dates')
->addOption('truncate', null, InputOption::VALUE_NONE, 'Truncate all instant wins before processing.')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/** @var EntityManager $em */
$em = $this->getContainer()->get('doctrine')->getManager();
/** @var Prize[] $basePrizes */
$basePrizes = $em->getRepository('AppBundle:Prize')->findAllRoot('slug');
$this->io = $io = new SymfonyStyle($input, $output);
$instantWins = $em->getRepository('AppBundle:InstantWin')->findAll();
if (count($instantWins)) {
$truncate = $input->getOption('truncate');
if (!$truncate && !$input->getOption('no-interaction')) {
$truncate = $io->confirm('Truncate the instant wins already stored in the database?', false);
}
} else {
$truncate = false;
}
if ($truncate) {
$io->block('Truncating...');
$io->progressStart(count($instantWins)+1);
foreach ($instantWins as $instantWin) {
$em->remove($instantWin);
$io->progressAdvance();
}
$em->flush();
$io->progressFinish();
}
$instantWins = null;
$startDate = \DateTime::createFromFormat(GameEndListener::DATE_FORMAT, $this->getContainer()->getParameter('game_start'));
$endDate = \DateTime::createFromFormat(GameEndListener::DATE_FORMAT, $this->getContainer()->getParameter('game_end'));
// Here we add "+1" because both start and end day are included.
$numberOfDays = $startDate->diff($endDate)->days + 1;
// Calculate the total InstantWin to generate.
$totalInstantWins = array_reduce($basePrizes, function ($carry, Prize $prize) {
return $carry + $prize->getQuantity();
}, 0);
$io->section('Game informations');
$io->table([], [
['Start date', '<info>' . $startDate->format('Y-m-d H:i:s') . '</info>'],
['End date', '<info>' . $endDate->format('Y-m-d H:i:s') . '</info>'],
['Days to play', '<info>' . $numberOfDays . '</info>'],
['Total InstantWins', '<info>' . $totalInstantWins . '</info>'],
]);
// Here we set up
$tableHeaders = [
'#',
'Quantity',
'Qty./day',
'Extra',
'Extra/day',
];
$prizesList = [];
foreach ($basePrizes as $prize) {
$quantity = $prize->getQuantity();
$numberPerDay = floor($quantity / $numberOfDays);
$extra = $quantity % $numberOfDays;
$extraInterval = $extra / $numberOfDays;
$prizesList[$prize->getSlug()] = array_combine(
$tableHeaders,
[
$prize->getId(),
$quantity,
$numberPerDay,
$extra,
$extraInterval,
]
);
}
$io->section('Prizes distribution');
$io->table($tableHeaders, $prizesList);
// Hours for each instant win will be between these two values
$startHour = 7;
$endHour = 23;
$intervalHour = $endHour - $startHour;
$numberOfInstantWinsIterated = 0;
$io->block('Processing...');
$io->progressStart($totalInstantWins);
// We determine instant wins for each prize.
foreach ($basePrizes as $prize) {
$items = $prize->getQuantity();
$period = $numberOfDays / $items;
$instantWinsForThisPrize = [];
// First, let's distribute them equally for every day.
for ($i = 0; $i < $items; $i++) {
$dayNumber = (int) floor($i * $period);
$date = clone $startDate;
$date->modify('+' . $dayNumber . ' day' . ($dayNumber > 1 ? 's' : ''));
$datestr = $date->format('Y-m-d');
$iw = (new InstantWin())
->setPrize($prize)
->setWinDate($date)
;
$instantWinsForThisPrize[$datestr][] = $iw;
}
// Then, for each day, we'll distribute equally the different instant wins during the day
foreach ($instantWinsForThisPrize as $date => $iws) {
/** @var InstantWin[] $iws */
$items = count($iws);
if (!$items) {
continue;
}
// Only one exception here:
// If we have only one instant win for a day, its hour and minute will be determined randomly
if (1 === $items) {
$instantWin = $iws[0];
$date = $instantWin->getWinDate();
$date->setTime(rand($startHour, $endHour), rand(0, 59));
$instantWin->setWinDate($date);
$em->persist($instantWin);
$io->progressAdvance();
continue;
}
$period = $intervalHour / ($items ?: 1);
$i = 0;
// Distribute each instant win equally during the day
foreach ($iws as $instantWin) {
$date = $instantWin->getWinDate();
$date->setTime($startHour, 0, 0);
if ($i) {
$date->setTimestamp($date->getTimestamp() + intval(($i * $period * 3600)));
}
$instantWin->setWinDate($date);
$em->persist($instantWin);
$io->progressAdvance();
$i++;
}
}
}
$io->progressFinish();
$io->block('Flushing...');
$em->flush();
$io->success('Done !');
}
}