Passed
Push — master ( 7fdfc7...134d32 )
by Paul
05:31
created

UrlService::create()   B

Complexity

Conditions 8
Paths 18

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
cc 8
eloc 19
c 4
b 0
f 1
nc 18
nop 3
dl 0
loc 31
rs 8.4444
1
<?php
2
3
namespace Devpri\Tinre\Services;
4
5
use Devpri\Tinre\Models\Url;
6
use Exception;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Support\Facades\DB;
9
use Illuminate\Support\Facades\Validator;
10
use Illuminate\Support\Str;
11
use Illuminate\Validation\ValidationException;
12
13
class UrlService
14
{
15
    public function index($data, $user)
16
    {
17
        $search = $data['search'] ?? null;
18
        $startDate = $data['start_date'] ?? null;
19
        $endDate = $data['end_date'] ?? null;
20
        $limit = $data['limit'] ?? 25;
21
        $active = $data['active'] ?? null;
22
        $sortBy = $data['sort_by'] ?? 'created_at';
23
        $sortDirection = $data['sort_direction'] ?? 'desc';
24
25
        $query = Url::query();
26
27
        if ($user->cant('viewAny', Url::class)) {
28
            $query->where('user_id', $user->id);
29
        }
30
31
        if ($search) {
32
            $query->where(function (Builder $query) use ($search) {
33
                $query->where('path', 'LIKE', "%{$search}%")
34
                    ->orWhere('long_url', 'LIKE', "%{$search}%")
35
                    ->orWhereHas('user', function ($query) use ($search) {
36
                        $query->where('name', 'LIKE', "%{$search}%")
37
                            ->orWhere('email', 'LIKE', "%{$search}%");
38
                    });
39
            });
40
        }
41
42
        if ($startDate && $endDate) {
43
            $query->whereBetween('created_at', [$startDate, $endDate]);
44
        }
45
46
        if (isset($active)) {
47
            $query->where('active', $active);
48
        }
49
50
        return $query->orderBy($sortBy, $sortDirection)->with('user')->paginate($limit);
51
    }
52
53
    public function create($longUrl, $path = null, $user = null): Url
54
    {
55
        $this->validateUrl($longUrl);
56
57
        $url = new Url();
58
        $url->long_url = $longUrl;
59
        $url->user_id = $user ? $user->id : null;
60
61
        if ($path && ($user || config('tinre.guest_form_custom_path'))) {
62
            $this->validatePath($path);
63
64
            $url->path = $path;
65
            $url->save();
66
67
            return $url;
68
        }
69
70
        $maxGenerationAttempts = 5;
71
72
        while ($maxGenerationAttempts-- > 0) {
73
            try {
74
                $url->path = strtolower(Str::random(config('tinre.default_path_length')));
75
76
                $this->validatePath($url->path);
77
78
                $url->save();
79
80
                return $url;
81
            } catch (Exception $e) {
82
                if ($maxGenerationAttempts === 0) {
83
                    throw $e;
84
                }
85
            }
86
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Devpri\Tinre\Models\Url. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
87
    }
88
89
    public function update($id, $active, $longUrl, $path, $user): Url
90
    {
91
        $this->validateUrl($longUrl);
92
93
        $url = Url::where('id', $id)->firstOrFail();
94
95
        if ($url->path != $path) {
96
            $this->validatePath($path);
97
        }
98
99
        if ($user->cant('update', $url)) {
100
            abort(401);
101
        }
102
103
        $url->active = $active;
104
        $url->long_url = $longUrl;
105
        $url->path = $path;
106
        $url->save();
107
108
        return $url;
109
    }
110
111
    protected function validatePath($path): void
112
    {
113
        Validator::make([
114
            'path' => $path,
115
        ], [
116
            'path' => ['required', 'alpha_dash', 'min:'.config('tinre.min_path_length'), 'max:'.config('tinre.max_path_length')],
117
        ])->validate();
118
119
        $path = strtolower($path);
120
121
        $url = DB::table('urls')->whereRaw('lower(path) like (?)', ["%{$path}%"])->count();
122
123
        if ($url) {
124
            throw ValidationException::withMessages([
125
                'path' => [__('The path has already been taken.')],
126
            ]);
127
        }
128
129
        if (in_array($path, config('tinre.restricted_paths'))) {
130
            throw ValidationException::withMessages([
131
                'path' => [__('Restricted path.')],
132
            ]);
133
        }
134
    }
135
136
    protected function validateUrl($url): void
137
    {
138
        Validator::make([
139
            'long_url' => $url,
140
        ], [
141
            'long_url' => ['required', 'url', 'active_url'],
142
        ])->validate();
143
144
        if (in_array(parse_url($url, PHP_URL_HOST), config('tinre.restricted_domains'))) {
145
            throw ValidationException::withMessages([
146
                'long_url' => [__('Restricted domain.')],
147
            ]);
148
        }
149
    }
150
}
151