producers: include additional details + refactor search

This commit is contained in:
Irfan 2022-07-27 23:15:53 +05:00
parent 3c12739067
commit 76291bc9af
11 changed files with 735 additions and 131 deletions

View File

@ -3,11 +3,16 @@
namespace App\Http\Controllers\V4DB;
use App\Anime;
use App\Http\Resources\V4\ExternalLinksResource;
use App\Producers;
use App\Http\HttpHelper;
use App\Http\HttpResponse;
use App\Http\QueryBuilder\SearchQueryBuilderProducer;
use App\Http\Resources\V4\AnimeCollection;
use App\Http\Resources\V4\ProducerCollection;
use App\Producer;
use Illuminate\Http\Request;
use Jikan\Model\Producer\Producer;
use MongoDB\BSON\UTCDateTime;
class ProducerController extends Controller
{
@ -17,84 +22,259 @@ class ProducerController extends Controller
/**
* @OA\Get(
* path="/producers",
* operationId="getProducers",
* path="/producers/{id}",
* operationId="getProducerById",
* tags={"producers"},
*
* @OA\Parameter(ref="#/components/parameters/page"),
* @OA\Parameter(ref="#/components/parameters/limit"),
*
* @OA\Parameter(
* name="q",
* in="query",
* @OA\Schema(type="string")
* ),
*
* @OA\Parameter(
* name="order_by",
* in="query",
* @OA\Schema(ref="#/components/schemas/producers_query_orderby")
* ),
*
* @OA\Parameter(
* name="sort",
* in="query",
* @OA\Schema(ref="#/components/schemas/search_query_sort")
* ),
*
* @OA\Parameter(
* name="letter",
* in="query",
* description="Return entries starting with the given letter",
* @OA\Schema(type="string")
* name="id",
* in="path",
* required=true,
* @OA\Schema(type="integer")
* ),
*
* @OA\Response(
* response="200",
* description="Returns producers collection",
* description="Returns producer resource",
* @OA\JsonContent(
* ref="#/components/schemas/producers"
* @OA\Property(
* property="data",
* ref="#/components/schemas/producer"
* )
* )
* ),
*
* @OA\Response(
* response="400",
* description="Error: Bad request. When required parameters were not supplied.",
* ),
* )
*/
public function main(Request $request)
public function main(Request $request, int $id)
{
$page = $request->get('page') ?? 1;
$limit = $request->get('limit') ?? self::MAX_RESULTS_PER_PAGE;
$results = Producers::query()
->where('mal_id', $id)
->get();
if (!empty($limit)) {
$limit = (int) $limit;
if (
$results->isEmpty()
|| $this->isExpired($request, $results)
) {
$response = Producers::scrape($id);
if ($limit <= 0) {
$limit = 1;
if (HttpHelper::hasError($response)) {
return HttpResponse::notFound($request);
}
if ($limit > self::MAX_RESULTS_PER_PAGE) {
$limit = self::MAX_RESULTS_PER_PAGE;
if ($results->isEmpty()) {
$meta = [
'createdAt' => new UTCDateTime(),
'modifiedAt' => new UTCDateTime(),
'request_hash' => $this->fingerprint
];
}
$meta['modifiedAt'] = new UTCDateTime();
$response = $meta + $response;
if ($results->isEmpty()) {
Producers::query()
->insert($response);
}
if ($this->isExpired($request, $results)) {
Producers::query()
->where('mal_id', $id)
->update($response);
}
$results = Producers::query()
->where('mal_id', $id)
->get();
}
$results = SearchQueryBuilderProducer::query(
$request,
Producer::query()
if ($results->isEmpty()) {
return HttpResponse::notFound($request);
}
$response = (new \App\Http\Resources\V4\ProducerResource(
$results->first()
))->response();
return $this->prepareResponse(
$response,
$results,
$request
);
}
$results = $results
->paginate(
$limit,
['*'],
null,
$page
);
/**
* @OA\Get(
* path="/producers/{id}/full",
* operationId="getProducerFullById",
* tags={"producers"},
*
* @OA\Parameter(
* name="id",
* in="path",
* required=true,
* @OA\Schema(type="integer")
* ),
*
* @OA\Response(
* response="200",
* description="Returns producer resource",
* @OA\JsonContent(
* @OA\Property(
* property="data",
* ref="#/components/schemas/producer_full"
* )
* )
* ),
* @OA\Response(
* response="400",
* description="Error: Bad request. When required parameters were not supplied.",
* ),
* )
*/
public function full(Request $request, int $id)
{
$results = Producers::query()
->where('mal_id', $id)
->get();
return new ProducerCollection(
$results
if (
$results->isEmpty()
|| $this->isExpired($request, $results)
) {
$response = Producers::scrape($id);
if (HttpHelper::hasError($response)) {
return HttpResponse::notFound($request);
}
if ($results->isEmpty()) {
$meta = [
'createdAt' => new UTCDateTime(),
'modifiedAt' => new UTCDateTime(),
'request_hash' => $this->fingerprint
];
}
$meta['modifiedAt'] = new UTCDateTime();
$response = $meta + $response;
if ($results->isEmpty()) {
Producers::query()
->insert($response);
}
if ($this->isExpired($request, $results)) {
Producers::query()
->where('mal_id', $id)
->update($response);
}
$results = Producers::query()
->where('mal_id', $id)
->get();
}
if ($results->isEmpty()) {
return HttpResponse::notFound($request);
}
$response = (new \App\Http\Resources\V4\ProducerFullResource(
$results->first()
))->response();
return $this->prepareResponse(
$response,
$results,
$request
);
}
/**
* @OA\Get(
* path="/producers/{id}/external",
* operationId="getProducerExternal",
* tags={"producers"},
*
* @OA\Parameter(
* name="id",
* in="path",
* required=true,
* @OA\Schema(type="integer")
* ),
*
* @OA\Response(
* response="200",
* description="Returns producer's external links",
* @OA\JsonContent(
* ref="#/components/schemas/external_links"
* )
* ),
* @OA\Response(
* response="400",
* description="Error: Bad request. When required parameters were not supplied.",
* ),
* )
*/
public function external(Request $request, int $id)
{
$results = Producers::query()
->where('mal_id', $id)
->get();
if (
$results->isEmpty()
|| $this->isExpired($request, $results)
) {
$response = Producers::scrape($id);
if (HttpHelper::hasError($response)) {
return HttpResponse::notFound($request);
}
if ($results->isEmpty()) {
$meta = [
'createdAt' => new UTCDateTime(),
'modifiedAt' => new UTCDateTime(),
'request_hash' => $this->fingerprint
];
}
$meta['modifiedAt'] = new UTCDateTime();
$response = $meta + $response;
if ($results->isEmpty()) {
Producers::query()
->insert($response);
}
if ($this->isExpired($request, $results)) {
Producers::query()
->where('mal_id', $id)
->update($response);
}
$results = Producers::query()
->where('mal_id', $id)
->get();
}
if ($results->isEmpty()) {
return HttpResponse::notFound($request);
}
$response = (new ExternalLinksResource(
$results->first()
))->response();
return $this->prepareResponse(
$response,
$results,
$request
);
}
}

View File

@ -13,6 +13,7 @@ use App\Http\QueryBuilder\SearchQueryBuilderCharacter;
use App\Http\QueryBuilder\SearchQueryBuilderClub;
use App\Http\QueryBuilder\SearchQueryBuilderManga;
use App\Http\QueryBuilder\SearchQueryBuilderPeople;
use App\Http\QueryBuilder\SearchQueryBuilderProducer;
use App\Http\QueryBuilder\SearchQueryBuilderUsers;
use App\Http\Resources\V4\AnimeCharactersResource;
use App\Http\Resources\V4\AnimeCollection;
@ -20,10 +21,12 @@ use App\Http\Resources\V4\CharacterCollection;
use App\Http\Resources\V4\ClubCollection;
use App\Http\Resources\V4\MangaCollection;
use App\Http\Resources\V4\PersonCollection;
use App\Http\Resources\V4\ProducerCollection;
use App\Http\Resources\V4\ResultsResource;
use App\Http\SearchQueryBuilder;
use App\Manga;
use App\Person;
use App\Producers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Jikan\Jikan;
@ -821,4 +824,87 @@ class SearchController extends Controller
$results
);
}
/**
* @OA\Get(
* path="/producers",
* operationId="getProducers",
* tags={"producers"},
*
* @OA\Parameter(ref="#/components/parameters/page"),
* @OA\Parameter(ref="#/components/parameters/limit"),
*
* @OA\Parameter(
* name="q",
* in="query",
* @OA\Schema(type="string")
* ),
*
* @OA\Parameter(
* name="order_by",
* in="query",
* @OA\Schema(ref="#/components/schemas/producers_query_orderby")
* ),
*
* @OA\Parameter(
* name="sort",
* in="query",
* @OA\Schema(ref="#/components/schemas/search_query_sort")
* ),
*
* @OA\Parameter(
* name="letter",
* in="query",
* description="Return entries starting with the given letter",
* @OA\Schema(type="string")
* ),
*
* @OA\Response(
* response="200",
* description="Returns producers collection",
* @OA\JsonContent(
* ref="#/components/schemas/producers"
* )
* ),
*
* @OA\Response(
* response="400",
* description="Error: Bad request. When required parameters were not supplied.",
* ),
* )
*/
public function producers(Request $request)
{
$page = $this->request->get('page') ?? 1;
$limit = $this->request->get('limit') ?? self::MAX_RESULTS_PER_PAGE;
if (!empty($limit)) {
$limit = (int) $limit;
if ($limit <= 0) {
$limit = 1;
}
if ($limit > self::MAX_RESULTS_PER_PAGE) {
$limit = self::MAX_RESULTS_PER_PAGE;
}
}
$results = SearchQueryBuilderProducer::query(
$request,
Producers::query()
);
$results = $results
->paginate(
$limit,
['*'],
null,
$page
);
return new ProducerCollection(
$results
);
}
}

View File

@ -14,14 +14,14 @@ use Jenssegers\Mongodb\Eloquent\Builder;
* schema="producers_query_orderby",
* description="Order by producers data",
* type="string",
* enum={"mal_id", "name", "count"}
* enum={"mal_id", "name", "count", "favorites", "established"}
* )
*/
class SearchQueryBuilderProducer implements SearchQueryBuilderInterface
{
const ORDER_BY = [
'mal_id', 'name', 'count'
'mal_id', 'name', 'count', 'favorites', 'established'
];
public static function query(Request $request, Builder $results) : Builder
@ -70,4 +70,4 @@ class SearchQueryBuilderProducer implements SearchQueryBuilderInterface
return $sort === 'desc' ? 'desc' : 'asc';
}
}
}

