Use a service for Interaction

This commit is contained in:
Phan An 2018-08-18 14:27:26 +02:00
parent 181357f3de
commit 1d5c8e84b6
7 changed files with 225 additions and 97 deletions

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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()));
}
}

View file

@ -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));
}

View file

@ -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));
});
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Services;
use App\Events\SongLikeToggled;
use App\Models\Interaction;
use App\Models\User;
class InteractionService
{
private $interaction;
public function __construct(Interaction $interaction)
{
$this->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));
}
);
}
}

View file

@ -0,0 +1,100 @@
<?php
namespace Tests\Integration\Services;
use App\Events\SongLikeToggled;
use App\Models\Interaction;
use App\Models\Song;
use App\Models\User;
use App\Services\InteractionService;
use Exception;
use Illuminate\Support\Collection;
use Tests\TestCase;
class InteractionServiceTest extends TestCase
{
/**
* @var InteractionService
*/
private $interactionService;
public function setUp()
{
parent::setUp();
$this->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);
});
}
}