koel/app/Services/SmartPlaylistService.php

89 lines
2.6 KiB
PHP
Raw Normal View History

<?php
namespace App\Services;
use App\Models\Playlist;
use App\Models\Rule;
use App\Models\Song;
use App\Models\User;
use App\Repositories\SongRepository;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use RuntimeException;
class SmartPlaylistService
{
private const RULE_REQUIRES_USER_PREFIXES = ['interactions.'];
private $songRepository;
public function __construct(SongRepository $songRepository)
{
$this->songRepository = $songRepository;
}
2020-12-22 20:11:22 +00:00
/** @return Collection|array<Song> */
public function getSongs(Playlist $playlist): Collection
{
if (!$playlist->is_smart) {
2020-12-22 20:11:22 +00:00
throw new RuntimeException($playlist->name . ' is not a smart playlist.');
}
$rules = $this->addRequiresUserRules($playlist->rules, $playlist->user);
2018-11-18 21:50:03 +00:00
return $this->buildQueryFromRules($rules)->get();
}
2018-11-18 21:50:03 +00:00
public function buildQueryFromRules(array $rules): Builder
{
2018-11-18 21:50:03 +00:00
$query = Song::query();
2018-11-18 21:50:03 +00:00
collect($rules)->each(static function (array $ruleGroup) use ($query): void {
$query->orWhere(static function (Builder $subQuery) use ($ruleGroup): void {
foreach ($ruleGroup['rules'] as $config) {
Rule::create($config)->build($subQuery);
}
});
});
2018-11-18 21:50:03 +00:00
return $query;
}
/**
* Some rules need to be driven by an additional "user" factor, for example play count, liked, or last played
* (basically everything related to interactions).
* For those, we create an additional "user_id" rule.
*
2020-12-22 20:11:22 +00:00
* @return array<mixed>
*/
2018-11-18 21:50:03 +00:00
public function addRequiresUserRules(array $rules, User $user): array
{
2018-11-18 21:50:03 +00:00
foreach ($rules as &$ruleGroup) {
$additionalRules = [];
2018-11-18 21:50:03 +00:00
foreach ($ruleGroup['rules'] as &$config) {
foreach (self::RULE_REQUIRES_USER_PREFIXES as $modelPrefix) {
if (starts_with($config['model'], $modelPrefix)) {
$additionalRules[] = $this->createRequireUserRule($user, $modelPrefix);
}
}
}
2018-11-18 21:50:03 +00:00
// make sure all those additional rules are unique.
$ruleGroup['rules'] = array_merge($ruleGroup['rules'], collect($additionalRules)->unique('model')->all());
}
2018-11-18 21:50:03 +00:00
return $rules;
}
2020-12-22 20:11:22 +00:00
/** @return array<mixed> */
private function createRequireUserRule(User $user, string $modelPrefix): array
{
return [
2020-12-22 20:11:22 +00:00
'model' => $modelPrefix . 'user_id',
'operator' => 'is',
'value' => [$user->id],
];
}
}