View File

@ -24,7 +24,7 @@ class ProducerCollection extends ResourceCollection
*
* @OA\Schema(
* schema="producers",
* description="Producer Collection Resource",
* description="Producers Collection Resource",
*
* allOf={
* @OA\Schema(ref="#/components/schemas/pagination"),

View File

@ -0,0 +1,97 @@
<?php
namespace App\Http\Resources\V4;
use Illuminate\Http\Resources\Json\JsonResource;
class ProducerFullResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*
* @OA\Schema(
* schema="producer_full",
* description="Producers Resource",
* @OA\Property(
* property="mal_id",
* type="integer",
* description="MyAnimeList ID"
* ),
* @OA\Property(
* property="url",
* type="string",
* description="MyAnimeList URL"
* ),
* @OA\Property(
* property="titles",
* type="array",
* description="All titles",
* @OA\Items(
* type="string"
* )
* ),
* @OA\Property(
* property="images",
* type="object",
* ref="#/components/schemas/common_images",
* ),
* @OA\Property(
* property="favorites",
* type="integer",
* description="Producers's member favorites count"
* ),
* @OA\Property(
* property="count",
* type="integer",
* description="Producers's anime count"
* ),
* @OA\Property(
* property="established",
* type="string",
* description="Established Date ISO8601",
* nullable=true
* ),
* @OA\Property(
* property="about",
* type="string",
* description="About the Producer",
* nullable=true
* ),
*
* @OA\Property(
* property="external",
* type="array",
*
* @OA\Items(
* type="object",
*
* @OA\Property(
* property="name",
* type="string",
* ),
* @OA\Property(
* property="url",
* type="string",
* ),
* ),
* ),
* ),
*/
public function toArray($request)
{
return [
'mal_id' => $this->mal_id,
'url' => $this->url,
'titles' => $this->titles,
'images' => $this->images,
'favorites' => $this->favorites ?? null,
'established' => $this->established ?? null,
'about' => $this->about ?? null,
'count' => $this->count ?? null,
'external' => $this->external_links ?? null
];
}
}

View File

@ -14,36 +14,65 @@ class ProducerResource extends JsonResource
*
* @OA\Schema(
* schema="producer",
* description="Producer Resource",
* description="Producers Resource",
* @OA\Property(
* property="mal_id",
* type="integer",
* description="MyAnimeList ID"
* ),
* @OA\Property(
* property="name",
* type="string",
* description="Producer Name"
* ),
* @OA\Property(
* property="url",
* type="string",
* description="MyAnimeList URL"
* ),
* @OA\Property(
* property="titles",
* type="array",
* description="All titles",
* @OA\Items(
* type="string"
* )
* ),
* @OA\Property(
* property="images",
* type="object",
* ref="#/components/schemas/common_images",
* ),
* @OA\Property(
* property="favorites",
* type="integer",
* description="Producers's member favorites count"
* ),
* @OA\Property(
* property="count",
* type="integer",
* description="Producer's anime count"
* description="Producers's anime count"
* ),
* @OA\Property(
* property="established",
* type="string",
* description="Established Date ISO8601",
* nullable=true
* ),
* @OA\Property(
* property="about",
* type="string",
* description="About the Producer",
* nullable=true
* ),
* ),
*/
public function toArray($request)
{
return [
'mal_id' => $this->mal_id,
'name' => $this->name,
'url' => $this->url,
'count' => $this->count
'titles' => $this->titles,
'images' => $this->images,
'favorites' => $this->favorites ?? null,
'established' => $this->established ?? null,
'about' => $this->about ?? null,
'count' => $this->count ?? null
];
}
}
}

