Refactor to a UseCase and some value objects
This commit is contained in:
parent
ba75e22439
commit
5aa1f79279
|
@ -1,18 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Collection;
|
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
final class EventCollection extends Collection
|
|
||||||
{
|
|
||||||
public function excludeEventHosts(): self
|
|
||||||
{
|
|
||||||
return (new self($this->items))->filter(function (array $rsvp): bool {
|
|
||||||
return !$rsvp['member']['event_context']['host'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
36
src/Collection/RsvpCollection.php
Normal file
36
src/Collection/RsvpCollection.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Collection;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
final class RsvpCollection extends Collection
|
||||||
|
{
|
||||||
|
private const RESPONSE_ATTENDING = 'yes';
|
||||||
|
|
||||||
|
public function excludeEventHosts(): self
|
||||||
|
{
|
||||||
|
return (new self($this->items))->filter(
|
||||||
|
function (array $rsvp): bool {
|
||||||
|
return !$rsvp['member']['event_context']['host'];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onlyAttending(): self
|
||||||
|
{
|
||||||
|
return (new self($this->items))->filter(
|
||||||
|
function (array $rsvp): bool {
|
||||||
|
return $rsvp['response'] == self::RESPONSE_ATTENDING;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNames(): self
|
||||||
|
{
|
||||||
|
return (new self($this->items))->pluck('member.name')->sort();
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,9 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Command;
|
namespace App\Command;
|
||||||
|
|
||||||
use App\Collection\EventCollection;
|
use App\Collection\RsvpCollection;
|
||||||
|
use App\UseCase\FindTheWinner;
|
||||||
|
use App\ValueObject\Winner;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
@ -17,6 +19,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
final class GetRaffleWinnerCommand extends Command
|
final class GetRaffleWinnerCommand extends Command
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $defaultName = 'app:get-raffle-winner';
|
protected static $defaultName = 'app:get-raffle-winner';
|
||||||
|
|
||||||
private HttpClientInterface $client;
|
private HttpClientInterface $client;
|
||||||
|
@ -58,91 +61,39 @@ final class GetRaffleWinnerCommand extends Command
|
||||||
OutputInterface $output
|
OutputInterface $output
|
||||||
): int {
|
): int {
|
||||||
$io = new SymfonyStyle($input, $output);
|
$io = new SymfonyStyle($input, $output);
|
||||||
$eventId = (int) $input->getArgument('event_id');
|
$eventId = (int)$input->getArgument('event_id');
|
||||||
|
|
||||||
$this->retrieveEventData($eventId);
|
$result = (new FindTheWinner(
|
||||||
$this->retrieveRsvps($eventId);
|
$this->client,
|
||||||
$this->pickWinner();
|
$this->cache,
|
||||||
|
$eventId
|
||||||
|
))->__invoke();
|
||||||
|
|
||||||
$io->title(sprintf(
|
$event = $result->getEvent();
|
||||||
'%s - %s',
|
$io->title($event->getName());
|
||||||
$this->eventData['group']['name'],
|
$io->text($event->getLink());
|
||||||
$this->eventData['name']
|
|
||||||
));
|
|
||||||
|
|
||||||
$io->text(rtrim($this->eventData['link'], '/'));
|
$io->section(
|
||||||
|
sprintf(
|
||||||
$io->section(sprintf('%s \'yes\' RSVPs (excluding hosts)', $this->yesRsvps->count()));
|
'%s \'yes\' RSVPs (excluding hosts)',
|
||||||
$io->listing($this->yesRsvps->pluck('member.name')->sort()->toArray());
|
$result->getRsvps()->count()
|
||||||
$io->writeln(
|
)
|
||||||
sprintf('Winner: %s', $this->winner['member']['name'])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->openWinnerPhoto($io);
|
$io->listing($result->getRsvps()->getNames()->toArray());
|
||||||
|
|
||||||
|
$io->writeln(
|
||||||
|
sprintf('Winner: %s', $result->getWinner()->getName())
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->openWinnerPhoto($result->getWinner(), $io);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function retrieveEventData(int $eventId): void
|
private function openWinnerPhoto(Winner $winner, SymfonyStyle $io): void
|
||||||
{
|
{
|
||||||
$eventData = $this->cache->getItem(sprintf('event.%d', $eventId));
|
if ($photo = $winner->getPhoto()) {
|
||||||
|
|
||||||
if (!$eventData->isHit()) {
|
|
||||||
$response = $this->client->request(
|
|
||||||
'GET',
|
|
||||||
sprintf(
|
|
||||||
'https://api.meetup.com/%s/events/%d',
|
|
||||||
'php-south-wales',
|
|
||||||
$eventId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$eventData->expiresAfter(DateInterval::createFromDateString('1 hour'));
|
|
||||||
$this->eventData = $response->toArray();
|
|
||||||
$this->cache->save($eventData->set($this->eventData));
|
|
||||||
} else {
|
|
||||||
$this->eventData = $eventData->get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function retrieveRsvps(int $eventId): void
|
|
||||||
{
|
|
||||||
$rsvps = $this->cache->getItem(sprintf('rsvps.%d', $eventId));
|
|
||||||
|
|
||||||
if (!$rsvps->isHit()) {
|
|
||||||
$response = $this->client->request(
|
|
||||||
'GET',
|
|
||||||
vsprintf(
|
|
||||||
'https://api.meetup.com/%s/events/%d/rsvps',
|
|
||||||
[
|
|
||||||
'php-south-wales',
|
|
||||||
$eventId,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->rsvps = EventCollection::make($response->toArray())
|
|
||||||
->excludeEventHosts();
|
|
||||||
|
|
||||||
$rsvps->expiresAfter(DateInterval::createFromDateString('1 hour'));
|
|
||||||
$this->cache->save($rsvps->set($this->rsvps));
|
|
||||||
} else {
|
|
||||||
$this->rsvps = $rsvps->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->yesRsvps = $this->rsvps->filter(function (array $rsvp): bool {
|
|
||||||
return $rsvp['response'] == 'yes';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function pickWinner(): void
|
|
||||||
{
|
|
||||||
$this->winner = $this->yesRsvps->random(1)->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function openWinnerPhoto(SymfonyStyle $io): void
|
|
||||||
{
|
|
||||||
if ($photo = $this->winner['member']['photo']['photo_link'] ?? NULL) {
|
|
||||||
$io->write($photo);
|
$io->write($photo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
106
src/UseCase/FindTheWinner.php
Normal file
106
src/UseCase/FindTheWinner.php
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\UseCase;
|
||||||
|
|
||||||
|
use App\Collection\RsvpCollection;
|
||||||
|
use App\ValueObject\Event;
|
||||||
|
use App\ValueObject\Result;
|
||||||
|
use App\ValueObject\Winner;
|
||||||
|
use DateInterval;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
final class FindTheWinner implements UseCaseInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
private int $eventId;
|
||||||
|
|
||||||
|
private HttpClientInterface $client;
|
||||||
|
|
||||||
|
private CacheInterface $cache;
|
||||||
|
|
||||||
|
private RsvpCollection $yesRsvps;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
HttpClientInterface $client,
|
||||||
|
CacheInterface $cache,
|
||||||
|
int $eventId
|
||||||
|
) {
|
||||||
|
$this->eventId = $eventId;
|
||||||
|
$this->client = $client;
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(): Result
|
||||||
|
{
|
||||||
|
$eventData = $this->retrieveEventData();
|
||||||
|
$rsvps = $this->retrieveRsvps();
|
||||||
|
$winner = $this->pickWinner($rsvps);
|
||||||
|
|
||||||
|
return new Result(
|
||||||
|
Winner::createFromArray($winner),
|
||||||
|
Event::createFromArray($eventData),
|
||||||
|
$rsvps
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function retrieveEventData(): array
|
||||||
|
{
|
||||||
|
$eventData = $this->cache->getItem(sprintf('event.%d', $this->eventId));
|
||||||
|
|
||||||
|
if (!$eventData->isHit()) {
|
||||||
|
$response = $this->client->request(
|
||||||
|
'GET',
|
||||||
|
sprintf(
|
||||||
|
'https://api.meetup.com/%s/events/%d',
|
||||||
|
'php-south-wales',
|
||||||
|
$this->eventId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$eventData->expiresAfter(
|
||||||
|
DateInterval::createFromDateString('1 hour')
|
||||||
|
);
|
||||||
|
$return = $response->toArray();
|
||||||
|
$this->cache->save($eventData->set($return));
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
} else {
|
||||||
|
return $eventData->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function retrieveRsvps(): RsvpCollection
|
||||||
|
{
|
||||||
|
$rsvps = $this->cache->getItem(sprintf('rsvps.%d', $this->eventId));
|
||||||
|
|
||||||
|
if (!$rsvps->isHit()) {
|
||||||
|
$response = $this->client->request(
|
||||||
|
'GET',
|
||||||
|
sprintf(
|
||||||
|
'https://api.meetup.com/%s/events/%d/rsvps',
|
||||||
|
'php-south-wales',
|
||||||
|
$this->eventId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$filteredRsvps = RsvpCollection::make($response->toArray())
|
||||||
|
->excludeEventHosts()
|
||||||
|
->onlyAttending();
|
||||||
|
|
||||||
|
$rsvps->expiresAfter(DateInterval::createFromDateString('1 hour'));
|
||||||
|
$this->cache->save($rsvps->set($filteredRsvps));
|
||||||
|
|
||||||
|
return $filteredRsvps;
|
||||||
|
} else {
|
||||||
|
return $rsvps->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function pickWinner(RsvpCollection $rsvps): array
|
||||||
|
{
|
||||||
|
return $rsvps->random(1)->first()['member'];
|
||||||
|
}
|
||||||
|
}
|
21
src/UseCase/UseCaseInterface.php
Normal file
21
src/UseCase/UseCaseInterface.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\UseCase;
|
||||||
|
|
||||||
|
use App\ValueObject\Result;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
interface UseCaseInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
HttpClientInterface $client,
|
||||||
|
CacheInterface $cache,
|
||||||
|
int $eventId
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __invoke(): Result;
|
||||||
|
}
|
44
src/ValueObject/Event.php
Normal file
44
src/ValueObject/Event.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\ValueObject;
|
||||||
|
|
||||||
|
use App\Collection\RsvpCollection;
|
||||||
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
private string $name;
|
||||||
|
|
||||||
|
private string $link;
|
||||||
|
|
||||||
|
public static function createFromArray(array $data)
|
||||||
|
{
|
||||||
|
return new static($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLink(): string
|
||||||
|
{
|
||||||
|
return rtrim($this->link, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function __construct(array $data)
|
||||||
|
{
|
||||||
|
[
|
||||||
|
'name' => $name,
|
||||||
|
'link' => $link,
|
||||||
|
] = $data;
|
||||||
|
|
||||||
|
$this->name = $name;
|
||||||
|
$this->link = $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRsvps(): Collection
|
||||||
|
{
|
||||||
|
return new Collection();
|
||||||
|
}
|
||||||
|
}
|
42
src/ValueObject/Result.php
Normal file
42
src/ValueObject/Result.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\ValueObject;
|
||||||
|
|
||||||
|
use App\Collection\RsvpCollection;
|
||||||
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
|
final class Result
|
||||||
|
{
|
||||||
|
private Winner $winner;
|
||||||
|
|
||||||
|
private Event $event;
|
||||||
|
|
||||||
|
private Collection $rsvps;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Winner $winner,
|
||||||
|
Event $event,
|
||||||
|
RsvpCollection $rsvps
|
||||||
|
) {
|
||||||
|
$this->winner = $winner;
|
||||||
|
$this->event = $event;
|
||||||
|
$this->rsvps = $rsvps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWinner(): Winner
|
||||||
|
{
|
||||||
|
return $this->winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEvent(): Event
|
||||||
|
{
|
||||||
|
return $this->event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRsvps(): RsvpCollection
|
||||||
|
{
|
||||||
|
return $this->rsvps;
|
||||||
|
}
|
||||||
|
}
|
39
src/ValueObject/Winner.php
Normal file
39
src/ValueObject/Winner.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\ValueObject;
|
||||||
|
|
||||||
|
final class Winner
|
||||||
|
{
|
||||||
|
|
||||||
|
private string $name;
|
||||||
|
|
||||||
|
private string $photo;
|
||||||
|
|
||||||
|
public static function createFromArray(array $data): self
|
||||||
|
{
|
||||||
|
return new static($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function __construct(array $data)
|
||||||
|
{
|
||||||
|
[
|
||||||
|
'name' => $name,
|
||||||
|
'photo' => $photo,
|
||||||
|
] = $data;
|
||||||
|
|
||||||
|
$this->name = $name;
|
||||||
|
$this->photo = $photo['photo_link'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPhoto(): ?string
|
||||||
|
{
|
||||||
|
return $this->photo ?? null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue