Skip to content

Commit

Permalink
Merge pull request #31 from wsssoftware/improve-data-adapter-on-join-…
Browse files Browse the repository at this point in the history
…queries

Improve data adapter on join queries
  • Loading branch information
allanmcarvalho authored Dec 11, 2024
2 parents 914a09b + 903d0e6 commit b36d064
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 8 deletions.
46 changes: 39 additions & 7 deletions src/DataAdapter/Filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace LaravelToolkit\DataAdapter;

use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Support\Arr;
Expand All @@ -22,31 +23,62 @@ private function __construct(
/**
* @return \Illuminate\Support\Collection<string, \LaravelToolkit\DataAdapter\Filter>|null
*/
public static function create(?array $filters, string $globalFilterName): ?Collection
public static function create(?array $filters, string $globalFilterName, EloquentBuilder $builder): ?Collection
{
if (empty($filters)) {
return null;
}

$collection = collect($filters)
->mapWithKeys(function (array $filter, string $key) use ($globalFilterName) {
return [$key => self::createItem($key, $globalFilterName, $filter)];
})
->mapWithKeys(fn (array $filter, string $key) => [
$key => self::createItem($key, $globalFilterName, $filter, $builder),
])
->filter(fn ($filter) => $filter instanceof Filter);

return $collection->count() > 0 ? $collection : null;
}

protected static function createItem(string $field, string $globalFilterName, array $data): ?Filter
{
protected static function createItem(
string $field,
string $globalFilterName,
array $data,
EloquentBuilder $builder
): ?Filter {
$operatorValue = trim(Arr::get($data, 'operator', 'and'));
$operator = Operator::tryFrom(! empty($operatorValue) ? $operatorValue : 'and');
$constraints = collect(Arr::get($data, 'constraints', [$data]))
->map(fn (array $constraint) => Constraint::create($constraint))
->filter(fn ($filter) => $filter instanceof Constraint);
$valid = $constraints->isNotEmpty() && $operator !== null;

return $valid ? new Filter($field, $operator, $constraints, $field === $globalFilterName) : null;
return $valid
? new Filter(self::fieldName($field, $builder), $operator, $constraints, $field === $globalFilterName)
: null;
}

protected static function fieldName(string $field, EloquentBuilder $builder): string
{
$query = $builder->getQuery();
if (count($query?->joins ?? []) === 0) {
return $field;
}

foreach ($query->columns as $column) {
if ($column instanceof Expression) {
$column = $column->getValue($query->grammar);
}
$column = str($column)
->deduplicate()
->remove(['(', ')', '`', '\'', '"'], '')
->replace([' As ', 'AS', 'aS'], ' as ');
if ($column->contains(".$field") || $column->contains(" as $field")) {
$field = $column
->before(' as ')
->toString();
}
}

return $field;
}

public function apply(QueryBuilder $builder): void
Expand Down
2 changes: 1 addition & 1 deletion src/DataAdapter/QueryHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function rows(): int

public function filters(EloquentBuilder $builder): void
{
if (($filters = Filter::create($this->get('filters'), $this->get('global_filter_name'))) === null) {
if (($filters = Filter::create($this->get('filters'), $this->get('global_filter_name'), $builder)) === null) {
return;
}
if (($filter = $filters->where('global', true)->first()) !== null) {
Expand Down
39 changes: 39 additions & 0 deletions tests/DataAdapterTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use LaravelToolkit\Tests\Model\Product;
use LaravelToolkit\Tests\Model\User;

it('test base functionality', function () {
Expand Down Expand Up @@ -298,3 +299,41 @@
->and($response->json('users.total'))
->toEqual(100);
});

it('test join feature', function () {
$user = User::factory()->create();
foreach (range(1, 10) as $item) {
Product::create(['user_id' => $user->id, 'name' => 'Product '.$item]);
}
Route::getAndPost('/', function () {
return response()->json([
'products' => Product::query()
->select('products.*', 'users.email_verified_at')
->selectSub('users.name', 'user_name')
->join('users', 'users.id', '=', 'products.user_id')
->primevueData(),
]);
});
$response = $this->post('/', [
'page' => 1,
'page-options' => [
'global_filter_name' => 'global',
'rows' => 15,
'filters' => [
'user_name' => [
'operator' => 'and', 'constraints' => [['value' => $user->name, 'matchMode' => 'contains']],
],
'email_verified_at' => [
'operator' => 'and', 'constraints' => [['value' => 'foo', 'matchMode' => 'notContains']],
],
],
],
]);
$response->assertSuccessful();
expect($response->content())
->toBeJson()
->and($response->json('products'))
->toBeArray()
->and($response->json('products.total'))
->toEqual(10);
});
5 changes: 5 additions & 0 deletions tests/Model/2024_09_24_163917_create_products_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')
->nullable()
->constrained('users')
->cascadeOnUpdate()
->cascadeOnDelete();
$table->uuid('image')->nullable();
$table->timestamps();
});
Expand Down
2 changes: 2 additions & 0 deletions tests/Model/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

/**
* @property int $id
* @property int|null $user_id
* @property string|\LaravelToolkit\StoredAssets\Assets $image
* @property \Illuminate\Support\Carbon $created_at
* @property \Illuminate\Support\Carbon $updated_at
Expand All @@ -22,6 +23,7 @@ class Product extends Model
*/
protected $fillable = [
'id',
'user_id',
'image',
'created_at',
'updated_at',
Expand Down

0 comments on commit b36d064

Please sign in to comment.