Skip to content

Commit

Permalink
Support Laravel Scout Builder (#1582)
Browse files Browse the repository at this point in the history
* Add Scout Builder support

* Add typesense.yml

* Add typesense.yml

* Add typesense.yml

* Disable typesense

* Add Builder Macro paginateSafe

* Add Builder Macro paginateSafe

* phpstan fixes
  • Loading branch information
luanfreitasdev committed Jun 1, 2024
1 parent 9125e36 commit 2d624af
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/postgres.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PostgreSQL
name: PostGreSQL

on:
push:
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/typesense.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Scout Typesense

on: workflow_dispatch

jobs:
build:
runs-on: ubuntu-latest

services:
mysql:
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: powergridtest
ports:
- 3307:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

strategy:
fail-fast: false
matrix:
php: [8.3]
laravel: [11.*]
dependency-version: [ prefer-stable ]

name: PHP:${{ matrix.php }} / L:${{ matrix.laravel }}

if: github.ref != 'refs/heads/todo-tests'

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv
tools: composer:v2
coverage: none

- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Cache composer dependencies
uses: actions/cache@v4
with:
path: $(composer config cache-files-dir)
key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}

- name: Install Typesense
run: |
curl -O https://dl.typesense.org/releases/26.0/typesense-server-26.0-arm64.deb
curl -O https://dl.typesense.org/releases/26.0/typesense-server-26.0-amd64.deb
sudo apt install ./typesense-server-26.0-amd64.deb
- name: Install Composer dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
composer require laravel/scout
composer require typesense/typesense-php
composer install
- name: Tests
run: composer test:typesense
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"larastan/larastan": "^2.9.0",
"pestphp/pest": "^2.34.0",
"orchestra/testbench": "8.19|^9.0",
"laradumps/laradumps": "^3.1"
"laradumps/laradumps": "^3.1",
"laravel/scout": "^10.9"
},
"suggest": {
"openspout/openspout": "Required to export XLS and CSV"
Expand Down Expand Up @@ -76,6 +77,9 @@
"test:sqlsrv": [
"./vendor/bin/pest --configuration phpunit.sqlsrv.xml"
],
"test:typesense": [
"curl http://localhost:8108/health"
],
"test:types": "./vendor/bin/phpstan analyse --ansi --memory-limit=-1",
"test:dbs": [
"@test:sqlite",
Expand Down
21 changes: 21 additions & 0 deletions phpunit.typesense.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage/>
<php>
<server name="SCOUT_DRIVER" value="typesense"/>
<server name="TYPESENSE_HOST" value="localhost"/>
<server name="TYPESENSE_PORT" value="8108"/>
<server name="TYPESENSE_PROTOCOL" value="http"/>
<server name="TYPESENSE_API_KEY" value="xyz"/>
</php>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
55 changes: 43 additions & 12 deletions src/ProcessDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
use Illuminate\Database\Eloquent\{Builder as EloquentBuilder, Model};
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\{Collection as BaseCollection, Facades\DB, Str};
use Illuminate\Support\{Collection as BaseCollection, Facades\DB, Str, Stringable};
use Laravel\Scout\Builder as ScoutBuilder;
use PowerComponents\LivewirePowerGrid\Components\Actions\ActionsController;
use PowerComponents\LivewirePowerGrid\Components\Rules\{RulesController};
use PowerComponents\LivewirePowerGrid\DataSource\{Builder, Collection};
Expand All @@ -18,8 +19,6 @@ class ProcessDataSource
{
use Concerns\SoftDeletes;

public bool $isCollection = false;

private array $queryLog = [];

public function __construct(
Expand Down Expand Up @@ -49,16 +48,46 @@ public function get(bool $isExport = false): Paginator|LengthAwarePaginator|\Ill
return $this->processCollection($datasource, $isExport);
}

if ($datasource instanceof ScoutBuilder) {
return $this->processScoutCollection($datasource);
}

$this->setCurrentTable($datasource);

/** @phpstan-ignore-next-line */
return $this->processModel($datasource);
return $this->processModel($datasource); // @phpstan-ignore-line
}

/**
* @return EloquentBuilder|BaseCollection|Collection|QueryBuilder|MorphToMany|null
*/
public function prepareDataSource(): EloquentBuilder|BaseCollection|Collection|QueryBuilder|MorphToMany|null
public function processScoutCollection(ScoutBuilder $datasource): Paginator|LengthAwarePaginator
{
$datasource->query = Str::of($datasource->query)
->when($this->component->search != '', fn (Stringable $self) => $self
->prepend($this->component->search . ','))
->toString();

collect($this->component->filters)->each(fn (array $filters) => collect($filters)
->each(fn (string $value, string $field) => $datasource
->where($field, $value)));

if ($this->component->multiSort) {
foreach ($this->component->sortArray as $sortField => $direction) {
$datasource->orderBy($sortField, $direction);
}
} else {
$datasource->orderBy($this->component->sortField, $this->component->sortDirection);
}

$results = self::applyPerPage($datasource);

if (method_exists($results, 'total')) {
$this->component->total = $results->total();
}

return $results->setCollection( // @phpstan-ignore-line
$this->transform($results->getCollection(), $this->component) // @phpstan-ignore-line
);
}

public function prepareDataSource(): EloquentBuilder|BaseCollection|Collection|QueryBuilder|MorphToMany|ScoutBuilder|null
{
$datasource = $this->component->datasource ?? null;

Expand All @@ -70,8 +99,6 @@ public function prepareDataSource(): EloquentBuilder|BaseCollection|Collection|Q
$datasource = collect($datasource);
}

$this->isCollection = $datasource instanceof BaseCollection;

return $datasource;
}

Expand Down Expand Up @@ -226,7 +253,7 @@ private function applyWithSortStringNumber(
return $results;
}

private function applyPerPage(EloquentBuilder|QueryBuilder|MorphToMany $results): LengthAwarePaginator|Paginator
private function applyPerPage(EloquentBuilder|QueryBuilder|MorphToMany|ScoutBuilder $results): LengthAwarePaginator|Paginator
{
$pageName = strval(data_get($this->component->setUp, 'footer.pageName', 'page'));
$perPage = intval(data_get($this->component->setUp, 'footer.perPage'));
Expand All @@ -237,6 +264,10 @@ private function applyPerPage(EloquentBuilder|QueryBuilder|MorphToMany $results)
default => 'paginate',
};

if ($results instanceof ScoutBuilder) {
return $results->paginateSafe($perPage, pageName: $pageName); // @phpstan-ignore-line
}

if ($perPage > 0) {
return $results->$paginate($perPage, pageName: $pageName);
}
Expand Down
44 changes: 43 additions & 1 deletion src/Providers/PowerGridServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

namespace PowerComponents\LivewirePowerGrid\Providers;

use Illuminate\Container\Container;
use Illuminate\Database\Events\MigrationsEnded;
use Illuminate\Pagination\{LengthAwarePaginator, Paginator};
use Illuminate\Support\Facades\{Blade, Event};
use Illuminate\Support\ServiceProvider;
use Laravel\Scout\Builder;
use Laravel\Scout\Contracts\PaginatesEloquentModels;
use Livewire\Features\SupportLegacyModels\{EloquentCollectionSynth, EloquentModelSynth};
use Livewire\Livewire;
use PowerComponents\LivewirePowerGrid\Commands\CheckDependenciesCommand;
Expand Down Expand Up @@ -64,7 +68,7 @@ public function register(): void
Livewire::component('powergrid-performance-card', PerformanceCard::class);
}

