diff --git a/app/Http/Controllers/API/Interaction/BatchLikeController.php b/app/Http/Controllers/API/Interaction/BatchLikeController.php index ead49387..da8d2482 100644 --- a/app/Http/Controllers/API/Interaction/BatchLikeController.php +++ b/app/Http/Controllers/API/Interaction/BatchLikeController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\API\Interaction; use App\Http\Requests\API\BatchInteractionRequest; -use App\Models\Interaction; use Illuminate\Http\JsonResponse; class BatchLikeController extends Controller @@ -17,7 +16,9 @@ class BatchLikeController extends Controller */ public function store(BatchInteractionRequest $request) { - return response()->json(Interaction::batchLike((array) $request->songs, $request->user())); + $interactions = $this->interactionService->batchLike((array) $request->songs, $request->user()); + + return response()->json($interactions); } /** @@ -29,6 +30,8 @@ class BatchLikeController extends Controller */ public function destroy(BatchInteractionRequest $request) { - return response()->json(Interaction::batchUnlike((array) $request->songs, $request->user())); + $this->interactionService->batchUnlike((array) $request->songs, $request->user()); + + return response()->json(); } } diff --git a/app/Http/Controllers/API/Interaction/Controller.php b/app/Http/Controllers/API/Interaction/Controller.php index 8d47c86d..e15f6d97 100644 --- a/app/Http/Controllers/API/Interaction/Controller.php +++ b/app/Http/Controllers/API/Interaction/Controller.php @@ -3,7 +3,14 @@ namespace App\Http\Controllers\API\Interaction; use App\Http\Controllers\Controller as BaseController; +use App\Services\InteractionService; class Controller extends BaseController { + protected $interactionService; + + public function __construct(InteractionService $interactionService) + { + $this->interactionService = $interactionService; + } } diff --git a/app/Http/Controllers/API/Interaction/LikeController.php b/app/Http/Controllers/API/Interaction/LikeController.php index 77f59522..0b542eb7 100644 --- a/app/Http/Controllers/API/Interaction/LikeController.php +++ b/app/Http/Controllers/API/Interaction/LikeController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\API\Interaction; use App\Http\Requests\API\SongLikeRequest; -use App\Models\Interaction; use Illuminate\Http\JsonResponse; class LikeController extends Controller @@ -17,6 +16,6 @@ class LikeController extends Controller */ public function store(SongLikeRequest $request) { - return response()->json(Interaction::toggleLike($request->song, $request->user())); + return response()->json($this->interactionService->toggleLike($request->song, $request->user())); } } diff --git a/app/Http/Controllers/API/Interaction/PlayCountController.php b/app/Http/Controllers/API/Interaction/PlayCountController.php index 9da7ab8c..810c9ec3 100644 --- a/app/Http/Controllers/API/Interaction/PlayCountController.php +++ b/app/Http/Controllers/API/Interaction/PlayCountController.php @@ -4,7 +4,6 @@ namespace App\Http\Controllers\API\Interaction; use App\Events\SongStartedPlaying; use App\Http\Requests\API\Interaction\StorePlayCountRequest; -use App\Models\Interaction; use Illuminate\Http\JsonResponse; class PlayCountController extends Controller @@ -18,7 +17,8 @@ class PlayCountController extends Controller */ public function store(StorePlayCountRequest $request) { - $interaction = Interaction::increasePlayCount($request->song, $request->user()); + $interaction = $this->interactionService->increasePlayCount($request->song, $request->user()); + if ($interaction) { event(new SongStartedPlaying($interaction->song, $interaction->user)); } diff --git a/app/Models/Interaction.php b/app/Models/Interaction.php index 4265e422..bffbc18c 100644 --- a/app/Models/Interaction.php +++ b/app/Models/Interaction.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Events\SongLikeToggled; use App\Traits\CanFilterByUser; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -46,93 +45,4 @@ class Interaction extends Model { return $this->belongsTo(Song::class); } - - /** - * Increase the number of times a song is played by a user. - * - * @param string $songId - * @param User $user - * - * @return Interaction - */ - public static function increasePlayCount($songId, User $user) - { - return tap(self::firstOrCreate([ - 'song_id' => $songId, - 'user_id' => $user->id, - ]), function (Interaction $interaction) { - if (!$interaction->exists) { - $interaction->liked = false; - } - - $interaction->play_count++; - $interaction->save(); - }); - } - - /** - * Like or unlike a song on behalf of a user. - * - * @param string $songId - * @param User $user - * - * @return Interaction - */ - public static function toggleLike($songId, User $user) - { - return tap(self::firstOrCreate([ - 'song_id' => $songId, - 'user_id' => $user->id, - ]), function (Interaction $interaction) { - $interaction->liked = !$interaction->liked; - $interaction->save(); - - event(new SongLikeToggled($interaction)); - }); - } - - /** - * Like several songs at once. - * - * @param array $songIds - * @param User $user - * - * @return array - */ - public static function batchLike(array $songIds, User $user) - { - return collect($songIds)->map(function ($songId) use ($user) { - return tap(self::firstOrCreate([ - 'song_id' => $songId, - 'user_id' => $user->id, - ]), function (Interaction $interaction) { - if (!$interaction->exists) { - $interaction->play_count = 0; - } - - $interaction->liked = true; - $interaction->save(); - - event(new SongLikeToggled($interaction)); - }); - })->all(); - } - - /** - * Unlike several songs at once. - * - * @param array $songIds - * @param User $user - * - * @return int - */ - public static function batchUnlike(array $songIds, User $user) - { - self::whereIn('song_id', $songIds)->whereUserId($user->id)->get()->each(function (Interaction $interaction) { - $interaction->liked = false; - $interaction->save(); - - event(new SongLikeToggled($interaction)); - }); - } } diff --git a/app/Services/InteractionService.php b/app/Services/InteractionService.php new file mode 100644 index 00000000..7ade14b9 --- /dev/null +++ b/app/Services/InteractionService.php @@ -0,0 +1,109 @@ +interaction = $interaction; + } + + /** + * Increase the number of times a song is played by a user. + * + * @param string $songId + * @param User $user + * + * @return Interaction The affected Interaction object + */ + public function increasePlayCount($songId, User $user) + { + return tap($this->interaction->firstOrCreate([ + 'song_id' => $songId, + 'user_id' => $user->id, + ]), static function (Interaction $interaction) { + if (!$interaction->exists) { + $interaction->liked = false; + } + + $interaction->play_count++; + $interaction->save(); + }); + } + + /** + * Like or unlike a song on behalf of a user. + * + * @param string $songId + * @param User $user + * + * @return Interaction The affected Interaction object. + */ + public function toggleLike($songId, User $user) + { + return tap($this->interaction->firstOrCreate([ + 'song_id' => $songId, + 'user_id' => $user->id, + ]), static function (Interaction $interaction) { + $interaction->liked = !$interaction->liked; + $interaction->save(); + + event(new SongLikeToggled($interaction)); + }); + } + + /** + * Like several songs at once as a user. + * + * @param array $songIds + * @param User $user + * + * @return array The array of Interaction objects. + */ + public function batchLike(array $songIds, User $user) + { + return collect($songIds)->map(function ($songId) use ($user) { + return tap($this->interaction->firstOrCreate([ + 'song_id' => $songId, + 'user_id' => $user->id, + ]), static function (Interaction $interaction) { + if (!$interaction->exists) { + $interaction->play_count = 0; + } + + $interaction->liked = true; + $interaction->save(); + + event(new SongLikeToggled($interaction)); + }); + })->all(); + } + + /** + * Unlike several songs at once. + * + * @param array $songIds + * @param User $user + */ + public function batchUnlike(array $songIds, User $user) + { + $this->interaction + ->whereIn('song_id', $songIds) + ->where('user_id', $user->id) + ->get() + ->each(static function (Interaction $interaction) { + $interaction->liked = false; + $interaction->save(); + + event(new SongLikeToggled($interaction)); + } + ); + } +} diff --git a/tests/Integration/Services/InteractionServiceTest.php b/tests/Integration/Services/InteractionServiceTest.php new file mode 100644 index 00000000..c76c30f7 --- /dev/null +++ b/tests/Integration/Services/InteractionServiceTest.php @@ -0,0 +1,100 @@ +interactionService = new InteractionService(new Interaction()); + } + + /** @test */ + public function it_increases_a_songs_play_count() + { + /** @var Interaction $interaction */ + $interaction = factory(Interaction::class)->create(); + + $this->interactionService->increasePlayCount($interaction->song, $interaction->user); + + /** @var Interaction $interaction */ + $updatedInteraction = Interaction::find($interaction->id); + self::assertEquals($interaction->play_count + 1, $updatedInteraction->play_count); + } + + /** + * @test + * + * @throws Exception + */ + public function it_toggles_like_status() + { + $this->expectsEvents(SongLikeToggled::class); + + $interaction = factory(Interaction::class)->create(); + + $this->interactionService->toggleLike($interaction->song, $interaction->user); + + /** @var Interaction $interaction */ + $updatedInteraction = Interaction::find($interaction->id); + self::assertNotSame($interaction->liked, $updatedInteraction->liked); + } + + /** + * @test + * + * @throws Exception + */ + public function user_can_like_multiple_songs_at_once() + { + $this->expectsEvents(SongLikeToggled::class); + + /** @var Collection $songs */ + $songs = factory(Song::class, 2)->create(); + $user = factory(User::class)->create(); + + $this->interactionService->batchLike($songs->pluck('id')->all(), $user); + + $songs->each(static function (Song $song) use ($user) { + self::assertTrue(Interaction::whereSongIdAndUserId($song->id, $user->id)->first()->liked); + }); + } + + /** + * @test + * + * @throws Exception + */ + public function user_can_unlike_multiple_songs_at_once() + { + $this->expectsEvents(SongLikeToggled::class); + + $user = factory(User::class)->create(); + /** @var Collection $interactions */ + $interactions = factory(Interaction::class, 3)->create([ + 'user_id' => $user->id, + 'liked' => true, + ]); + + $this->interactionService->batchUnlike($interactions->pluck('song.id')->all(), $user); + + $interactions->each(static function (Interaction $interaction) { + self::assertFalse(Interaction::find($interaction->id)->liked); + }); + } +}