View File

@ -3,13 +3,9 @@
namespace App;
use Jenssegers\Mongodb\Eloquent\Model;
use Jikan\Request\Producer\ProducersRequest;
use Jikan\Request\Producer\ProducerRequest;
/**
* Class Magazine
* @package App
*/
class Producer extends Model
class Producers extends Model
{
/**
@ -18,7 +14,7 @@ class Producer extends Model
* @var array
*/
protected $fillable = [
'mal_id', 'name', 'url', 'count'
'mal_id', 'url', 'images', 'titles', 'established', 'favorites', 'about', 'external', 'count'
];
/**
@ -28,27 +24,26 @@ class Producer extends Model
*/
protected $table = 'producers';
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'_id', 'expiresAt'
'_id', 'request_hash', 'expiresAt'
];
/**
* @return array
*/
public static function scrape() : array
public static function scrape(int $id)
{
$data = app('JikanParser')->getProducers(new ProducersRequest());
$data = app('JikanParser')->getProducer(new ProducerRequest($id));
return json_decode(
$data = json_decode(
app('SerializerV4')
->serialize($data, 'json'),
true
);
unset($data['results'], $data['has_next_page'], $data['last_visible_page']);
return $data;
}
}
}

View File

@ -190,11 +190,7 @@ return [
* Producers
*/
'ProducerController@main' => [
'table_name' => 'common',
'ttl' => env('CACHE_PRODUCERS_EXPIRE')
],
'ProducerController@resource' => [
'table_name' => 'common',
'table_name' => 'producers',
'ttl' => env('CACHE_DEFAULT_EXPIRE')
],
@ -337,6 +333,10 @@ return [
'table_name' => 'common',
'ttl' => env('CACHE_SEARCH_EXPIRE')
],
'SearchController@producers' => [
'table_name' => 'common',
'ttl' => env('CACHE_SEARCH_EXPIRE')
],
'ClubController@main' => [
'table_name' => 'clubs',

View File

@ -17,6 +17,9 @@ class CreateProducersTable extends Migration
$table->unique(['mal_id' => 1], 'mal_id');
$table->index('count', 'count');
$table->index('name', 'name');
$table->index('titles', 'titles');
$table->index('established', 'established');
$table->index('favorites', 'favorites');
$table->index(
[

View File

@ -279,7 +279,34 @@ $router->group(
],
function() use ($router) {
$router->get('/', [
'uses' => 'ProducerController@main',
'uses' => 'SearchController@producers',
]);
$router->get('/{id:[0-9]+}', [
'uses' => 'ProducerController@main'
]);
}
);
$router->get('/producers', [
'uses' => 'SearchController@producers'
]);
$router->group(
[
'prefix' => 'producers/{id:[0-9]+}'
],
function () use ($router) {
$router->get('/', [
'uses' => 'ProducerController@main'
]);
$router->get('/full', [
'uses' => 'ProducerController@full'
]);
$router->get('/external', [
'uses' => 'ProducerController@external'
]);
}
);

View File

@ -1895,56 +1895,105 @@
}
}
},
"/producers": {
"/producers/{id}": {
"get": {
"tags": [
"producers"
],
"operationId": "getProducers",
"operationId": "getProducerById",
"parameters": [
{
"$ref": "#/components/parameters/page"
},
{
"$ref": "#/components/parameters/limit"
},
{
"name": "q",
"in": "query",
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "order_by",
"in": "query",
"schema": {
"$ref": "#/components/schemas/producers_query_orderby"
}
},
{
"name": "sort",
"in": "query",
"schema": {
"$ref": "#/components/schemas/search_query_sort"
}
},
{
"name": "letter",
"in": "query",
"description": "Return entries starting with the given letter",
"schema": {
"type": "string"
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Returns producers collection",
"description": "Returns producer resource",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/producers"
"properties": {
"data": {
"$ref": "#/components/schemas/producer"
}
},
"type": "object"
}
}
}
},
"400": {
"description": "Error: Bad request. When required parameters were not supplied."
}
}
}
},
"/producers/{id}/full": {
"get": {
"tags": [
"producers"
],
"operationId": "getProducerFullById",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Returns producer resource",
"content": {
"application/json": {
"schema": {
"properties": {
"data": {
"$ref": "#/components/schemas/producer_full"
}
},
"type": "object"
}
}
}
},
"400": {
"description": "Error: Bad request. When required parameters were not supplied."
}
}
}
},
"/producers/{id}/external": {
"get": {
"tags": [
"producers"
],
"operationId": "getProducerExternal",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "Returns producer's external links",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/external_links"
}
}
}
@ -2873,6 +2922,66 @@
}
}
},
"/producers": {
"get": {
"tags": [
"producers"
],
"operationId": "getProducers",
"parameters": [
{
"$ref": "#/components/parameters/page"
},
{
"$ref": "#/components/parameters/limit"
},
{
"name": "q",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "order_by",
"in": "query",
"schema": {
"$ref": "#/components/schemas/producers_query_orderby"
}
},
{
"name": "sort",
"in": "query",
"schema": {
"$ref": "#/components/schemas/search_query_sort"
}
},
{
"name": "letter",
"in": "query",
"description": "Return entries starting with the given letter",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Returns producers collection",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/producers"
}
}
}
},
"400": {
"description": "Error: Bad request. When required parameters were not supplied."
}
}
}
},
"/seasons/{year}/{season}": {
"get": {
"tags": [
@ -4596,7 +4705,9 @@
"enum": [
"mal_id",
"name",
"count"
"count",
"favorites",
"established"
]
},
"users_search_query_gender": {
@ -7381,7 +7492,7 @@
"type": "object"
},
"producers": {
"description": "Producer Collection Resource",
"description": "Producers Collection Resource",
"allOf": [
{
"properties": {
@ -7399,24 +7510,100 @@
}
]
},
"producer": {
"description": "Producer Resource",
"producer_full": {
"description": "Producers Resource",
"properties": {
"mal_id": {
"description": "MyAnimeList ID",
"type": "integer"
},
"name": {
"description": "Producer Name",
"url": {
"description": "MyAnimeList URL",
"type": "string"
},
"titles": {
"description": "All titles",
"type": "array",
"items": {
"type": "string"
}
},
"images": {
"$ref": "#/components/schemas/common_images"
},
"favorites": {
"description": "Producers's member favorites count",
"type": "integer"
},
"count": {
"description": "Producers's anime count",
"type": "integer"
},
"established": {
"description": "Established Date ISO8601",
"type": "string",
"nullable": true
},
"about": {
"description": "About the Producer",
"type": "string",
"nullable": true
},
"external": {
"type": "array",
"items": {
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string"
}
},
"type": "object"
}
}
},
"type": "object"
},
"producer": {
"description": "Producers Resource",
"properties": {
"mal_id": {
"description": "MyAnimeList ID",
"type": "integer"
},
"url": {
"description": "MyAnimeList URL",
"type": "string"
},
"count": {
"description": "Producer's anime count",
"titles": {
"description": "All titles",
"type": "array",
"items": {
"type": "string"
}
},
"images": {
"$ref": "#/components/schemas/common_images"
},
"favorites": {
"description": "Producers's member favorites count",
"type": "integer"
},
"count": {
"description": "Producers's anime count",
"type": "integer"
},
"established": {
"description": "Established Date ISO8601",
"type": "string",
"nullable": true
},
"about": {
"description": "About the Producer",
"type": "string",
"nullable": true
}
},
"type": "object"