This commit is contained in:
Dan Harrin
2022-11-11 17:33:51 +00:00
parent eea66cc794
commit 4bb9db5f34
17 changed files with 369 additions and 40 deletions
Generated
+6 -6
View File
@@ -127,16 +127,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.241.1",
"version": "3.242.0",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "124ec1258f667e99f2fc3288f3e59fd4903a22d3"
"reference": "cab6d4f830ce275a1742a3e35e2625917b006dd2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/124ec1258f667e99f2fc3288f3e59fd4903a22d3",
"reference": "124ec1258f667e99f2fc3288f3e59fd4903a22d3",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/cab6d4f830ce275a1742a3e35e2625917b006dd2",
"reference": "cab6d4f830ce275a1742a3e35e2625917b006dd2",
"shasum": ""
},
"require": {
@@ -215,9 +215,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.241.1"
"source": "https://github.com/aws/aws-sdk-php/tree/3.242.0"
},
"time": "2022-11-09T19:31:43+00:00"
"time": "2022-11-10T19:15:37+00:00"
},
{
"name": "blade-ui-kit/blade-heroicons",
@@ -118,7 +118,7 @@ class BaseFileUpload extends Field
if ($component->getVisibility() === 'private') {
try {
$url ??= $storage->temporaryUrl(
$url = $storage->temporaryUrl(
$file,
now()->addMinutes(5),
);
@@ -77,7 +77,7 @@ class SpatieMediaLibraryFileUpload extends FileUpload
if ($component->getVisibility() === 'private') {
try {
$url ??= $media?->getTemporaryUrl(
$url = $media?->getTemporaryUrl(
now()->addMinutes(5),
);
} catch (Throwable $exception) {
@@ -92,9 +92,9 @@ class SpatieMediaLibraryFileUpload extends FileUpload
$url ??= $media?->getUrl();
return [
'name' => $media->name ?? $media->file_name,
'size' => $media->size,
'type' => $media->mime_type,
'name' => $media->name ?? $media->getAttributeValue('file_name'),
'size' => $media->getAttributeValue('size'),
'type' => $media->getAttributeValue('mime_type'),
'url' => $url,
];
});
+6
View File
@@ -109,6 +109,12 @@ namespace Livewire\Testing {
public function assertTableColumnStateNotSet(string $name, $value, $record): static {}
public function assertTableColumnSummarizerExists(string $columnName, string $summarizerId): static {}
public function assertTableColumnSummarySet(string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static {}
public function assertTableColumnSummaryNotSet(string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static {}
public function sortTable(?string $name = null, ?string $direction = null): static {}
public function searchTable(?string $search = null): static {}
@@ -50,12 +50,7 @@
:columns="$columns"
:heading="__('filament-tables::table.summary.subheadings.page', ['label' => $pluralModelLabel])"
:is-selection-enabled="$isSelectionEnabled"
:query="$this->applySortingToTableQuery(
$this->getFilteredTableQuery()->forPage(
page: $records->currentPage(),
perPage: $records->perPage(),
),
)"
:query="$this->getPageTableSummaryQuery()"
/>
@endif
@@ -65,5 +60,5 @@
:columns="$columns"
:heading="__('filament-tables::table.summary.subheadings.all', ['label' => $pluralModelLabel])"
:is-selection-enabled="$isSelectionEnabled"
:query="$this->getFilteredTableQuery()"
:query="$this->getAllTableSummaryQuery()"
/>
@@ -12,10 +12,15 @@ trait CanBeSummarized
public function summarize(array | Summarizer $summarizers): static
{
$this->summarizers = array_merge(
$this->summarizers,
Arr::wrap($summarizers),
);
foreach (Arr::wrap($summarizers) as $summarizer) {
$summarizer->column($this);
if (filled($id = $summarizer->getId())) {
$this->summarizers[$id] = $summarizer;
} else {
$this->summarizers[] = $summarizer;
}
}
return $this;
}
@@ -23,13 +28,17 @@ trait CanBeSummarized
public function getSummary(Builder $query): array
{
return array_map(function (Summarizer $summarizer) use ($query): Summarizer {
$summarizer->column($this);
$summarizer->query($query);
return $summarizer;
}, $this->getSummarizers());
}
public function getSummarizer(string $id): ?Summarizer
{
return $this->getSummarizers()[$id] ?? null;
}
public function getSummarizers(): array
{
return $this->summarizers;
@@ -37,6 +46,6 @@ trait CanBeSummarized
public function hasSummary(): bool
{
return count($this->summarizers);
return (bool) count($this->summarizers);
}
}
@@ -21,16 +21,30 @@ class Summarizer extends ViewComponent
protected string $view = 'filament-tables::columns.summaries.text';
protected ?string $id = null;
protected ?Closure $using = null;
public static function make(): static
final public function __construct(?string $id = null)
{
$static = app(static::class);
$this->id($id);
}
public static function make(?string $id = null): static
{
$static = app(static::class, ['id' => $id]);
$static->configure();
return $static;
}
public function id(?string $id): static
{
$this->id = $id;
return $this;
}
public function using(?Closure $using): static
{
$this->using = $using;
@@ -52,7 +66,7 @@ class Summarizer extends ViewComponent
->plural()
->camel();
$query = $relationship->getModel()->newQuery()
$query = $relationship->getQuery()->getModel()->newQuery()
->whereHas(
$inverseRelationship,
fn (EloquentBuilder $relatedQuery): EloquentBuilder => $this->hasPaginatedQuery() ?
@@ -61,7 +75,7 @@ class Summarizer extends ViewComponent
);
}
$query = DB::table($query);
$query = DB::table($query->getQuery());
if ($this->hasQueryModificationCallback()) {
$query = $this->evaluate($this->modifyQueryUsing, [
@@ -85,6 +99,11 @@ class Summarizer extends ViewComponent
return null;
}
public function getId(): ?string
{
return $this->id;
}
protected function hasPaginatedQuery(): bool
{
return $this->getQuery()->getQuery()->limit !== null;
@@ -44,6 +44,8 @@ class TextColumn extends Column
) {
$this->formatStateUsing(static fn ($state): ?string => $enum::tryFrom($state)?->getLabel() ?? ($default ?? $state));
}
return $this;
}
public function rowIndex(bool $isFromZero = false): static
@@ -46,14 +46,17 @@ trait CanSortRecords
protected function applySortingToTableQuery(Builder $query): Builder
{
if ($this->isTableReordering()) {
return $query->orderBy($this->getTableReorderColumn());
return $query->orderBy($this->getTable()->getReorderColumn());
}
$sortColumn = $this->tableSortColumn;
if (! $sortColumn) {
/** @phpstan-ignore-next-line */
$orders = $query->getQuery()->orders ?? [];
return $query->when(
count($query->getQuery()->orders ?? []) === 0,
count($orders) === 0,
fn (Builder $query) => $query->orderBy($query->getModel()->getQualifiedKeyName()),
);
}
@@ -0,0 +1,23 @@
<?php
namespace Filament\Tables\Concerns;
use Illuminate\Database\Eloquent\Builder;
trait CanSummarizeRecords
{
public function getAllTableSummaryQuery(): Builder
{
return $this->getFilteredTableQuery();
}
public function getPageTableSummaryQuery(): Builder
{
return $this->applySortingToTableQuery(
$this->getFilteredTableQuery()->forPage(
page: $this->getTableRecords()->currentPage(),
perPage: $this->getTableRecords()->perPage(),
),
);
}
}
@@ -13,26 +13,27 @@ use Illuminate\Database\Eloquent\Relations\Relation;
trait InteractsWithTable
{
use CanBeStriped;
use CanPaginateRecords;
use CanPollRecords;
use CanReorderRecords;
use CanSearchRecords;
use CanSelectRecords;
use CanSortRecords;
use CanSummarizeRecords;
use CanToggleColumns;
use HasActions;
use HasBulkActions;
use HasColumns;
use HasFilters;
use HasRecords;
use Forms\Concerns\InteractsWithForms;
use CanBeStriped;
use CanPollRecords;
use HasContent;
use HasEmptyState;
use HasFilters;
use HasHeader;
use HasRecords;
use HasRecordAction;
use HasRecordClasses;
use HasRecordUrl;
use Forms\Concerns\InteractsWithForms;
protected Table $table;
@@ -8,6 +8,7 @@ use Filament\Tables\Actions\Action;
use Filament\Tables\Actions\BulkAction;
use Filament\Tables\Table;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
@@ -51,6 +52,10 @@ interface HasTable extends HasForms
public function getTableSortDirection(): ?string;
public function getAllTableSummaryQuery(): Builder;
public function getPageTableSummaryQuery(): Builder;
public function isTableColumnToggledHidden(string $name): bool;
public function getTableColumnToggleForm(): Form;
@@ -7,6 +7,7 @@ use Filament\Tables\Testing\TestsBulkActions;
use Filament\Tables\Testing\TestsColumns;
use Filament\Tables\Testing\TestsFilters;
use Filament\Tables\Testing\TestsRecords;
use Filament\Tables\Testing\TestsSummaries;
use Illuminate\Filesystem\Filesystem;
use Livewire\Testing\TestableLivewire;
use Spatie\LaravelPackageTools\Package;
@@ -61,5 +62,6 @@ class TablesServiceProvider extends PackageServiceProvider
TestableLivewire::mixin(new TestsColumns());
TestableLivewire::mixin(new TestsFilters());
TestableLivewire::mixin(new TestsRecords());
TestableLivewire::mixin(new TestsSummaries());
}
}
@@ -0,0 +1,88 @@
<?php
namespace Filament\Tables\Testing;
use Closure;
use Filament\Tables\Columns\Column;
use Filament\Tables\Columns\Summarizers\Summarizer;
use Filament\Tables\Contracts\HasTable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Testing\Assert;
use Livewire\Testing\TestableLivewire;
/**
* @method HasTable instance()
*
* @mixin TestableLivewire
*/
class TestsSummaries
{
public function assertTableColumnSummarySet(): Closure
{
return function (string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static {
/** @phpstan-ignore-next-line */
$this->assertTableColumnSummarizerExists($columnName, $summarizerId);
$summarizer = $this->instance()->getTable()->getColumn($columnName)->getSummarizer($summarizerId);
$query = $isCurrentPaginationPageOnly ?
$this->instance()->getPageTableSummaryQuery() :
$this->instance()->getAllTableSummaryQuery();
$livewireClass = $this->instance()::class;
Assert::assertEquals(
$state,
$summarizer->query($query)->getState(),
message: "Failed asserting that summarizer [$summarizerId], for column [{$columnName}], on the [{$livewireClass}] component, is set.",
);
return $this;
};
}
public function assertTableColumnSummaryNotSet(): Closure
{
return function (string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static {
/** @phpstan-ignore-next-line */
$this->assertTableColumnSummarizerExists($columnName, $summarizerId);
$summarizer = $this->instance()->getTable()->getColumn($columnName)->getSummarizer($summarizerId);
$query = $isCurrentPaginationPageOnly ?
$this->instance()->getPageTableSummaryQuery() :
$this->instance()->getAllTableSummaryQuery();
$livewireClass = $this->instance()::class;
Assert::assertNotEquals(
$state,
$summarizer->query($query)->getState(),
message: "Failed asserting that summarizer [$summarizerId], for column [{$columnName}], on the [{$livewireClass}] component, is not set.",
);
return $this;
};
}
public function assertTableColumnSummarizerExists(): Closure
{
return function (string $columnName, string $summarizerId): static {
$this->assertTableColumnExists($columnName);
$column = $this->instance()->getTable()->getColumn($columnName);
$summarizer = $column->getSummarizer($summarizerId);
$livewireClass = $this->instance()::class;
Assert::assertInstanceOf(
Summarizer::class,
$summarizer,
message: "Failed asserting that a table column with name [{$columnName}] has a summarizer with ID [{$summarizerId}] on the [{$livewireClass}] component. Please ensure that the ID is passed to the summarizer with [Summarizer::make('{$summarizerId}')].",
);
return $this;
};
}
}
@@ -13,9 +13,9 @@ return new class() extends Migration
$table->foreignId('author_id');
$table->text('content')->nullable();
$table->boolean('is_published')->default(true);
$table->unsignedTinyInteger('rating')->default(0);
$table->json('tags')->nullable();
$table->string('title');
$table->unsignedTinyInteger('rating')->default(0);
$table->timestamps();
});
}
+28 -4
View File
@@ -8,7 +8,8 @@ use Filament\Tables;
use Filament\Tables\Table;
use Filament\Tests\Models\Post;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder;
use Livewire\Component;
class PostsTable extends Component implements Tables\Contracts\HasTable
@@ -30,8 +31,31 @@ class PostsTable extends Component implements Tables\Contracts\HasTable
->action(
Tables\Actions\Action::make('column-action-object')
->action(fn () => $this->emit('column-action-object-called')),
),
Tables\Columns\IconColumn::make('is_published')->boolean(),
)
->summarize([
Tables\Columns\Summarizers\Range::make('range'),
Tables\Columns\Summarizers\Range::make('published_range')
->query(fn (Builder $query) => $query->where('is_published', true)),
]),
Tables\Columns\IconColumn::make('is_published')
->boolean()
->summarize([
Tables\Columns\Summarizers\Count::make('published_count')
->query(fn (Builder $query) => $query->where('is_published', true)),
]),
Tables\Columns\TextColumn::make('rating')
->summarize([
Tables\Columns\Summarizers\Average::make('average'),
Tables\Columns\Summarizers\Average::make('published_average')
->query(fn (Builder $query) => $query->where('is_published', true)),
Tables\Columns\Summarizers\Count::make('count'),
Tables\Columns\Summarizers\Range::make('range'),
Tables\Columns\Summarizers\Range::make('published_range')
->query(fn (Builder $query) => $query->where('is_published', true)),
Tables\Columns\Summarizers\Sum::make('sum'),
Tables\Columns\Summarizers\Sum::make('published_sum')
->query(fn (Builder $query) => $query->where('is_published', true)),
]),
Tables\Columns\TextColumn::make('visible'),
Tables\Columns\TextColumn::make('hidden')
->hidden(),
@@ -40,7 +64,7 @@ class PostsTable extends Component implements Tables\Contracts\HasTable
])
->filters([
Tables\Filters\Filter::make('is_published')
->query(fn (Builder $query) => $query->where('is_published', true)),
->query(fn (EloquentBuilder $query) => $query->where('is_published', true)),
Tables\Filters\SelectFilter::make('author')
->relationship('author', 'name'),
Tables\Filters\SelectFilter::make('select_filter_attribute')
+152
View File
@@ -0,0 +1,152 @@
<?php
use Filament\Tests\Models\Post;
use Filament\Tests\Tables\Fixtures\PostsTable;
use Filament\Tests\Tables\TestCase;
use function Pest\Livewire\livewire;
uses(TestCase::class);
it('can average values in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'average', $posts->avg('rating'));
});
it('can average values in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'average', $posts->take(10)->avg('rating'), isCurrentPaginationPageOnly: true);
});
it('can average subset of values in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'published_average', $posts->where('is_published', true)->avg('rating'));
});
it('can average subset of values in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'published_average', $posts->take(10)->where('is_published', true)->avg('rating'), isCurrentPaginationPageOnly: true);
});
it('can count rows', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'count', $posts->count());
});
it('can count rows on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'count', $posts->take(10)->count(), isCurrentPaginationPageOnly: true);
});
it('can count subset of rows', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('is_published', 'published_count', $posts->where('is_published', true)->count());
});
it('can count subset of rows on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('is_published', 'published_count', $posts->take(10)->where('is_published', true)->count(), isCurrentPaginationPageOnly: true);
});
it('can get the range of values in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'range', [$posts->min('rating'), $posts->max('rating')]);
});
it('can get the range of values in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'range', [$posts->take(10)->min('rating'), $posts->take(10)->max('rating')], isCurrentPaginationPageOnly: true);
});
it('can get the range of a subset of values in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'published_range', [$posts->where('is_published', true)->min('rating'), $posts->where('is_published', true)->max('rating')]);
});
it('can get the range of a subset of values in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'published_range', [$posts->take(10)->where('is_published', true)->min('rating'), $posts->take(10)->where('is_published', true)->max('rating')], isCurrentPaginationPageOnly: true);
});
it('can get the range of values from a relationship in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('author.name', 'range', [$posts->min('author.name'), $posts->max('author.name')]);
});
it('can get the range of values from a relationship in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('author.name', 'range', [$posts->take(10)->min('author.name'), $posts->take(10)->max('author.name')], isCurrentPaginationPageOnly: true);
});
it('can sum values in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'sum', $posts->sum('rating'));
});
it('can sum values in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'sum', $posts->take(10)->sum('rating'), isCurrentPaginationPageOnly: true);
});
it('can sum subset of values in a column', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'published_sum', $posts->where('is_published', true)->sum('rating'));
});
it('can sum subset of values in a column on this pagination page', function () {
$posts = Post::factory()->count(20)->create();
livewire(PostsTable::class)
->assertCanSeeTableRecords($posts->take(10))
->assertTableColumnSummarySet('rating', 'published_sum', $posts->take(10)->where('is_published', true)->sum('rating'), isCurrentPaginationPageOnly: true);
});