Skip to content

Commit

Permalink
Merge pull request #118 from Lomkit/feature/laravel-scout-integration
Browse files Browse the repository at this point in the history
🚧 laravel scout implementation
  • Loading branch information
GautierDele authored Jun 30, 2024
2 parents 3c18e55 + a759e44 commit 095d0f7
Show file tree
Hide file tree
Showing 19 changed files with 898 additions and 26 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,4 @@ Here is a quick look at what you can do using API search method:

- Metrics support
- Refactor the response class
- Plain text search using Laravel Scout
- Alias for includes / aggregates
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"require-dev": {
"guzzlehttp/guzzle": "^6.0|^7.0",
"orchestra/testbench": "^8.5|^9.0",
"phpunit/phpunit": "^8.0|^9.0|^10.0|^11.0"
"phpunit/phpunit": "^8.0|^9.0|^10.0|^11.0",
"laravel/scout": "^10.0|^11.0"
},
"autoload": {
"psr-4": {
Expand Down
13 changes: 13 additions & 0 deletions src/Concerns/PerformsQueries.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ public function searchQuery(\Lomkit\Rest\Http\Requests\RestRequest $request, \Il
return $query;
}

/**
* Build a "search" scout query for fetching resource.
*
* @param \Lomkit\Rest\Http\Requests\RestRequest $request
* @param \Laravel\Scout\Builder $query
*
* @return \Laravel\Scout\Builder
*/
public function searchScoutQuery(\Lomkit\Rest\Http\Requests\RestRequest $request, \Laravel\Scout\Builder $query)
{
return $query;
}

/**
* Build a query for mutating resource.
*
Expand Down
5 changes: 4 additions & 1 deletion src/Concerns/PerformsRestOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Lomkit\Rest\Http\Requests\OperateRequest;
use Lomkit\Rest\Http\Requests\RestoreRequest;
use Lomkit\Rest\Http\Requests\SearchRequest;
use Lomkit\Rest\Query\ScoutBuilder;

trait PerformsRestOperations
{
Expand Down Expand Up @@ -47,7 +48,9 @@ public function search(SearchRequest $request)

$this->beforeSearch($request);

$query = app()->make(QueryBuilder::class, ['resource' => $resource, 'query' => null])
$builder = $request->has('search.text') ? ScoutBuilder::class : QueryBuilder::class;

$query = app()->make($builder, ['resource' => $resource, 'query' => null])
->search($request->input('search', []));

$responsable = $resource->paginate($query, $request);
Expand Down
13 changes: 9 additions & 4 deletions src/Concerns/Resource/Paginable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@

namespace Lomkit\Rest\Concerns\Resource;

use Illuminate\Database\Eloquent\Builder;
use Laravel\Scout\Builder;
use Lomkit\Rest\Http\Requests\RestRequest;

trait Paginable
{
/**
* Paginate the results of a query.
*
* @param Builder $query
* @param RestRequest $request
* @param Illuminate\Database\Eloquent\Builder|\Laravel\Scout\Builder $query
* @param RestRequest $request
*
* @return mixed
*/
public function paginate(Builder $query, RestRequest $request)
public function paginate($query, RestRequest $request)
{
// In case we have a scout builder
if ($query instanceof Builder) {
return $query->paginate($request->input('search.limit', 50), 'page', $request->input('search.page', 1));
}

return $query->paginate($request->input('search.limit', 50), ['*'], 'page', $request->input('search.page', 1));
}
}
94 changes: 94 additions & 0 deletions src/Concerns/Resource/Scoutable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Lomkit\Rest\Concerns\Resource;

use Lomkit\Rest\Http\Requests\RestRequest;
use Lomkit\Rest\Instructions\Instruction;

trait Scoutable
{
/**
* The calculated scout fields if already done in this request.
*
* @var array
*/
protected array $calculatedScoutFields;

/**
* The calculated scout instructions if already done in this request.
*
* @var array
*/
protected array $calculatedScoutInstructions;

/**
* The scout fields that could be provided.
*
* @param RestRequest $request
*
* @return array
*/
public function scoutFields(RestRequest $request): array
{
return [];
}

/**
* The scout instructions that could be provided.
*
* @param RestRequest $request
*
* @return array
*/
public function scoutInstructions(RestRequest $request): array
{
return [];
}

/**
* Get the resource's scout fields.
*
* @param \Lomkit\Rest\Http\Requests\RestRequest $request
*
* @return array
*/
public function getScoutFields(\Lomkit\Rest\Http\Requests\RestRequest $request): array
{
return $this->calculatedScoutFields ?? ($this->calculatedScoutFields = $this->scoutFields($request));
}

/**
* Get the resource's scout instructions.
*
* @param \Lomkit\Rest\Http\Requests\RestRequest $request
*
* @return array
*/
public function getScoutInstructions(\Lomkit\Rest\Http\Requests\RestRequest $request): array
{
return $this->calculatedScoutInstructions ?? ($this->calculatedScoutInstructions = $this->scoutInstructions($request));
}

/**
* Retrieve a specific scout instruction by its key.
*
* @param RestRequest $request The REST request instance.
* @param string $instructionKey The key of the instruction to retrieve.
*
* @return Instruction|null The instruction instance or null if not found.
*/
public function scoutInstruction(RestRequest $request, string $instructionKey)
{
$instruction = collect($this->getScoutInstructions($request))
->first(function (Instruction $instruction) use ($instructionKey) {
return $instruction->uriKey() === $instructionKey;
});

if (!is_null($instruction)) {
$instruction
->resource($this);
}

return $instruction;
}
}
2 changes: 1 addition & 1 deletion src/Contracts/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function search(array $parameters = []);
/**
* Convert the query builder to an Eloquent query builder.
*
* @return \Illuminate\Database\Eloquent\Builder
* @return \Illuminate\Database\Eloquent\Builder|\Laravel\Scout\Builder
*/
public function toBase();
}
24 changes: 17 additions & 7 deletions src/Http/Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Laravel\Scout\Searchable;
use Lomkit\Rest\Actions\Actionable;
use Lomkit\Rest\Concerns\Authorizable;
use Lomkit\Rest\Concerns\PerformsModelOperations;
Expand All @@ -13,6 +14,7 @@
use Lomkit\Rest\Concerns\Resource\Paginable;
use Lomkit\Rest\Concerns\Resource\Relationable;
use Lomkit\Rest\Concerns\Resource\Rulable;
use Lomkit\Rest\Concerns\Resource\Scoutable;
use Lomkit\Rest\Http\Requests\RestRequest;
use Lomkit\Rest\Instructions\Instructionable;

