1 | <?php |
||
15 | class AbTesting |
||
16 | { |
||
17 | protected $experiments; |
||
18 | protected $visitor; |
||
19 | |||
20 | const SESSION_KEY_GOALS = 'ab_testing_goals'; |
||
21 | |||
22 | 51 | public function __construct() |
|
23 | { |
||
24 | 51 | $this->experiments = new Collection; |
|
25 | 51 | } |
|
26 | |||
27 | /** |
||
28 | * Validates the config items and puts them into models. |
||
29 | * |
||
30 | * @return void |
||
31 | */ |
||
32 | 51 | protected function start() |
|
33 | { |
||
34 | 51 | $configExperiments = config('ab-testing.experiments'); |
|
35 | 51 | $configGoals = config('ab-testing.goals'); |
|
36 | |||
37 | 51 | if (count($configExperiments) !== count(array_unique($configExperiments))) { |
|
38 | 3 | throw InvalidConfiguration::experiment(); |
|
39 | } |
||
40 | |||
41 | 48 | if (count($configGoals) !== count(array_unique($configGoals))) { |
|
42 | 3 | throw InvalidConfiguration::goal(); |
|
43 | } |
||
44 | |||
45 | 45 | foreach ($configExperiments as $configExperiment) { |
|
46 | 45 | $this->experiments[] = $experiment = Experiment::firstOrCreate([ |
|
47 | 45 | 'name' => $configExperiment, |
|
48 | ], [ |
||
49 | 45 | 'visitors' => 0, |
|
50 | ]); |
||
51 | |||
52 | 45 | foreach ($configGoals as $configGoal) { |
|
53 | 45 | $experiment->goals()->firstOrCreate([ |
|
54 | 45 | 'name' => $configGoal, |
|
55 | ], [ |
||
56 | 45 | 'hit' => 0, |
|
57 | ]); |
||
58 | } |
||
59 | } |
||
60 | |||
61 | 45 | session([ |
|
62 | 45 | self::SESSION_KEY_GOALS => new Collection, |
|
63 | ]); |
||
64 | 45 | } |
|
65 | |||
66 | /** |
||
67 | * Resets the visitor data. |
||
68 | * |
||
69 | * @return void |
||
70 | */ |
||
71 | 12 | public function resetVisitor() |
|
72 | { |
||
73 | 12 | session()->flush(); |
|
74 | 12 | $this->visitor = null; |
|
75 | 12 | } |
|
76 | |||
77 | /** |
||
78 | * Triggers a new visitor. Picks a new experiment and saves it to the Visitor. |
||
79 | * |
||
80 | * @param int $visitor_id An optional visitor identifier |
||
81 | * |
||
82 | * @return \Ben182\AbTesting\Models\Experiment|void |
||
83 | */ |
||
84 | 51 | public function pageView($visitor_id = null) |
|
85 | { |
||
86 | 51 | $visitor = $this->getVisitor($visitor_id); |
|
87 | |||
88 | 51 | if (! session(self::SESSION_KEY_GOALS)) { |
|
89 | 51 | $this->start(); |
|
90 | 45 | $this->setNextExperiment($visitor); |
|
91 | |||
92 | 45 | event(new ExperimentNewVisitor($this->getExperiment(), $visitor)); |
|
93 | |||
94 | 45 | return $this->getExperiment(); |
|
95 | } |
||
96 | 15 | } |
|
97 | |||
98 | /** |
||
99 | * Calculates a new experiment and sets it to the Visitor. |
||
100 | * |
||
101 | * @param VisitorInterface $visitor An object implementing VisitorInterface |
||
102 | * |
||
103 | * @return void |
||
104 | */ |
||
105 | 45 | protected function setNextExperiment(VisitorInterface $visitor) |
|
106 | { |
||
107 | 45 | $next = $this->getNextExperiment(); |
|
108 | 45 | $next->incrementVisitor(); |
|
109 | |||
110 | 45 | $visitor->setExperiment($next); |
|
111 | 45 | } |
|
112 | |||
113 | /** |
||
114 | * Calculates a new experiment. |
||
115 | * |
||
116 | * @return \Ben182\AbTesting\Models\Experiment |
||
117 | */ |
||
118 | 45 | protected function getNextExperiment() |
|
119 | { |
||
120 | 45 | $sorted = $this->experiments->sortBy('visitors'); |
|
121 | |||
122 | 45 | return $sorted->first(); |
|
123 | } |
||
124 | |||
125 | /** |
||
126 | * Checks if the currently active experiment is the given one. |
||
127 | * |
||
128 | * @param string $name The experiments name |
||
129 | * |
||
130 | * @return bool |
||
131 | */ |
||
132 | 9 | public function isExperiment(string $name) |
|
138 | |||
139 | /** |
||
140 | * Completes a goal by incrementing the hit property of the model and setting its ID in the session. |
||
141 | * |
||
142 | * @param string $goal The goals name |
||
143 | * @param int $visitor_id An optional visitor identifier |
||
144 | * |
||
145 | * @return \Ben182\AbTesting\Models\Goal|false |
||
146 | */ |
||
147 | 18 | public function completeGoal(string $goal, $visitor_id = null) |
|
168 | |||
169 | /** |
||
170 | * Returns the currently active experiment. |
||
171 | * |
||
172 | * @param int $visitor_id An optional visitor identifier |
||
173 | * |
||
174 | * @return \Ben182\AbTesting\Models\Experiment|null |
||
175 | */ |
||
176 | 45 | public function getExperiment($visitor_id = null) |
|
180 | |||
181 | /** |
||
182 | * Returns all the completed goals. |
||
183 | * |
||
184 | * @return \Illuminate\Support\Collection|false |
||
185 | */ |
||
186 | 3 | public function getCompletedGoals() |
|
196 | |||
197 | /** |
||
198 | * Returns a visitor instance. |
||
199 | * |
||
200 | * @param int $visitor_id An optional visitor identifier |
||
201 | * |
||
202 | * @return \Ben182\AbTesting\Models\SessionVisitor|\Ben182\AbTesting\Models\DatabaseVisitor |
||
203 | */ |
||
204 | 51 | public function getVisitor($visitor_id = null) |
|
216 | } |
||
217 |