1 | <?php |
||
2 | |||
3 | /* |
||
4 | * @copyright 2019 Mautic Contributors. All rights reserved |
||
5 | * @author Mautic |
||
6 | * |
||
7 | * @link http://mautic.org |
||
8 | * |
||
9 | * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html |
||
10 | */ |
||
11 | |||
12 | namespace Mautic\PageBundle\EventListener; |
||
13 | |||
14 | use Mautic\CoreBundle\Event\DetermineWinnerEvent; |
||
15 | use Mautic\PageBundle\Entity\HitRepository; |
||
16 | use Mautic\PageBundle\PageEvents; |
||
17 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
||
18 | use Symfony\Component\Translation\TranslatorInterface; |
||
19 | |||
20 | class DetermineWinnerSubscriber implements EventSubscriberInterface |
||
21 | { |
||
22 | /** |
||
23 | * @var HitRepository |
||
24 | */ |
||
25 | private $hitRepository; |
||
26 | |||
27 | /** |
||
28 | * @var TranslatorInterface |
||
29 | */ |
||
30 | private $translator; |
||
31 | |||
32 | public function __construct(HitRepository $hitRepository, TranslatorInterface $translator) |
||
33 | { |
||
34 | $this->hitRepository = $hitRepository; |
||
35 | $this->translator = $translator; |
||
36 | } |
||
37 | |||
38 | /** |
||
39 | * @return array |
||
40 | */ |
||
41 | public static function getSubscribedEvents() |
||
42 | { |
||
43 | return [ |
||
44 | PageEvents::ON_DETERMINE_BOUNCE_RATE_WINNER => ['onDetermineBounceRateWinner', 0], |
||
45 | PageEvents::ON_DETERMINE_DWELL_TIME_WINNER => ['onDetermineDwellTimeWinner', 0], |
||
46 | ]; |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * Determines the winner of A/B test based on bounce rates. |
||
51 | */ |
||
52 | public function onDetermineBounceRateWinner(DetermineWinnerEvent $event) |
||
53 | { |
||
54 | //find the hits that did not go any further |
||
55 | $parent = $event->getParameters()['parent']; |
||
56 | $children = $event->getParameters()['children']; |
||
57 | $pageIds = $parent->getRelatedEntityIds(); |
||
58 | $startDate = $parent->getVariantStartDate(); |
||
59 | |||
60 | if (null != $startDate && !empty($pageIds)) { |
||
61 | //get their bounce rates |
||
62 | $counts = $this->hitRepository->getBounces($pageIds, $startDate, true); |
||
63 | if ($counts) { |
||
0 ignored issues
–
show
|
|||
64 | // Group by translation |
||
65 | $combined = [ |
||
66 | $parent->getId() => $counts[$parent->getId()], |
||
67 | ]; |
||
68 | |||
69 | if ($parent->hasTranslations()) { |
||
70 | $translations = $parent->getTranslationChildren()->getKeys(); |
||
71 | |||
72 | foreach ($translations as $translation) { |
||
73 | $combined[$parent->getId()]['bounces'] += $counts[$translation]['bounces']; |
||
74 | $combined[$parent->getId()]['totalHits'] += $counts[$translation]['totalHits']; |
||
75 | $combined[$parent->getId()]['rate'] = ($combined[$parent->getId()]['totalHits']) ? round( |
||
76 | ($combined[$parent->getId()]['bounces'] / $combined[$parent->getId()]['totalHits']) * 100, |
||
77 | 2 |
||
78 | ) : 0; |
||
79 | } |
||
80 | } |
||
81 | |||
82 | foreach ($children as $child) { |
||
83 | $combined[$child->getId()] = $counts[$child->getId()]; |
||
84 | |||
85 | if ($child->hasTranslations()) { |
||
86 | $translations = $child->getTranslationChildren()->getKeys(); |
||
87 | foreach ($translations as $translation) { |
||
88 | $combined[$child->getId()]['bounces'] += $counts[$translation]['bounces']; |
||
89 | $combined[$child->getId()]['totalHits'] += $counts[$translation]['totalHits']; |
||
90 | $combined[$child->getId()]['rate'] = ($combined[$child->getId()]['totalHits']) ? round( |
||
91 | ($combined[$child->getId()]['bounces'] / $combined[$child->getId()]['totalHits']) * 100, |
||
92 | 2 |
||
93 | ) : 0; |
||
94 | } |
||
95 | } |
||
96 | } |
||
97 | unset($counts); |
||
98 | |||
99 | //let's arrange by rate |
||
100 | $rates = []; |
||
101 | $support['data'] = []; |
||
102 | $support['labels'] = []; |
||
103 | $bounceLabel = $this->translator->trans('mautic.page.abtest.label.bounces'); |
||
104 | |||
105 | foreach ($combined as $pid => $stats) { |
||
106 | $rates[$pid] = $stats['rate']; |
||
107 | $support['data'][$bounceLabel][] = $rates[$pid]; |
||
108 | $support['labels'][] = $pid.':'.$stats['title']; |
||
109 | } |
||
110 | |||
111 | // investigate the rate calculation, seems that lowest value should be the winner |
||
112 | $max = max($rates); |
||
113 | $support['step_width'] = (ceil($max / 10) * 10); |
||
114 | |||
115 | $winners = ($max > 0) ? array_keys($rates, $max) : []; |
||
116 | |||
117 | $event->setAbTestResults([ |
||
118 | 'winners' => $winners, |
||
119 | 'support' => $support, |
||
120 | 'basedOn' => 'page.bouncerate', |
||
121 | 'supportTemplate' => 'MauticPageBundle:SubscribedEvents\AbTest:bargraph.html.php', |
||
122 | ]); |
||
123 | |||
124 | return; |
||
125 | } |
||
126 | } |
||
127 | |||
128 | $event->setAbTestResults([ |
||
129 | 'winners' => [], |
||
130 | 'support' => [], |
||
131 | 'basedOn' => 'page.bouncerate', |
||
132 | ]); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Determines the winner of A/B test based on dwell time rates. |
||
137 | */ |
||
138 | public function onDetermineDwellTimeWinner(DetermineWinnerEvent $event) |
||
139 | { |
||
140 | //find the hits that did not go any further |
||
141 | $parent = $event->getParameters()['parent']; |
||
142 | $pageIds = $parent->getRelatedEntityIds(); |
||
143 | $startDate = $parent->getVariantStartDate(); |
||
144 | |||
145 | if (null != $startDate && !empty($pageIds)) { |
||
146 | //get their bounce rates |
||
147 | $counts = $this->hitRepository->getDwellTimesForPages($pageIds, ['fromDate' => $startDate]); |
||
148 | $support = []; |
||
149 | |||
150 | if ($counts) { |
||
151 | //in order to get a fair grade, we have to compare the averages here since a page that is only shown |
||
152 | //25% of the time will have a significantly lower sum than a page shown 75% of the time |
||
153 | $avgs = []; |
||
154 | $support['data'] = []; |
||
155 | $support['labels'] = []; |
||
156 | foreach ($counts as $pid => $stats) { |
||
157 | $avgs[$pid] = $stats['average']; |
||
158 | $support['data'][$this->translator->trans('mautic.page.abtest.label.dewlltime.average')][] = $stats['average']; |
||
159 | $support['labels'][] = $pid.':'.$stats['title']; |
||
160 | } |
||
161 | |||
162 | //set max for scales |
||
163 | $max = max($avgs); |
||
164 | $support['step_width'] = (ceil($max / 10) * 10); |
||
165 | |||
166 | //get the page ids with the greatest average dwell time |
||
167 | $winners = ($max > 0) ? array_keys($avgs, $max) : []; |
||
168 | |||
169 | $event->setAbTestResults([ |
||
170 | 'winners' => $winners, |
||
171 | 'support' => $support, |
||
172 | 'basedOn' => 'page.dwelltime', |
||
173 | 'supportTemplate' => 'MauticPageBundle:SubscribedEvents\AbTest:bargraph.html.php', |
||
174 | ]); |
||
175 | |||
176 | return; |
||
177 | } |
||
178 | } |
||
179 | |||
180 | $event->setAbTestResults([ |
||
181 | 'winners' => [], |
||
182 | 'support' => [], |
||
183 | 'basedOn' => 'page.dwelltime', |
||
184 | ]); |
||
185 | } |
||
186 | } |
||
187 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.