Timeline 👉 2nd MVP
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\API\DataProvider;
|
||||
|
||||
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
|
||||
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
|
||||
use App\API\Resources\Timeline;
|
||||
use Elasticsearch\Client as Elasticsearch;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
final class TimelineDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Elasticsearch $elasticsearch
|
||||
) {
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): Timeline
|
||||
{
|
||||
$timeline = new Timeline();
|
||||
$timeline->id = Uuid::fromString($id);
|
||||
$timeline->cheeps = [];
|
||||
|
||||
$indexName = 'timelines_' . (string)$id;
|
||||
|
||||
if (!$this->elasticsearch->indices()->exists(['index' => $indexName])) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
$result = $this->elasticsearch->search([
|
||||
'index' => $indexName,
|
||||
'body' => [
|
||||
'sort' => [
|
||||
'cheep_date' => ['order' => 'desc']
|
||||
],
|
||||
'query' => [
|
||||
'match_all' => new \stdClass()
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$timeline->cheeps = $result['hits']['hits'];
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
|
||||
{
|
||||
return Timeline::class === $resourceClass;
|
||||
}
|
||||
}
|
||||
29
src/App/Controller/GetTimelineController.php
Normal file
29
src/App/Controller/GetTimelineController.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Cheeper\Application\Query\Timeline\Timeline;
|
||||
use Cheeper\Chapter6\Application\Query\QueryBus;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
final class GetTimelineController extends AbstractController
|
||||
{
|
||||
#[Route(path: "/api/timelines/{id}", methods: ["GET"])]
|
||||
public function __invoke(string $id, QueryBus $queryBus, SerializerInterface $serializer): Response
|
||||
{
|
||||
$timeline = $queryBus->query(
|
||||
Timeline::fromArray(['authorId' => $id])
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
$serializer->serialize($timeline, 'json'),
|
||||
json: true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application\Query;
|
||||
namespace Cheeper\Application\Query\Timeline;
|
||||
|
||||
use Cheeper\Chapter6\Application\Query\Query;
|
||||
|
||||
//snippet timeline-query
|
||||
final class Timeline
|
||||
final class Timeline implements Query
|
||||
{
|
||||
public static function fromArray(array $array): self
|
||||
{
|
||||
20
src/Cheeper/Application/Query/Timeline/TimelineHandler.php
Normal file
20
src/Cheeper/Application/Query/Timeline/TimelineHandler.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application\Query\Timeline;
|
||||
|
||||
final class TimelineHandler
|
||||
{
|
||||
public function __construct(
|
||||
private TimelineReadLayer $timelineReadLayer
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(Timeline $query): TimelineResponse
|
||||
{
|
||||
return new TimelineResponse(
|
||||
$this->timelineReadLayer->byAuthorId($query->authorId())
|
||||
);
|
||||
}
|
||||
}
|
||||
10
src/Cheeper/Application/Query/Timeline/TimelineReadLayer.php
Normal file
10
src/Cheeper/Application/Query/Timeline/TimelineReadLayer.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application\Query\Timeline;
|
||||
|
||||
interface TimelineReadLayer
|
||||
{
|
||||
public function byAuthorId(string $authorId): array;
|
||||
}
|
||||
13
src/Cheeper/Application/Query/Timeline/TimelineResponse.php
Normal file
13
src/Cheeper/Application/Query/Timeline/TimelineResponse.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Application\Query\Timeline;
|
||||
|
||||
final class TimelineResponse
|
||||
{
|
||||
public function __construct(
|
||||
public array $cheeps
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cheeper\Infrastructure\Query;
|
||||
|
||||
use Elasticsearch\Client;
|
||||
use Cheeper\Application\Query\Timeline\TimelineReadLayer;
|
||||
|
||||
final class ElasticsearchTimelineReadLayer implements TimelineReadLayer
|
||||
{
|
||||
public function __construct(
|
||||
private Client $elasticsearch
|
||||
) {
|
||||
}
|
||||
|
||||
public function byAuthorId(string $authorId): array
|
||||
{
|
||||
$indexName = 'timelines_' . $authorId;
|
||||
|
||||
if (!$this->elasticsearch->indices()->exists(['index' => $indexName])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = $this->elasticsearch->search([
|
||||
'index' => $indexName,
|
||||
'body' => [
|
||||
'sort' => [
|
||||
'cheep_date' => ['order' => 'desc']
|
||||
],
|
||||
'query' => [
|
||||
'match_all' => new \stdClass()
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
return $result['hits']['hits'] ?? [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user