Completed
Push — master ( 854d5e...fc0783 )
by Stepan
05:04
created

StatisticService::_getFirstDayOfTicketSales()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 20
rs 9.7666
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace Application\Bundle\DefaultBundle\Service;
4
5
use Stfalcon\Bundle\EventBundle\Entity\Event;
6
use Stfalcon\Bundle\EventBundle\Entity\Payment;
7
use Doctrine\ORM\EntityManager;
8
use Doctrine\ORM\NoResultException;
9
10
class StatisticService
11
{
12
    /** @var EntityManager */
13
    protected $em;
14
15
    /**
16
     * @param EntityManager $em
17
     */
18
    public function __construct($em)
19
    {
20
        $this->em = $em;
21
    }
22
23
    /**
24
     * Get data for daily statistics of tickets sold
25
     * @param Event $event
26
     *
27
     * @return array
28
     * @throws \Exception
29
     */
30
    public function getDataForDailyStatisticsOfTicketsSold(Event $event)
31
    {
32
        $dateFrom = $this->_getFirstDayOfTicketSales($event);
33
        $dateTo = $this->_getLastDayOfTicketSales($event);
34
35
        $qb = $this->em->createQueryBuilder();
36
        $qb->select('DATE(p.updatedAt) as date_of_sale, COUNT(t.id) as tickets_sold_number')
37
            ->from('Stfalcon\Bundle\EventBundle\Entity\Ticket', 't')
38
            ->join('t.payment', 'p')
39
            ->where($qb->expr()->eq('t.event', ':event'))
40
            ->andWhere($qb->expr()->gte('p.updatedAt', ':date_from'))
41
            ->andWhere($qb->expr()->lte('p.updatedAt', ':date_to'))
42
            ->andWhere($qb->expr()->eq('p.status', ':status'))
43
            ->andWhere('p.amount > 0') // тільки реально продані квитки
44
            ->setParameters([
45
                'event' => $event,
46
                'date_from' => $dateFrom,
47
                'date_to' => $dateTo,
48
                'status' => Payment::STATUS_PAID,
49
            ])
50
            ->addGroupBy('date_of_sale')
51
        ;
52
53
        $results = $qb
54
            ->getQuery()
55
            ->getResult()
56
        ;
57
58
        // fill the possible gap in sequence of dates
59
        $dateRange = new \DatePeriod($dateFrom, new \DateInterval('P1D'), $dateTo->modify('+1 day'));
60
61
        $formattedResult = [];
62
        foreach ($dateRange as $date) {
63
            $key = $date->format('Y-m-d');
64
            $formattedResult[$key][0] = $date;
65
            $formattedResult[$key][1] = null;
66
        }
67
68
        // merge real data with array of prepared results
69
        foreach ($results as $result) {
70
            $formattedResult[$result['date_of_sale']][1] = (int) $result['tickets_sold_number'];
71
        }
72
73
        return $formattedResult;
74
    }
75
76
77
    /**
78
     * Get data for total statistics of tickets sold
79
     * @param Event|null $event
80
     *
81
     * @return array
82
     * @throws \Exception
83
     */
84
    public function getDataForTotalStatisticsOfTicketsSold(Event $event)
85
    {
86
        $qbTicketsSold = $this->em->createQueryBuilder();
87
        $qbTicketsSold->select('COUNT(t.id) as tickets_sold_number, SUM(t.amount) as tickets_amount')
88
            ->from('Stfalcon\Bundle\EventBundle\Entity\Ticket', 't')
89
            ->join('t.payment', 'p')
90
            ->andWhere($qbTicketsSold->expr()->eq('t.event', ':event'))
91
            ->andWhere($qbTicketsSold->expr()->eq('p.status', ':status'))
92
            ->setParameters([
93
                'event' => $event,
94
                'status' => Payment::STATUS_PAID,
95
            ])
96
        ;
97
98
        $qbFreeTickets = clone $qbTicketsSold;
99
100
        $qbTicketsSold->andWhere('p.amount > 0');
101
        $results = $qbTicketsSold->getQuery()->getSingleResult();
102
103
        $qbFreeTickets->andWhere('p.amount = 0'); //free_tickets_number
104
        $resultsFreeTickets = $qbFreeTickets->getQuery()->getSingleResult();
105
106
        $results['free_tickets_number'] = $resultsFreeTickets['tickets_sold_number'];
107
        $results['total_tickets_number'] = $results['free_tickets_number'] + $results['tickets_sold_number'];
108
109
        return $results;
110
    }
111
112
    /**
113
     * Get the first day of ticket sales (get createdAt of the first event ticket)
114
     * @param Event $event
115
     *
116
     * @return \DateTime
117
     * @throws \Doctrine\ORM\Query\QueryException
118
     */
119
    private function _getFirstDayOfTicketSales(Event $event) {
120
        $qb = $this->em->createQueryBuilder();
121
        $qb->select('t.createdAt')
122
            ->from('Stfalcon\Bundle\EventBundle\Entity\Ticket', 't')
123
            ->where($qb->expr()->eq('t.event', ':event'))
124
            ->andWhere($qb->expr()->eq('t.event', ':event'))
125
            ->setParameters([
126
                'event' => $event,
127
            ])
128
            ->orderBy('t.createdAt', 'ASC')
129
            ->setMaxResults(1);
130
131
        try {
132
            $date = $qb->getQuery()
133
                ->getSingleScalarResult();
134
        } catch (NoResultException $e) {
135
            $date = $this->_getLastDayOfTicketSales($event)->modify('-1 week')->format('Y-m-d');
136
        }
137
138
        return new \DateTime($date);
139
    }
140
141
    /**
142
     * Get the last day of ticket sales
143
     *
144
     * @param Event $event
145
     * @return \DateTime|null
146
     */
147
    private function _getLastDayOfTicketSales(Event $event) {
148
        return $event->getDateEnd() ?: $event->getDate();
149
    }
150
151
    /**
152
     * Get data for forecasting tickets sales (based on previous events)
153
     *
154
     * @param Event $event
155
     */
156
    public function getDataForForecastingTicketsSales(Event $event)
157
    {
158
        $weeksMaxNumber = 20; // задаєм максимальну глибину аналізу
159
160
        // витягнути івенти з цієї групи
161
        $events = $this->em->getRepository('StfalconEventBundle:Event')
162
            ->findBy(['group' => $event->getGroup()], ['date' => 'DESC'], 4);
163
164
        // формуєм масив ключів з айдшок івентів, щоб використати його в формуванні заготовки масиву результатів
165
        $resultsKeys = [$event->getId()];
166
        foreach ($events as $e) {
167
            $resultsKeys[] = $e->getId();
168
        }
169
        $resultsValueTemplate = array_fill_keys($resultsKeys, null);
170
        // заповнюєм заготовку масиву результатів
171
        $results = array_fill(0, $weeksMaxNumber, $resultsValueTemplate);
172
173
        // витягуєм статистику продажів для івентів з цієї групи
174
        foreach ($events as $event) {
175
            $dataForDailyStatistics = $this->getDataForDailyStatisticsOfTicketsSold($event);
176
            $reverseDataForDailyStatistics = array_reverse($dataForDailyStatistics);
177
178
            // групуєм статистику продажів для івенту по тижнях
179
            $oneEventResults = [];
180
            foreach ($reverseDataForDailyStatistics as $oneDateData) {
181
                $date = $oneDateData[0];
182
                $number = $oneDateData[1];
183
184
                $key = $date->format("Y-W");
185
                $oneEventResults[$key] = (isset($oneEventResults[$key]) ? $oneEventResults[$key] : 0) + $number;
186
            }
187
188
            // мержим отриману статистику івента в загальний масив результатів
189
            foreach (array_values($oneEventResults) as $week => $number) {
190
                if ($week == $weeksMaxNumber) {
191
                    break;
192
                }
193
                $results[$week][$event->getId()] = $number;
194
            }
195
        }
196
197
        return array_reverse($results);
198
    }
199
}
200