Expand All @@ -22,6 +24,7 @@ class Resource implements \JsonSerializable
use PerformsModelOperations;
use Relationable;
use Paginable;
use Scoutable;
use Rulable;
use ConfiguresRestParameters;
use Authorizable;
Expand Down Expand Up @@ -142,6 +145,11 @@ public function cacheAuthorizationFor()
return now()->addMinutes(config('rest.authorizations.cache.default', 5));
}

public function isModelSearchable()
{
return in_array(Searchable::class, class_uses_recursive(static::$model));
}

/**
* Serialize the resource into a JSON-serializable format.
*
Expand All @@ -152,13 +160,15 @@ public function jsonSerialize(): mixed
$request = app(RestRequest::class);

return [
'actions' => collect($this->getActions($request))->map->jsonSerialize()->toArray(),
'instructions' => collect($this->getInstructions($request))->map->jsonSerialize()->toArray(),
'fields' => $this->getFields($request),
'limits' => $this->getLimits($request),
'scopes' => $this->getScopes($request),
'relations' => collect($this->getRelations($request))->map->jsonSerialize()->toArray(),
'rules' => [
'actions' => collect($this->getActions($request))->map->jsonSerialize()->toArray(),
'instructions' => collect($this->getInstructions($request))->map->jsonSerialize()->toArray(),
'scout_instructions' => collect($this->getScoutInstructions($request))->map->jsonSerialize()->toArray(),
'fields' => $this->getFields($request),
'scout_fields' => $this->getScoutFields($request),
'limits' => $this->getLimits($request),
'scopes' => $this->getScopes($request),
'relations' => collect($this->getRelations($request))->map->jsonSerialize()->toArray(),
'rules' => [
'all' => $this->rules($request),
'create' => $this->createRules($request),
'update' => $this->updateRules($request),
Expand Down
14 changes: 14 additions & 0 deletions src/Instructions/Instruction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Lomkit\Rest\Instructions;

use http\Exception\RuntimeException;
use Illuminate\Support\Str;
use Lomkit\Rest\Concerns\Fieldable;
use Lomkit\Rest\Concerns\Makeable;
Expand Down Expand Up @@ -72,4 +73,17 @@ public function handle(array $fields, \Illuminate\Database\Eloquent\Builder $que
{
// ...
}

/**
* Perform the instruction on the scout query.
*
* @param array $fields
* @param \Laravel\Scout\Builder $query
*
* @return void
*/
public function handleScout(array $fields, \Laravel\Scout\Builder $query)
{
throw new RuntimeException('Not implemented');
}
}
Loading

0 comments on commit 095d0f7

Please sign in to comment.