Macros::boot();
$this->macros();
}

private function publishViews(): void
Expand All @@ -89,4 +93,42 @@ private function publishConfigs(): void

$this->publishes([__DIR__ . '/../../resources/lang' => lang_path('vendor/' . $this->packageName)], $this->packageName . '-lang');
}

private function macros(): void
{
Macros::boot();

if (class_exists(\Laravel\Scout\Builder::class)) {
Builder::macro('paginateSafe', function ($perPage = null, $pageName = 'page', $page = null) {
$engine = $this->engine(); // @phpstan-ignore-line

if ($engine instanceof PaginatesEloquentModels) {
return $engine->paginate($this, $perPage, $page)->appends('query', $this->query);
}

$page = $page ?: Paginator::resolveCurrentPage($pageName);

$perPage = $perPage ?: $this->model->getPerPage();

$results = $this->model->newCollection(
$engine->map(
$this,
$rawResults = $engine->paginate($this, $perPage, $page),
$this->model
)->all()
);

return Container::getInstance()->makeWith(LengthAwarePaginator::class, [
'items' => $results,
'total' => $engine->getTotalCount($rawResults),
'perPage' => $perPage,
'currentPage' => $page,
'options' => [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
],
])->appends('query', $this->query);
});
}
}
}
2 changes: 1 addition & 1 deletion src/Traits/WithExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public function prepareToExport(bool $selected = false): Eloquent\Collection|Sup
$inClause = $processDataSource->component->checkboxValues;
}

if ($processDataSource->isCollection) {
if ($processDataSource->component->datasource() instanceof Collection) {
if ($inClause) {
$results = $processDataSource->get(isExport: true)->whereIn($this->primaryKey, $inClause);

Expand Down

0 comments on commit 2d624af

Please sign in to comment.