generated from spatie/package-skeleton-laravel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add more tests and columns transformer
- Loading branch information
1 parent
cd9e914
commit a7661b0
Showing
8 changed files
with
674 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<?php | ||
|
||
namespace Clickbar\LaravelPowerRelations\Eloquent; | ||
|
||
use Illuminate\Contracts\Database\Query\Expression; | ||
use Illuminate\Database\Grammar; | ||
use Illuminate\Support\Str; | ||
|
||
/** | ||
* The Class is used to transform the $columns given to the getRelationExistenceQuery method of a power relation. | ||
*/ | ||
class ColumnsTransformator | ||
{ | ||
const defaultAggregates = [ | ||
'sum', | ||
'avg', | ||
'min', | ||
'max', | ||
'count', | ||
]; | ||
|
||
private static function isAggregate(string $sql): bool | ||
{ | ||
foreach (self::defaultAggregates as $aggregate) { | ||
if (str_starts_with(strtolower($sql), strtolower($aggregate))) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public static function transform(mixed $columns, string $tableName, Grammar $grammar): mixed | ||
{ | ||
if (is_array($columns)) { | ||
return array_map(fn ($column) => self::transform($column, $tableName, $grammar), $columns); | ||
} | ||
|
||
if ($columns instanceof Expression) { | ||
return self::transform($columns->getValue($grammar), $tableName, $grammar); | ||
} | ||
|
||
if (is_string($columns) && self::isAggregate($columns)) { | ||
return new \Illuminate\Database\Query\Expression(self::transformAggregate($columns, $tableName)); | ||
} | ||
|
||
return $columns; | ||
} | ||
|
||
private static function transformAggregate(string $sql, string $tableName): string | ||
{ | ||
|
||
// Retrieve the column after the last "." | ||
$column = Str::of($sql) | ||
->between('(', ')') | ||
->afterLast('"."') | ||
->trim('"') | ||
->toString(); | ||
|
||
// In case of only the * we do not need to prefix it with a table name | ||
if ($column === '*') { | ||
return $sql; | ||
} | ||
|
||
$aggregate = Str::before($sql, '('); | ||
|
||
return "$aggregate(\"$tableName\".\"$column\")"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<?php | ||
|
||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Client; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Order; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Project; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Task; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\TaskWithSoftDelete; | ||
|
||
it('can delete tasks of client via PowerRelation', function () { | ||
|
||
// Create some noise | ||
Client::factory()->count(3) | ||
->has(Project::factory()->count(7) | ||
->has(Order::factory()->count(4) | ||
->has(Task::factory()->count(8)) | ||
) | ||
) | ||
->create(); | ||
|
||
// Create the data we will expect | ||
$client = Client::factory()->create(); | ||
$projects = Project::factory()->count(2)->recycle($client)->create(); | ||
$orders = $projects->flatMap(fn (Project $project) => Order::factory()->count(2)->recycle($project)->create()); | ||
$tasks = $orders->flatMap(fn (Order $order) => Task::factory()->count(3)->recycle($order)->create()); | ||
|
||
expect(Task::whereIn('order_id', $orders->pluck('id'))->count())->toBeGreaterThan(0); | ||
$otherTaskCount = Task::whereNotIn('order_id', $orders->pluck('id'))->count(); | ||
|
||
$client->tasks()->delete(); | ||
expect(Task::all())->toHaveCount($otherTaskCount); | ||
expect(Task::whereIn('order_id', $orders->pluck('id'))->get())->toBeEmpty(); | ||
}); | ||
|
||
it('can delete tasks of client via PowerRelation from Parent', function () { | ||
|
||
// Create some noise | ||
Client::factory()->count(3) | ||
->has(Project::factory()->count(7) | ||
->has(Order::factory()->count(4) | ||
->has(Task::factory()->count(8)) | ||
) | ||
) | ||
->create(); | ||
|
||
// Create the data we will expect | ||
$client = Client::factory()->create(); | ||
$projects = Project::factory()->count(2)->recycle($client)->create(); | ||
$orders = $projects->flatMap(fn (Project $project) => Order::factory()->count(2)->recycle($project)->create()); | ||
$tasks = $orders->flatMap(fn (Order $order) => Task::factory()->count(3)->recycle($order)->create()); | ||
|
||
expect(Task::whereIn('order_id', $orders->pluck('id'))->count())->toBeGreaterThan(0); | ||
$otherTaskCount = Task::whereNotIn('order_id', $orders->pluck('id'))->count(); | ||
|
||
$client->tasksFromParent()->delete(); | ||
expect(Task::all())->toHaveCount($otherTaskCount); | ||
expect(Task::whereIn('order_id', $orders->pluck('id'))->get())->toBeEmpty(); | ||
}); | ||
|
||
it('can soft delete tasks of client via PowerRelation', function () { | ||
|
||
// Create some noise | ||
Client::factory()->count(3) | ||
->has(Project::factory()->count(7) | ||
->has(Order::factory()->count(4) | ||
->has(TaskWithSoftDelete::factory()->count(8), 'tasksWithSoftDelete') | ||
) | ||
) | ||
->create(); | ||
|
||
// Create the data we will expect | ||
$client = Client::factory()->create(); | ||
$projects = Project::factory()->count(2)->recycle($client)->create(); | ||
$orders = $projects->flatMap(fn (Project $project) => Order::factory()->count(2)->recycle($project)->create()); | ||
$tasks = $orders->flatMap(fn (Order $order) => TaskWithSoftDelete::factory()->count(3)->recycle($order)->create()); | ||
|
||
expect(TaskWithSoftDelete::whereIn('order_id', $orders->pluck('id'))->count())->toBeGreaterThan(0); | ||
$otherTaskCount = TaskWithSoftDelete::whereNotIn('order_id', $orders->pluck('id'))->count(); | ||
|
||
$client->tasksWithSoftDelete()->delete(); | ||
|
||
expect(TaskWithSoftDelete::all())->toHaveCount($otherTaskCount); | ||
expect(TaskWithSoftDelete::whereIn('order_id', $orders->pluck('id'))->get())->toBeEmpty(); | ||
expect(TaskWithSoftDelete::whereIn('order_id', $orders->pluck('id'))->withTrashed()->get())->toHaveCount($tasks->count()); | ||
}); | ||
|
||
it('can soft delete tasks of client via PowerRelation from parent', function () { | ||
|
||
// Create some noise | ||
Client::factory()->count(3) | ||
->has(Project::factory()->count(7) | ||
->has(Order::factory()->count(4) | ||
->has(TaskWithSoftDelete::factory()->count(8), 'tasksWithSoftDelete') | ||
) | ||
) | ||
->create(); | ||
|
||
// Create the data we will expect | ||
$client = Client::factory()->create(); | ||
$projects = Project::factory()->count(2)->recycle($client)->create(); | ||
$orders = $projects->flatMap(fn (Project $project) => Order::factory()->count(2)->recycle($project)->create()); | ||
$tasks = $orders->flatMap(fn (Order $order) => TaskWithSoftDelete::factory()->count(3)->recycle($order)->create()); | ||
|
||
expect(TaskWithSoftDelete::whereIn('order_id', $orders->pluck('id'))->count())->toBeGreaterThan(0); | ||
$otherTaskCount = TaskWithSoftDelete::whereNotIn('order_id', $orders->pluck('id'))->count(); | ||
|
||
$client->tasksWithSoftDeleteFromParent()->delete(); | ||
|
||
expect(TaskWithSoftDelete::all())->toHaveCount($otherTaskCount); | ||
expect(TaskWithSoftDelete::whereIn('order_id', $orders->pluck('id'))->get())->toBeEmpty(); | ||
expect(TaskWithSoftDelete::whereIn('order_id', $orders->pluck('id'))->withTrashed()->get())->toHaveCount($tasks->count()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
|
||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Client; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Order; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Project; | ||
use Clickbar\LaravelPowerRelations\Tests\TestClasses\Models\Task; | ||
use Illuminate\Support\Collection; | ||
use Illuminate\Support\Facades\DB; | ||
|
||
it('can eager load tasks of client via PowerRelation', function () { | ||
|
||
// Create some noise | ||
Client::factory()->count(3) | ||
->has(Project::factory()->count(7) | ||
->has(Order::factory()->count(4) | ||
->has(Task::factory()->count(8)) | ||
) | ||
) | ||
->create(); | ||
|
||
DB::enableQueryLog(); | ||
$clients = Client::with('tasks')->get(); | ||
expect(DB::getQueryLog())->toHaveCount(2); | ||
|
||
// Load it for the flat way for comparison | ||
$clients->load('projects.orders.tasks'); | ||
|
||
DB::flushQueryLog(); | ||
foreach ($clients as $client) { | ||
/** @var Collection<int, Task> $tasksFromFlatWay */ | ||
$tasksFromFlatWay = $client->tasks_from_relation_chain; | ||
/** @var Collection<int, Task> $tasksFromEagerLoad */ | ||
$tasksFromEagerLoad = $client->tasks; | ||
|
||
expect($tasksFromFlatWay)->toHaveSameSize($tasksFromEagerLoad); | ||
|
||
// Compare the task with its key => values | ||
$tasksFromEagerLoad->zip($tasksFromFlatWay)->each(function ($data) { | ||
$eagerTaskData = $data->get(0)->toArray(); | ||
$flatTaskData = $data->get(1)->toArray(); | ||
|
||
ksort($flatTaskData); | ||
ksort($eagerTaskData); | ||
|
||
// Use toMatchArray, because the eager loads adds the clients.id property to the attributes | ||
expect($eagerTaskData)->toMatchArray($flatTaskData); | ||
}); | ||
} | ||
expect(DB::getQueryLog())->toBeEmpty(); | ||
|
||
}); | ||
|
||
it('can eager load tasks of client via PowerRelation from Parent', function () { | ||
|
||
// Create some noise | ||
Client::factory()->count(3) | ||
->has(Project::factory()->count(7) | ||
->has(Order::factory()->count(4) | ||
->has(Task::factory()->count(8)) | ||
) | ||
) | ||
->create(); | ||
|
||
DB::enableQueryLog(); | ||
$clients = Client::with('tasksFromParent')->get(); | ||
expect(DB::getQueryLog())->toHaveCount(2); | ||
|
||
// Load it for the flat way for comparison | ||
$clients->load('projects.orders.tasks'); | ||
|
||
DB::flushQueryLog(); | ||
foreach ($clients as $client) { | ||
/** @var Collection<int, Task> $tasksFromFlatWay */ | ||
$tasksFromFlatWay = $client->tasks_from_relation_chain; | ||
/** @var Collection<int, Task> $tasksFromEagerLoad */ | ||
$tasksFromEagerLoad = $client->tasksFromParent; | ||
|
||
expect($tasksFromFlatWay)->toHaveSameSize($tasksFromEagerLoad); | ||
|
||
// Compare the task with its key => values | ||
$tasksFromEagerLoad->zip($tasksFromFlatWay)->each(function ($data) { | ||
$eagerTaskData = $data->get(0)->toArray(); | ||
$flatTaskData = $data->get(1)->toArray(); | ||
|
||
ksort($flatTaskData); | ||
ksort($eagerTaskData); | ||
|
||
// Use toMatchArray, because the eager loads adds the clients.id property to the attributes | ||
expect($eagerTaskData)->toMatchArray($flatTaskData); | ||
}); | ||
} | ||
expect(DB::getQueryLog())->toBeEmpty(); | ||
|
||
}); |
Oops, something went wrong.