mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
feat: support adding collaborative playlists into usr own folder
This commit is contained in:
parent
a11eaeb809
commit
9b7759a492
17 changed files with 284 additions and 60 deletions
|
@ -28,7 +28,7 @@ class PlaylistFolderPlaylistController extends Controller
|
|||
{
|
||||
$this->authorize('own', $playlistFolder);
|
||||
|
||||
$this->service->movePlaylistsToRootLevel(Arr::wrap($request->playlists));
|
||||
$this->service->movePlaylistsToRootLevel($playlistFolder, Arr::wrap($request->playlists));
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Http\Requests\API;
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Rules\AllPlaylistsBelongTo;
|
||||
use App\Rules\AllPlaylistsAreAccessibleBy;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ class PlaylistFolderPlaylistDestroyRequest extends Request
|
|||
'playlists' => [
|
||||
'required',
|
||||
'array',
|
||||
new AllPlaylistsBelongTo($this->user()),
|
||||
new AllPlaylistsAreAccessibleBy($this->user()),
|
||||
Rule::exists(Playlist::class, 'id'),
|
||||
],
|
||||
];
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Http\Requests\API;
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Rules\AllPlaylistsBelongTo;
|
||||
use App\Rules\AllPlaylistsAreAccessibleBy;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ class PlaylistFolderPlaylistStoreRequest extends Request
|
|||
'playlists' => [
|
||||
'required',
|
||||
'array',
|
||||
new AllPlaylistsBelongTo($this->user()),
|
||||
new AllPlaylistsAreAccessibleBy($this->user()),
|
||||
Rule::exists(Playlist::class, 'id'),
|
||||
],
|
||||
];
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class PlaylistResource extends JsonResource
|
||||
|
@ -27,11 +28,14 @@ class PlaylistResource extends JsonResource
|
|||
/** @return array<mixed> */
|
||||
public function toArray($request): array
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $request->user() ?? $this->playlist->user;
|
||||
|
||||
return [
|
||||
'type' => 'playlists',
|
||||
'id' => $this->playlist->id,
|
||||
'name' => $this->playlist->name,
|
||||
'folder_id' => $this->playlist->folder_id,
|
||||
'folder_id' => $this->playlist->getFolderId($user),
|
||||
'user_id' => $this->playlist->user_id,
|
||||
'is_smart' => $this->playlist->is_smart,
|
||||
'is_collaborative' => $this->playlist->is_collaborative,
|
||||
|
|
|
@ -24,8 +24,6 @@ use LogicException;
|
|||
* @property bool $is_smart
|
||||
* @property int $user_id
|
||||
* @property User $user
|
||||
* @property ?string $folder_id
|
||||
* @property ?PlaylistFolder $folder
|
||||
* @property Collection|array<array-key, Song> $songs
|
||||
* @property array<string> $song_ids
|
||||
* @property ?SmartPlaylistRuleGroupCollection $rule_groups
|
||||
|
@ -36,6 +34,7 @@ use LogicException;
|
|||
* @property-read bool $is_collaborative
|
||||
* @property-read ?string $cover The playlist cover's URL
|
||||
* @property-read ?string $cover_path
|
||||
* @property-read Collection|array<array-key, PlaylistFolder> $folders
|
||||
*/
|
||||
class Playlist extends Model
|
||||
{
|
||||
|
@ -53,7 +52,7 @@ class Playlist extends Model
|
|||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
protected $appends = ['is_smart'];
|
||||
protected $with = ['collaborators'];
|
||||
protected $with = ['user', 'collaborators', 'folders'];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
|
@ -74,9 +73,9 @@ class Playlist extends Model
|
|||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function folder(): BelongsTo
|
||||
public function folders(): BelongsToMany
|
||||
{
|
||||
return $this->belongsTo(PlaylistFolder::class);
|
||||
return $this->belongsToMany(PlaylistFolder::class, null, null, 'folder_id');
|
||||
}
|
||||
|
||||
public function collaborationTokens(): HasMany
|
||||
|
@ -128,7 +127,19 @@ class Playlist extends Model
|
|||
|
||||
public function inFolder(PlaylistFolder $folder): bool
|
||||
{
|
||||
return $this->folder_id === $folder->id;
|
||||
return $this->folders->contains($folder);
|
||||
}
|
||||
|
||||
public function getFolder(?User $contextUser = null): ?PlaylistFolder
|
||||
{
|
||||
return $this->folders->firstWhere(
|
||||
fn (PlaylistFolder $folder) => $folder->user->is($contextUser ?? $this->user)
|
||||
);
|
||||
}
|
||||
|
||||
public function getFolderId(?User $user = null): ?string
|
||||
{
|
||||
return $this->getFolder($user)?->id;
|
||||
}
|
||||
|
||||
public function addCollaborator(User $user): void
|
||||
|
@ -138,11 +149,9 @@ class Playlist extends Model
|
|||
}
|
||||
}
|
||||
|
||||
public function hasCollaborator(User $user): bool
|
||||
public function hasCollaborator(User $collaborator): bool
|
||||
{
|
||||
return $this->collaborators->contains(static function (User $collaborator) use ($user): bool {
|
||||
return $collaborator->is($user);
|
||||
});
|
||||
return $this->collaborators->contains(static fn (User $user): bool => $collaborator->is($user));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Collection;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
|
@ -31,9 +31,9 @@ class PlaylistFolder extends Model
|
|||
static::creating(static fn (self $folder) => $folder->id = Str::uuid()->toString());
|
||||
}
|
||||
|
||||
public function playlists(): HasMany
|
||||
public function playlists(): BelongsToMany
|
||||
{
|
||||
return $this->hasMany(Playlist::class, 'folder_id');
|
||||
return $this->belongsToMany(Playlist::class, null, 'folder_id');
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
|
|
|
@ -12,10 +12,21 @@ class PlaylistRepository extends Repository
|
|||
/** @return array<array-key, Playlist>|Collection<Playlist> */
|
||||
public function getAllAccessibleByUser(User $user): Collection
|
||||
{
|
||||
$ownPlaylists = Playlist::query()
|
||||
->where('playlists.user_id', $user->id)
|
||||
->leftJoin('playlist_playlist_folder', 'playlists.id', '=', 'playlist_playlist_folder.playlist_id')
|
||||
->get(['playlists.*', 'playlist_playlist_folder.folder_id']);
|
||||
|
||||
if (License::isCommunity()) {
|
||||
return $user->playlists;
|
||||
return $ownPlaylists;
|
||||
}
|
||||
|
||||
return $user->playlists->merge($user->collaboratedPlaylists);
|
||||
$collaboratedPlaylists = Playlist::query()
|
||||
->join('playlist_collaborators', 'playlists.id', '=', 'playlist_collaborators.playlist_id')
|
||||
->where('playlist_collaborators.user_id', $user->id)
|
||||
->join('playlist_playlist_folder', 'playlists.id', '=', 'playlist_playlist_folder.playlist_id')
|
||||
->get(['playlists.*', 'playlist_playlist_folder.folder_id']);
|
||||
|
||||
return $ownPlaylists->merge($collaboratedPlaylists);
|
||||
}
|
||||
}
|
||||
|
|
34
app/Rules/AllPlaylistsAreAccessibleBy.php
Normal file
34
app/Rules/AllPlaylistsAreAccessibleBy.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Facades\License;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
final class AllPlaylistsAreAccessibleBy implements Rule
|
||||
{
|
||||
public function __construct(private User $user)
|
||||
{
|
||||
}
|
||||
|
||||
/** @param array<int> $value */
|
||||
public function passes($attribute, $value): bool
|
||||
{
|
||||
$accessiblePlaylists = $this->user->playlists;
|
||||
|
||||
if (License::isPlus()) {
|
||||
$accessiblePlaylists = $accessiblePlaylists->merge($this->user->collaboratedPlaylists);
|
||||
}
|
||||
|
||||
return array_diff(Arr::wrap($value), $accessiblePlaylists->pluck('id')->toArray()) === [];
|
||||
}
|
||||
|
||||
public function message(): string
|
||||
{
|
||||
return License::isPlus()
|
||||
? 'Not all playlists are accessible by the user'
|
||||
: 'Not all playlists belong to the user';
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
final class AllPlaylistsBelongTo implements Rule
|
||||
{
|
||||
public function __construct(private User $user)
|
||||
{
|
||||
}
|
||||
|
||||
/** @param array<int> $value */
|
||||
public function passes($attribute, $value): bool
|
||||
{
|
||||
return array_diff(Arr::wrap($value), $this->user->playlists->pluck('id')->toArray()) === [];
|
||||
}
|
||||
|
||||
public function message(): string
|
||||
{
|
||||
return 'Not all playlists belong to the user';
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistFolder;
|
||||
use App\Models\User;
|
||||
|
||||
|
@ -22,11 +21,11 @@ class PlaylistFolderService
|
|||
|
||||
public function addPlaylistsToFolder(PlaylistFolder $folder, array $playlistIds): void
|
||||
{
|
||||
Playlist::query()->whereIn('id', $playlistIds)->update(['folder_id' => $folder->id]);
|
||||
$folder->playlists()->attach($playlistIds);
|
||||
}
|
||||
|
||||
public function movePlaylistsToRootLevel(array $playlistIds): void
|
||||
public function movePlaylistsToRootLevel(PlaylistFolder $folder, array $playlistIds): void
|
||||
{
|
||||
Playlist::query()->whereIn('id', $playlistIds)->update(['folder_id' => null]);
|
||||
$folder->playlists()->detach($playlistIds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,9 +47,10 @@ class PlaylistService
|
|||
'name' => $name,
|
||||
'rules' => $ruleGroups,
|
||||
'own_songs_only' => $ownSongsOnly,
|
||||
'folder_id' => $folder?->id,
|
||||
]);
|
||||
|
||||
$folder?->playlists()->attach($playlist);
|
||||
|
||||
if (!$playlist->is_smart && $songs) {
|
||||
$playlist->addSongs($songs, $user);
|
||||
}
|
||||
|
@ -77,10 +78,11 @@ class PlaylistService
|
|||
$playlist->update([
|
||||
'name' => $name,
|
||||
'rules' => $ruleGroups,
|
||||
'folder_id' => $folder?->id,
|
||||
'own_songs_only' => $ownSongsOnly,
|
||||
]);
|
||||
|
||||
$folder?->playlists()->syncWithoutDetaching($playlist);
|
||||
|
||||
return $playlist;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('playlist_playlist_folder', static function (Blueprint $table): void {
|
||||
$table->string('folder_id', 36)->nullable(false);
|
||||
$table->string('playlist_id', 36)->nullable(false);
|
||||
});
|
||||
|
||||
Schema::table('playlist_playlist_folder', static function (Blueprint $table): void {
|
||||
$table->foreign('folder_id')
|
||||
->references('id')
|
||||
->on('playlist_folders')
|
||||
->cascadeOnDelete()
|
||||
->cascadeOnUpdate();
|
||||
|
||||
$table->foreign('playlist_id')
|
||||
->references('id')
|
||||
->on('playlists')
|
||||
->cascadeOnDelete()
|
||||
->cascadeOnUpdate();
|
||||
|
||||
$table->unique(['folder_id', 'playlist_id']);
|
||||
});
|
||||
|
||||
DB::table('playlists')->whereNotNull('folder_id')->get()->each(static function ($playlist): void {
|
||||
DB::table('playlist_playlist_folder')->insert([
|
||||
'folder_id' => $playlist->folder_id,
|
||||
'playlist_id' => $playlist->id,
|
||||
]);
|
||||
});
|
||||
|
||||
Schema::table('playlists', static function (Blueprint $table): void {
|
||||
if (DB::getDriverName() !== 'sqlite') {
|
||||
$table->dropForeign('playlists_folder_id_foreign');
|
||||
}
|
||||
|
||||
$table->dropColumn('folder_id');
|
||||
});
|
||||
}
|
||||
};
|
74
tests/Feature/KoelPlus/PlaylistFolderTest.php
Normal file
74
tests/Feature/KoelPlus/PlaylistFolderTest.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\KoelPlus;
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistFolder;
|
||||
use Tests\PlusTestCase;
|
||||
|
||||
use function Tests\create_user;
|
||||
|
||||
class PlaylistFolderTest extends PlusTestCase
|
||||
{
|
||||
public function testCollaboratorPuttingPlaylistIntoTheirFolder(): void
|
||||
{
|
||||
$collaborator = create_user();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->create();
|
||||
$playlist->addCollaborator($collaborator);
|
||||
|
||||
/** @var PlaylistFolder $ownerFolder */
|
||||
$ownerFolder = PlaylistFolder::factory()->for($playlist->user)->create();
|
||||
$ownerFolder->playlists()->attach($playlist->id);
|
||||
self::assertTrue($playlist->refresh()->getFolder($playlist->user)?->is($ownerFolder));
|
||||
|
||||
/** @var PlaylistFolder $collaboratorFolder */
|
||||
$collaboratorFolder = PlaylistFolder::factory()->for($collaborator)->create();
|
||||
self::assertNull($playlist->getFolder($collaborator));
|
||||
|
||||
$this->postAs(
|
||||
"api/playlist-folders/$collaboratorFolder->id/playlists",
|
||||
['playlists' => [$playlist->id]],
|
||||
$collaborator
|
||||
)
|
||||
->assertSuccessful();
|
||||
|
||||
self::assertTrue($playlist->fresh()->getFolder($collaborator)?->is($collaboratorFolder));
|
||||
|
||||
// Verify the playlist is in the owner's folder too
|
||||
self::assertTrue($playlist->fresh()->getFolder($playlist->user)?->is($ownerFolder));
|
||||
}
|
||||
|
||||
public function testCollaboratorMovingPlaylistToRootLevel(): void
|
||||
{
|
||||
$collaborator = create_user();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->create();
|
||||
$playlist->addCollaborator($collaborator);
|
||||
self::assertNull($playlist->getFolder($playlist->user));
|
||||
|
||||
/** @var PlaylistFolder $ownerFolder */
|
||||
$ownerFolder = PlaylistFolder::factory()->for($playlist->user)->create();
|
||||
$ownerFolder->playlists()->attach($playlist->id);
|
||||
self::assertTrue($playlist->refresh()->getFolder($playlist->user)?->is($ownerFolder));
|
||||
|
||||
/** @var PlaylistFolder $collaboratorFolder */
|
||||
$collaboratorFolder = PlaylistFolder::factory()->for($collaborator)->create();
|
||||
|
||||
$collaboratorFolder->playlists()->attach($playlist->id);
|
||||
self::assertTrue($playlist->refresh()->getFolder($collaborator)?->is($collaboratorFolder));
|
||||
|
||||
$this->deleteAs(
|
||||
"api/playlist-folders/$collaboratorFolder->id/playlists",
|
||||
['playlists' => [$playlist->id]],
|
||||
$collaborator
|
||||
)
|
||||
->assertSuccessful();
|
||||
|
||||
self::assertNull($playlist->fresh()->getFolder($collaborator));
|
||||
// Verify the playlist is still in the owner's folder
|
||||
self::assertTrue($playlist->getFolder($playlist->user)?->is($ownerFolder));
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ class PlaylistTest extends PlusTestCase
|
|||
self::assertTrue($playlist->ownedBy($user));
|
||||
self::assertTrue($playlist->is_smart);
|
||||
self::assertCount(1, $playlist->rule_groups);
|
||||
self::assertNull($playlist->folder_id);
|
||||
self::assertNull($playlist->getFolderId());
|
||||
self::assertTrue($rule->equals($playlist->rule_groups[0]->rules[0]));
|
||||
self::assertTrue($playlist->own_songs_only);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests\Feature;
|
||||
|
||||
use App\Http\Resources\PlaylistFolderResource;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistFolder;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -73,4 +74,68 @@ class PlaylistFolderTest extends TestCase
|
|||
|
||||
self::assertModelExists($folder);
|
||||
}
|
||||
|
||||
public function testMovingPlaylistToFolder(): void
|
||||
{
|
||||
/** @var PlaylistFolder $folder */
|
||||
$folder = PlaylistFolder::factory()->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->for($folder->user)->create();
|
||||
self::assertNull($playlist->getFolderId($folder->user));
|
||||
|
||||
$this->postAs("api/playlist-folders/$folder->id/playlists", ['playlists' => [$playlist->id]], $folder->user)
|
||||
->assertSuccessful();
|
||||
|
||||
self::assertTrue($playlist->fresh()->getFolder($folder->user)->is($folder));
|
||||
}
|
||||
|
||||
public function testUnauthorizedMovingPlaylistToFolderIsNotAllowed(): void
|
||||
{
|
||||
/** @var PlaylistFolder $folder */
|
||||
$folder = PlaylistFolder::factory()->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->for($folder->user)->create();
|
||||
self::assertNull($playlist->getFolderId($folder->user));
|
||||
|
||||
$this->postAs("api/playlist-folders/$folder->id/playlists", ['playlists' => [$playlist->id]])
|
||||
->assertUnprocessable();
|
||||
|
||||
self::assertNull($playlist->fresh()->getFolder($folder->user));
|
||||
}
|
||||
|
||||
public function testMovingPlaylistToRootLevel(): void
|
||||
{
|
||||
/** @var PlaylistFolder $folder */
|
||||
$folder = PlaylistFolder::factory()->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->for($folder->user)->create();
|
||||
|
||||
$folder->playlists()->attach($playlist->id);
|
||||
self::assertTrue($playlist->refresh()->getFolder($folder->user)->is($folder));
|
||||
|
||||
$this->deleteAs("api/playlist-folders/$folder->id/playlists", ['playlists' => [$playlist->id]], $folder->user)
|
||||
->assertSuccessful();
|
||||
|
||||
self::assertNull($playlist->fresh()->getFolder($folder->user));
|
||||
}
|
||||
|
||||
public function testUnauthorizedMovingPlaylistToRootLevelIsNotAllowed(): void
|
||||
{
|
||||
/** @var PlaylistFolder $folder */
|
||||
$folder = PlaylistFolder::factory()->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->for($folder->user)->create();
|
||||
|
||||
$folder->playlists()->attach($playlist->id);
|
||||
self::assertTrue($playlist->refresh()->getFolder($folder->user)->is($folder));
|
||||
|
||||
$this->deleteAs("api/playlist-folders/$folder->id/playlists", ['playlists' => [$playlist->id]])
|
||||
->assertUnprocessable();
|
||||
|
||||
self::assertTrue($playlist->refresh()->getFolder($folder->user)->is($folder));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class PlaylistTest extends TestCase
|
|||
|
||||
self::assertSame('Foo Bar', $playlist->name);
|
||||
self::assertTrue($playlist->ownedBy($user));
|
||||
self::assertNull($playlist->folder_id);
|
||||
self::assertNull($playlist->getFolder());
|
||||
self::assertEqualsCanonicalizing($songs->pluck('id')->all(), $playlist->songs->pluck('id')->all());
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ class PlaylistTest extends TestCase
|
|||
self::assertTrue($playlist->ownedBy($user));
|
||||
self::assertTrue($playlist->is_smart);
|
||||
self::assertCount(1, $playlist->rule_groups);
|
||||
self::assertNull($playlist->folder_id);
|
||||
self::assertNull($playlist->getFolder());
|
||||
self::assertTrue($rule->equals($playlist->rule_groups[0]->rules[0]));
|
||||
}
|
||||
|
||||
|
|
|
@ -45,11 +45,13 @@ class PlaylistFolderServiceTest extends TestCase
|
|||
|
||||
public function testAddPlaylistsToFolder(): void
|
||||
{
|
||||
$user = create_user();
|
||||
|
||||
/** @var Collection|array<array-key, Playlist> $playlists */
|
||||
$playlists = Playlist::factory()->count(3)->create();
|
||||
$playlists = Playlist::factory()->for($user)->count(3)->create();
|
||||
|
||||
/** @var PlaylistFolder $folder */
|
||||
$folder = PlaylistFolder::factory()->create();
|
||||
$folder = PlaylistFolder::factory()->for($user)->create();
|
||||
|
||||
$this->service->addPlaylistsToFolder($folder, $playlists->pluck('id')->all());
|
||||
|
||||
|
@ -62,12 +64,13 @@ class PlaylistFolderServiceTest extends TestCase
|
|||
$folder = PlaylistFolder::factory()->create();
|
||||
|
||||
/** @var Collection|array<array-key, Playlist> $playlists */
|
||||
$playlists = Playlist::factory()->count(3)->for($folder, 'folder')->create();
|
||||
$playlists = Playlist::factory()->count(3)->create();
|
||||
$folder->playlists()->attach($playlists->pluck('id')->all());
|
||||
|
||||
$this->service->movePlaylistsToRootLevel($playlists->pluck('id')->all());
|
||||
$this->service->movePlaylistsToRootLevel($folder, $playlists->pluck('id')->all());
|
||||
|
||||
self::assertCount(0, $folder->playlists);
|
||||
|
||||
$playlists->each(static fn (Playlist $playlist) => self::assertNull($playlist->refresh()->folder_id));
|
||||
$playlists->each(static fn (Playlist $playlist) => self::assertNull($playlist->refresh()->getFolder()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue