Passed
Pull Request — master (#39)
by Cesar
05:03
created

MagicLink::getMagicLinkByToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 2
nc 2
nop 1
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\ActionInterface;
12
use MagicLink\Events\MagicLinkWasCreated;
13
use MagicLink\Events\MagicLinkWasVisited;
14
15
class MagicLink extends Model
16
{
17
    public $incrementing = false;
18
19
    protected $keyType = 'string';
20
21
    protected static function boot()
22
    {
23
        parent::boot();
24
25
        static::creating(function ($model) {
26
            $model->id = Str::uuid();
27
        });
28
    }
29
30
    protected static function getTokenLength()
31
    {
32
        return config('magiclink.token.length', 64) <= 255
33
            ? config('magiclink.token.length', 64)
34
            : 255;
35
    }
36
37
    public function getActionAttribute($value)
38
    {
39
        if ($this->getConnection()->getDriverName() === 'pgsql') {
40
            return unserialize(base64_decode($value));
41
        }
42
43
        return unserialize($value);
44
    }
45
46
    public function setActionAttribute($value)
47
    {
48
        $this->attributes['action'] = $this->getConnection()->getDriverName() === 'pgsql'
49
                                        ? base64_encode(serialize($value))
50
                                        : serialize($value);
51
    }
52
53
    public function getUrlAttribute()
54
    {
55
        return url(sprintf(
56
            '%s/%s:%s',
57
            config('magiclink.url.validate_path', 'magiclink'),
58
            $this->id,
59
            $this->token
60
        ));
61
    }
62
63
    /**
64
     * Create makiglink.
65
     *
66
     * @param ActionInterface $action
67
     * @param int|null $lifetime
68
     * @param int|null $numMaxVisits
69
     * @return self
70
     */
71
    public static function create(ActionInterface $action, ?int $lifetime = 4320, ?int $numMaxVisits = null)
72
    {
73
        self::deleteMagicLinkExpired();
74
75
        $magiclink = new static();
76
77
        $magiclink->token = Str::random(self::getTokenLength());
0 ignored issues
show
Bug introduced by
The property token does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
78
        $magiclink->available_at = $lifetime
0 ignored issues
show
Bug introduced by
The property available_at does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
79
                                    ? Carbon::now()->addMinute($lifetime)
0 ignored issues
show
Unused Code introduced by
The call to Carbon\Carbon::addMinute() has too many arguments starting with $lifetime. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
                                    ? Carbon::now()->/** @scrutinizer ignore-call */ addMinute($lifetime)

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
80
                                    : null;
81
        $magiclink->max_visits = $numMaxVisits;
0 ignored issues
show
Bug introduced by
The property max_visits does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
82
        $magiclink->action = $action;
0 ignored issues
show
Bug introduced by
The property action does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
83
84
        $magiclink->save();
85
86
        Event::dispatch(new MagicLinkWasCreated($magiclink));
87
88
        return $magiclink;
89
    }
90
91
    /**
92
     * Protect the Action with an access code
93
     *
94
     * @param string $accessCode
95
     * @return self
96
     */
97
    public function protectWithAccessCode(string $accessCode): self
98
    {
99
        $this->access_code = Hash::make($accessCode);
100
101
        $this->save();
102
103
        return $this;
104
    }
105
106
    /**
107
     * Execute Action.
108
     *
109
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
110
     */
111
    public function run()
112
    {
113
        return $this->action->run();
114
    }
115
116
    /**
117
     * Call when magiclink has been visited.
118
     *
119
     * @return void
120
     */
121
    public function visited()
122
    {
123
        try {
124
            $this->increment('num_visits');
125
        } catch (QueryException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
126
        }
127
128
        Event::dispatch(new MagicLinkWasVisited($this));
129
    }
130
131
    /**
132
     * Get valid MagicLink by token.
133
     *
134
     * @param string $token
135
     * @return null|\MagicLink\MagicLink
136
     */
137
    public static function getValidMagicLinkByToken($token)
138
    {
139
        [$tokenId, $tokenSecret] = explode(':', "{$token}:");
140
141
        if (empty($tokenSecret)) {
142
            return;
143
        }
144
145
        return self::where('id', $tokenId)
146
                    ->where('token', $tokenSecret)
147
                    ->where(function ($query) {
148
                        $query
149
                            ->whereNull('available_at')
150
                            ->orWhere('available_at', '>=', Carbon::now());
151
                    })
152
                    ->where(function ($query) {
153
                        $query
154
                            ->whereNull('max_visits')
155
                            ->orWhereRaw('max_visits > num_visits');
156
                    })
157
                    ->first();
158
    }
159
160
    /**
161
     * Get MagicLink by token.
162
     *
163
     * @param string $token
164
     * @return null|\MagicLink\MagicLink
165
     */
166
    public static function getMagicLinkByToken($token)
167
    {
168
        [$tokenId, $tokenSecret] = explode(':', "{$token}:");
169
170
        if (empty($tokenSecret)) {
171
            return;
172
        }
173
174
        return self::where('id', $tokenId)
175
                    ->where('token', $tokenSecret)
176
                    ->first();
177
    }
178
179
    /**
180
     * Delete magiclink was expired.
181
     *
182
     * @return void
183
     */
184
    public static function deleteMagicLinkExpired()
185
    {
186
        self::where(function ($query) {
187
            $query
188
                ->where('available_at', '<', Carbon::now())
189
                ->orWhere(function ($query) {
190
                    $query
191
                        ->whereNotNull('max_visits')
192
                        ->whereRaw('max_visits <= num_visits');
193
                });
194
        })
195
        ->delete();
196
    }
197
198
    /**
199
     * Delete all MagicLink.
200
     *
201
     * @return void
202
     */
203
    public static function deleteAllMagicLink()
204
    {
205
        self::truncate();
206
    }
207
}
208