Passed
Push — master ( 9a4b58...860dbf )
by Cesar
07:21 queued 18s
created

MagicLink::getTokenLength()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace MagicLink;
4
5
use Carbon\Carbon;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\QueryException;
8
use Illuminate\Support\Facades\Event;
9
use Illuminate\Support\Facades\Hash;
10
use Illuminate\Support\Str;
11
use MagicLink\Actions\ActionAbstract;
12
use MagicLink\Events\MagicLinkWasCreated;
13
use MagicLink\Events\MagicLinkWasVisited;
14
15
/**
16
 * @property string $token
17
 * @property Carbon|null $available_at
18
 * @property int|null $max_visits
19
 * @property \MagicLink\Actions\ActionAbstract $action
20
 */
21
class MagicLink extends Model
22
{
23
    use AccessCode;
0 ignored issues
show
Bug introduced by
The trait MagicLink\AccessCode requires the property $cookieNam which is not provided by MagicLink\MagicLink.
Loading history...
24
25
    public $incrementing = false;
26
27
    protected $keyType = 'string';
28
29
    protected static function boot()
30
    {
31
        parent::boot();
32
33
        static::creating(function ($model) {
34
            $model->id = Str::uuid();
35
        });
36
    }
37
38
    protected static function getTokenLength()
39
    {
40
        return config('magiclink.token.length', 64) <= 255
41
            ? config('magiclink.token.length', 64)
42
            : 255;
43
    }
44
45
    public function getActionAttribute($value)
46
    {
47
        if ($this->getConnection()->getDriverName() === 'pgsql') {
48
            return unserialize(base64_decode($value));
49
        }
50
51
        return unserialize($value);
52
    }
53
54
    public function setActionAttribute($value)
55
    {
56
        $this->attributes['action'] = $this->getConnection()->getDriverName() === 'pgsql'
57
                                        ? base64_encode(serialize($value))
58
                                        : serialize($value);
59
    }
60
61
    public function getUrlAttribute()
62
    {
63
        return url(sprintf(
64
            '%s/%s:%s',
65
            config('magiclink.url.validate_path', 'magiclink'),
66
            $this->id,
67
            $this->token
68
        ));
69
    }
70
71
    /**
72
     * Create makiglink.
73
     *
74
     * @return self
75
     */
76
    public static function create(ActionAbstract $action, ?int $lifetime = 4320, ?int $numMaxVisits = null)
77
    {
78
        self::deleteMagicLinkExpired();
79
80
        $magiclink = new static();
81
82
        $magiclink->token = Str::random(self::getTokenLength());
83
        $magiclink->available_at = $lifetime
84
                                    ? Carbon::now()->addMinutes($lifetime)
85
                                    : null;
86
        $magiclink->max_visits = $numMaxVisits;
87
        $magiclink->action = $action;
88
89
        $magiclink->save();
90
91
        $magiclink->action = $action->setMagicLinkId($magiclink->id);
92
93
        $magiclink->save();
94
95
        Event::dispatch(new MagicLinkWasCreated($magiclink));
96
97
        return $magiclink;
98
    }
99
100
    /**
101
     * Protect the Action with an access code.
102
     */
103
    public function protectWithAccessCode(string $accessCode): self
104
    {
105
        $this->access_code = Hash::make($accessCode);
106
107
        $this->save();
108
109
        return $this;
110
    }
111
112
    /**
113
     * Execute Action.
114
     *
115
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
116
     */
117
    public function run()
118
    {
119
        return $this->action->run();
120
    }
121
122
    /**
123
     * Call when magiclink has been visited.
124
     *
125
     * @return void
126
     */
127
    public function visited()
128
    {
129
        try {
130
            $this->increment('num_visits');
131
        } catch (QueryException $e) {
132
            // catch exceptino if fails to increment num_visits
133
        }
134
135
        Event::dispatch(new MagicLinkWasVisited($this));
136
    }
137
138
    /**
139
     * Get valid MagicLink by token.
140
     *
141
     * @param  string  $token
142
     * @return \MagicLink\MagicLink|null
143
     */
144
    public static function getValidMagicLinkByToken($token)
145
    {
146
        [$tokenId, $tokenSecret] = explode(':', "{$token}:");
147
148
        if (empty($tokenSecret)) {
149
            return;
150
        }
151
152
        return self::where('id', $tokenId)
153
                    ->where('token', $tokenSecret)
154
                    ->where(function ($query) {
155
                        $query
156
                            ->whereNull('available_at')
157
                            ->orWhere('available_at', '>=', Carbon::now());
158
                    })
159
                    ->where(function ($query) {
160
                        $query
161
                            ->whereNull('max_visits')
162
                            ->orWhereRaw('max_visits > num_visits');
163
                    })
164
                    ->first();
165
    }
166
167
    /**
168
     * Get MagicLink by token.
169
     *
170
     * @param  string  $token
171
     * @return \MagicLink\MagicLink|null
172
     */
173
    public static function getMagicLinkByToken($token)
174
    {
175
        [$tokenId, $tokenSecret] = explode(':', "{$token}:");
176
177
        if (empty($tokenSecret)) {
178
            return;
179
        }
180
181
        return self::where('id', $tokenId)
182
                    ->where('token', $tokenSecret)
183
                    ->first();
184
    }
185
186
    /**
187
     * Delete magiclink was expired.
188
     *
189
     * @return void
190
     */
191
    public static function deleteMagicLinkExpired()
192
    {
193
        self::where(function ($query) {
194
            $query
195
                ->where('available_at', '<', Carbon::now())
196
                ->orWhere(function ($query) {
197
                    $query
198
                        ->whereNotNull('max_visits')
199
                        ->whereRaw('max_visits <= num_visits');
200
                });
201
        })
202
        ->delete();
203
    }
204
205
    /**
206
     * Delete all MagicLink.
207
     *
208
     * @return void
209
     */
210
    public static function deleteAllMagicLink()
211
    {
212
        self::truncate();
213
    }
214
}
215