SmartPlaylistService::createRequireUserRule()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 2
crap 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