Completed
Push — master ( d6b600...7a8fea )
by Benjamin
10:41
created

AbTesting::getCompletedGoals()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 2
cts 2
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Ben182\AbTesting;
4
5
use Ben182\AbTesting\Models\Goal;
6
use Illuminate\Support\Collection;
7
use Ben182\AbTesting\Models\Experiment;
8
use Ben182\AbTesting\Events\GoalCompleted;
9
use Ben182\AbTesting\Events\ExperimentNewVisitor;
10
use Ben182\AbTesting\Exceptions\InvalidConfiguration;
11
12
class AbTesting
13
{
14
    protected $experiments;
15
16
    const SESSION_KEY_EXPERIMENT = 'ab_testing_experiment';
17
    const SESSION_KEY_GOALS = 'ab_testing_goals';
18
19 48
    public function __construct()
20
    {
21 48
        $this->experiments = new Collection;
22 48
    }
23
24
    /**
25
     * Validates the config items and puts them into models.
26
     *
27
     * @return void
28
     */
29 48
    protected function start()
30
    {
31 48
        $configExperiments = config('ab-testing.experiments');
32 48
        $configGoals = config('ab-testing.goals');
33
34 48
        if (!count($configExperiments)) {
35 3
            throw InvalidConfiguration::noExperiment();
36
        }
37
38 45
        if (count($configExperiments) !== count(array_unique($configExperiments))) {
39 3
            throw InvalidConfiguration::experiment();
40
        }
41
42 42
        if (count($configGoals) !== count(array_unique($configGoals))) {
43 42
            throw InvalidConfiguration::goal();
44 42
        }
45
46 42
        foreach ($configExperiments as $configExperiment) {
47
            $this->experiments[] = $experiment = Experiment::firstOrCreate([
48
                'name' => $configExperiment,
49 42
            ], [
50 42
                'visitors' => 0,
51 42
            ]);
52
53 42
            foreach ($configGoals as $configGoal) {
54
                $experiment->goals()->firstOrCreate([
55
                    'name' => $configGoal,
56
                ], [
57
                    'hit' => 0,
58 42
                ]);
59 42
            }
60
        }
61 42
62
        session([
63
            self::SESSION_KEY_GOALS => new Collection,
64
        ]);
65
    }
66
67
    /**
68 48
     * Triggers a new visitor. Picks a new experiment and saves it to the session.
69
     *
70 48
     * @return \Ben182\AbTesting\Models\Experiment|void
71 48
     */
72 42
    public function pageView()
73
    {
74 42
        if (! session(self::SESSION_KEY_EXPERIMENT)) {
75
            $this->start();
76 42
            $this->setNextExperiment();
77
78 9
            event(new ExperimentNewVisitor($this->getExperiment()));
79
80
            return $this->getExperiment();
81
        }
82
    }
83
84
    /**
85 42
     * Calculates a new experiment and sets it to the session.
86
     *
87 42
     * @return void
88 42
     */
89
    protected function setNextExperiment()
90 42
    {
91 42
        $next = $this->getNextExperiment();
92
        $next->incrementVisitor();
93 42
94
        session([
95
            self::SESSION_KEY_EXPERIMENT => $next,
96
        ]);
97
    }
98
99
    /**
100 42
     * Calculates a new experiment.
101
     *
102 42
     * @return \Ben182\AbTesting\Models\Experiment|null
103
     */
104 42
    protected function getNextExperiment()
105
    {
106
        $sorted = $this->experiments->sortBy('visitors');
107
108
        return $sorted->first();
109
    }
110
111
    /**
112
     * Checks if the currently active experiment is the given one.
113
     *
114 9
     * @param string $name The experiments name
115
     *
116 9
     * @return bool
117
     */
118 9
    public function isExperiment(string $name)
119
    {
120
        $this->pageView();
121
122
        return $this->getExperiment()->name === $name;
123
    }
124
125
    /**
126
     * Completes a goal by incrementing the hit property of the model and setting its ID in the session.
127
     *
128 15
     * @param string $goal The goals name
129
     *
130 15
     * @return \Ben182\AbTesting\Models\Goal|false
131 12
     */
132
    public function completeGoal(string $goal)
133
    {
134 15
        if (! $this->getExperiment()) {
135
            $this->pageView();
136 15
        }
137 3
138
        $goal = $this->getExperiment()->goals->where('name', $goal)->first();
139
140 12
        if (! $goal) {
141 3
            return false;
142
        }
143
144 12
        if (session(self::SESSION_KEY_GOALS)->contains($goal->id)) {
145
            return false;
146 12
        }
147 12
148
        session(self::SESSION_KEY_GOALS)->push($goal->id);
149 12
150
        $goal->incrementHit();
151
        event(new GoalCompleted($goal));
152
153
        return $goal;
154
    }
155
156
    /**
157 42
     * Returns the currently active experiment.
158
     *
159 42
     * @return \Ben182\AbTesting\Models\Experiment|null
160
     */
161
    public function getExperiment()
162
    {
163
        return session(self::SESSION_KEY_EXPERIMENT);
164
    }
165
166
    /**
167 3
     * Returns all the completed goals.
168
     *
169 3
     * @return \Illuminate\Support\Collection|false
170
     */
171
    public function getCompletedGoals()
172
    {
173
        if (! session(self::SESSION_KEY_GOALS)) {
174 3
            return false;
175 3
        }
176
177
        return session(self::SESSION_KEY_GOALS)->map(function ($goalId) {
178
            return Goal::find($goalId);
179
        });
180
    }
181
}
182