SmartPlaylistService   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 70
Duplicated Lines 0 %

Test Coverage

Coverage 82.14%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 11
eloc 26
c 1
b 0
f 0
dl 0
loc 70
ccs 23
cts 28
cp 0.8214
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getSongs() 0 9 2
A createRequireUserRule() 0 6 1
A buildQueryFromRules() 0 13 2
A addRequiresUserRules() 0 18 5
A __construct() 0 3 1
1
<?php
2
3
namespace App\Services;
4
5
use App\Models\Playlist;
6
use App\Models\Rule;
7
use App\Models\Song;
8
use App\Models\User;
9
use App\Repositories\SongRepository;
10
use Illuminate\Database\Eloquent\Builder;
11
use Illuminate\Database\Eloquent\Collection;
12
use RuntimeException;
13
14
class SmartPlaylistService
15
{
16
    private const RULE_REQUIRES_USER_PREFIXES = ['interactions.'];
17
18
    private $songRepository;
19
20 20
    public function __construct(SongRepository $songRepository)
21
    {
22 20
        $this->songRepository = $songRepository;
23 20
    }
24
25
    public function getSongs(Playlist $playlist): Collection
26
    {
27
        if (!$playlist->is_smart) {
28
            throw new RuntimeException($playlist->name.' is not a smart playlist.');
29
        }
30
31
        $rules = $this->addRequiresUserRules($playlist->rules, $playlist->user);
0 ignored issues
show
Bug introduced by
$playlist->rules of type string is incompatible with the type array expected by parameter $rules of App\Services\SmartPlayli...:addRequiresUserRules(). ( Ignorable by Annotation )

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

31
        $rules = $this->addRequiresUserRules(/** @scrutinizer ignore-type */ $playlist->rules, $playlist->user);
Loading history...
32
33
        return $this->buildQueryFromRules($rules)->get();
34
    }
35
36 13
    public function buildQueryFromRules(array $rules): Builder
37
    {
38 13
        $query = Song::query();
39
40
        collect($rules)->each(static function (array $ruleGroup) use ($query): void {
41
            $query->orWhere(static function (Builder $subQuery) use ($ruleGroup): void {
42 13
                foreach ($ruleGroup['rules'] as $config) {
43 13
                    Rule::create($config)->build($subQuery);
44
                }
45 13
            });
46 13
        });
47
48 13
        return $query;
49
    }
50
51
    /**
52
     * Some rules need to be driven by an additional "user" factor, for example play count, liked, or last played
53
     * (basically everything related to interactions).
54
     * For those, we create an additional "user_id" rule.
55
     *
56
     * @param array[] $rules
57
     */
58 1
    public function addRequiresUserRules(array $rules, User $user): array
59
    {
60 1
        foreach ($rules as &$ruleGroup) {
61 1
            $additionalRules = [];
62
63 1
            foreach ($ruleGroup['rules'] as &$config) {
64 1
                foreach (self::RULE_REQUIRES_USER_PREFIXES as $modelPrefix) {
65 1
                    if (starts_with($config['model'], $modelPrefix)) {
66 1
                        $additionalRules[] = $this->createRequireUserRule($user, $modelPrefix);
67
                    }
68
                }
69
            }
70
71
            // make sure all those additional rules are unique.
72 1
            $ruleGroup['rules'] = array_merge($ruleGroup['rules'], collect($additionalRules)->unique('model')->all());
73
        }
74
75 1
        return $rules;
76
    }
77
78 1
    private function createRequireUserRule(User $user, string $modelPrefix): array
79
    {
80
        return [
81 1
            'model' => $modelPrefix.'user_id',
82 1
            'operator' => 'is',
83 1
            'value' => [$user->id],
84
        ];
85
    }
86
}
87