mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
Add more type safety
This commit is contained in:
@@ -1,33 +1,33 @@
|
||||
name: npm-build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 2.x
|
||||
types: [closed]
|
||||
pull_request:
|
||||
branches:
|
||||
- 2.x
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
npm-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: 2.x
|
||||
npm-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: 2.x
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build assets
|
||||
run: npm run build
|
||||
- name: Build assets
|
||||
run: npm run build
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
branch: 2.x
|
||||
commit_message: >
|
||||
chore: build assets
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
branch: 2.x
|
||||
commit_message: >
|
||||
chore: build assets
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
name: phpstan
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
phpstan:
|
||||
name: phpstan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.0'
|
||||
coverage: none
|
||||
|
||||
- name: Install composer dependencies
|
||||
uses: ramsey/composer-install@v1
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan --error-format=github
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./vendor/bin/pest --parallel"
|
||||
"test": "./vendor/bin/pest --parallel && ./vendor/bin/phpstan analyse"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace Filament\Commands;
|
||||
|
||||
use Filament\Facades\Filament;
|
||||
use Illuminate\Auth\EloquentUserProvider;
|
||||
use Illuminate\Auth\SessionGuard;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
@@ -16,9 +18,13 @@ class MakeUserCommand extends Command
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
/** @var SessionGuard $auth */
|
||||
$auth = Filament::auth();
|
||||
|
||||
$userModel = $auth->getProvider()->getModel();
|
||||
/** @var EloquentUserProvider $userProvider */
|
||||
$userProvider = $auth->getProvider();
|
||||
|
||||
$userModel = $userProvider->getModel();
|
||||
|
||||
$user = $userModel::create([
|
||||
'name' => $this->validateInput(fn () => $this->ask('Name'), 'name', ['required']),
|
||||
@@ -29,7 +35,7 @@ class MakeUserCommand extends Command
|
||||
$loginUrl = route('filament.auth.login');
|
||||
$this->info("Success! {$user->email} may now log in at {$loginUrl}.");
|
||||
|
||||
if ($auth->getProvider()->getModel()::count() === 1 && $this->confirm('Would you like to show some love by starring the repo?', true)) {
|
||||
if ($userProvider->getModel()::count() === 1 && $this->confirm('Would you like to show some love by starring the repo?', true)) {
|
||||
if (PHP_OS_FAMILY === 'Darwin') {
|
||||
exec('open https://github.com/laravel-filament/filament');
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use Filament\Events\ServingFilament;
|
||||
use Filament\Models\Contracts\HasAvatar;
|
||||
use Filament\Models\Contracts\HasName;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Contracts\Auth\StatefulGuard;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
@@ -33,7 +35,7 @@ class FilamentManager
|
||||
|
||||
protected array $widgets = [];
|
||||
|
||||
public function auth(): StatefulGuard
|
||||
public function auth(): Guard
|
||||
{
|
||||
return auth()->guard(config('filament.auth.guard'));
|
||||
}
|
||||
@@ -190,7 +192,7 @@ class FilamentManager
|
||||
return $firstItem->getUrl();
|
||||
}
|
||||
|
||||
public function getUserAvatarUrl(Authenticatable $user): string
|
||||
public function getUserAvatarUrl(Model $user): string
|
||||
{
|
||||
$avatar = null;
|
||||
|
||||
@@ -207,13 +209,13 @@ class FilamentManager
|
||||
return (new $provider())->get($user);
|
||||
}
|
||||
|
||||
public function getUserName(Authenticatable $user): string
|
||||
public function getUserName(Model $user): string
|
||||
{
|
||||
if ($user instanceof HasName) {
|
||||
return $user->getFilamentName();
|
||||
}
|
||||
|
||||
return $user->name;
|
||||
return $user->getAttributeValue('name');
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
|
||||
@@ -19,7 +19,7 @@ use Livewire\Livewire;
|
||||
use ReflectionClass;
|
||||
use Spatie\LaravelPackageTools\Package;
|
||||
use Spatie\LaravelPackageTools\PackageServiceProvider;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class FilamentServiceProvider extends PackageServiceProvider
|
||||
{
|
||||
|
||||
@@ -5,13 +5,18 @@ namespace Filament\Http\Livewire\Auth;
|
||||
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
use Livewire\Macros\ViewMacros;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $form
|
||||
*/
|
||||
class Login extends Component implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
@@ -76,9 +81,17 @@ class Login extends Component implements HasForms
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('filament::login')
|
||||
->layout('filament::components.layouts.base', [
|
||||
'title' => __('filament::login.title'),
|
||||
]);
|
||||
$view = view('filament::login');
|
||||
|
||||
/*
|
||||
* Livewire uses a macro for the `layout()` method.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
$view->layout('filament::components.layouts.base', [
|
||||
'title' => __('filament::login.title'),
|
||||
]);
|
||||
|
||||
return $view;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class Authenticate extends Middleware
|
||||
|
||||
$this->auth->shouldUse($guardName);
|
||||
|
||||
$user = $this->auth->user();
|
||||
$user = $guard->user();
|
||||
|
||||
if (config('app.env') === 'local') {
|
||||
return;
|
||||
|
||||
@@ -9,6 +9,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
use Livewire\Macros\ViewMacros;
|
||||
|
||||
class Page extends Component
|
||||
{
|
||||
@@ -86,8 +87,16 @@ class Page extends Component
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view(static::$view, $this->getViewData())
|
||||
->layout(static::$layout, $this->getLayoutData());
|
||||
$view = view(static::$view, $this->getViewData());
|
||||
|
||||
/*
|
||||
* Livewire uses a macro for the `layout()` method.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
$view->layout(static::$layout, $this->getLayoutData());
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function getBreadcrumbs(): array
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
namespace Filament\Resources\Pages;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Pages\Actions\ButtonAction;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $form
|
||||
*/
|
||||
class CreateRecord extends Page implements Forms\Contracts\HasForms
|
||||
{
|
||||
use Concerns\UsesResourceForm;
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
namespace Filament\Resources\Pages;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Pages\Actions\ButtonAction;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $form
|
||||
*/
|
||||
class EditRecord extends Page implements Forms\Contracts\HasForms
|
||||
{
|
||||
use Concerns\HasRecordBreadcrumb;
|
||||
|
||||
@@ -83,7 +83,7 @@ class ListRecords extends Page implements Tables\Contracts\HasTable
|
||||
{
|
||||
return Tables\Actions\BulkAction::make('delete')
|
||||
->label(__('filament::resources/pages/list-records.table.bulk_actions.delete.label'))
|
||||
->action(fn (Collection $records) => $records->each->delete())
|
||||
->action(fn (Collection $records) => $records->each(fn (Model $record) => $record->delete()))
|
||||
->requiresConfirmation()
|
||||
->deselectRecordsAfterCompletion()
|
||||
->color('danger')
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
namespace Filament\Resources\Pages;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Pages\Actions\ButtonAction;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $form
|
||||
*/
|
||||
class ViewRecord extends Page implements Forms\Contracts\HasForms
|
||||
{
|
||||
use Concerns\HasRecordBreadcrumb;
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Filament\Resources\RelationManagers;
|
||||
|
||||
use Filament\Resources\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class BelongsToManyRelationManager extends RelationManager
|
||||
@@ -55,7 +57,9 @@ class BelongsToManyRelationManager extends RelationManager
|
||||
|
||||
$this->callHook('beforeCreate');
|
||||
|
||||
/** @var BelongsToMany $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$pivotColumns = $relationship->getPivotColumns();
|
||||
$pivotData = Arr::only($data, $pivotColumns);
|
||||
$data = Arr::except($data, $pivotColumns);
|
||||
@@ -79,7 +83,9 @@ class BelongsToManyRelationManager extends RelationManager
|
||||
|
||||
$this->callHook('beforeSave');
|
||||
|
||||
/** @var BelongsToMany $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$pivotColumns = $relationship->getPivotColumns();
|
||||
$pivotData = Arr::only($data, $pivotColumns);
|
||||
$data = Arr::except($data, $pivotColumns);
|
||||
@@ -96,6 +102,15 @@ class BelongsToManyRelationManager extends RelationManager
|
||||
{
|
||||
$query = parent::getTableQuery();
|
||||
|
||||
return $query->select($this->getRelationship()->getTable().'.*', $query->getModel()->getTable().'.*');
|
||||
/** @var BelongsToMany $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
/** @var Builder $query */
|
||||
$query->select(
|
||||
$relationship->getTable().'.*',
|
||||
$query->getModel()->getTable().'.*',
|
||||
);
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use Filament\Resources\Form;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Actions\Modal\Actions\ButtonAction;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
trait CanAttachRecords
|
||||
@@ -45,10 +47,15 @@ trait CanAttachRecords
|
||||
|
||||
$displayColumnName = static::getRecordTitleAttribute();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($displayColumnName);
|
||||
/** @var Builder $relationshipQuery */
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($displayColumnName);
|
||||
|
||||
$query = strtolower($query);
|
||||
$searchOperator = match ($relationshipQuery->getConnection()->getDriverName()) {
|
||||
|
||||
/** @var Connection $databaseConnection */
|
||||
$databaseConnection = $relationshipQuery->getConnection();
|
||||
|
||||
$searchOperator = match ($databaseConnection->getDriverName()) {
|
||||
'pgsql' => 'ilike',
|
||||
default => 'like',
|
||||
};
|
||||
@@ -61,7 +68,7 @@ trait CanAttachRecords
|
||||
->pluck($displayColumnName, $relationship->getRelated()->getKeyName())
|
||||
->toArray();
|
||||
})
|
||||
->getOptionLabelUsing(fn (RelationManager $livewire, $value): ?string => static::getRecordTitle($livewire->getRelationship()->getRelated()->find($value)))
|
||||
->getOptionLabelUsing(fn (RelationManager $livewire, $value): ?string => static::getRecordTitle($livewire->getRelationship()->getRelated()->query()->find($value)))
|
||||
->disableLabel();
|
||||
}
|
||||
|
||||
@@ -95,10 +102,12 @@ trait CanAttachRecords
|
||||
|
||||
$this->callHook('beforeCreate');
|
||||
|
||||
/** @var BelongsToMany $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$pivotColumns = $relationship->getPivotColumns();
|
||||
|
||||
$record = $relationship->getRelated()->find($data['recordId']);
|
||||
$record = $relationship->getRelated()->query()->find($data['recordId']);
|
||||
$relationship->attach($record, Arr::only($data, $pivotColumns));
|
||||
|
||||
$this->callHook('afterCreate');
|
||||
|
||||
@@ -43,7 +43,7 @@ trait CanDeleteRecords
|
||||
{
|
||||
return Tables\Actions\BulkAction::make('delete')
|
||||
->label(__('filament::resources/relation-managers/delete.bulk_action.label'))
|
||||
->action(fn (Collection $records) => $records->each->delete())
|
||||
->action(fn (Collection $records) => $records->each(fn (Model $record) => $record->delete()))
|
||||
->requiresConfirmation()
|
||||
->modalHeading(__('filament::resources/relation-managers/delete.bulk_action.modal.heading', ['label' => static::getPluralRecordLabel()]))
|
||||
->deselectRecordsAfterCompletion()
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Filament\Resources\RelationManagers\Concerns;
|
||||
use Filament\Tables;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
trait CanDetachRecords
|
||||
{
|
||||
@@ -22,7 +23,10 @@ trait CanDetachRecords
|
||||
{
|
||||
$this->callHook('beforeDetach');
|
||||
|
||||
$this->getRelationship()->detach($this->getMountedTableActionRecord());
|
||||
/** @var BelongsToMany $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$relationship->detach($this->getMountedTableActionRecord());
|
||||
|
||||
$this->callHook('afterDetach');
|
||||
}
|
||||
@@ -43,7 +47,12 @@ trait CanDetachRecords
|
||||
{
|
||||
return Tables\Actions\BulkAction::make('detach')
|
||||
->label(__('filament::resources/relation-managers/detach.bulk_action.label'))
|
||||
->action(fn (Collection $records) => $this->getRelationship()->detach($records))
|
||||
->action(function (Collection $records) {
|
||||
/** @var BelongsToMany $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$relationship->detach($records);
|
||||
})
|
||||
->requiresConfirmation()
|
||||
->modalHeading(__('filament::resources/relation-managers/detach.bulk_action.modal.heading', ['label' => static::getPluralRecordLabel()]))
|
||||
->deselectRecordsAfterCompletion()
|
||||
|
||||
@@ -11,6 +11,9 @@ class HasManyThroughRelationManager extends HasManyRelationManager
|
||||
{
|
||||
$query = parent::getTableQuery();
|
||||
|
||||
return $query->select($query->getModel()->getTable().'.*');
|
||||
/** @var Builder $query */
|
||||
$query->select($query->getModel()->getTable().'.*');
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,15 @@ class RelationManager extends Component implements Tables\Contracts\HasTable
|
||||
return $this->ownerRecord->{static::getRelationshipName()}();
|
||||
}
|
||||
|
||||
protected function getResourceTable(): Table
|
||||
{
|
||||
if (! $this->resourceTable) {
|
||||
$this->resourceTable = Table::make();
|
||||
}
|
||||
|
||||
return $this->resourceTable;
|
||||
}
|
||||
|
||||
protected function getDefaultTableSortColumn(): ?string
|
||||
{
|
||||
return $this->getResourceTable()->getDefaultSortColumn();
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Filament\Resources;
|
||||
use Closure;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Navigation\NavigationItem;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -250,7 +251,7 @@ class Resource
|
||||
return function () {
|
||||
$slug = static::getSlug();
|
||||
|
||||
Route::name("{$slug}.")->prefix($slug)->group(function () use ($slug) {
|
||||
Route::name("{$slug}.")->prefix($slug)->group(function () {
|
||||
foreach (static::getPages() as $name => $page) {
|
||||
Route::get($page['route'], $page['class'])->name($name);
|
||||
}
|
||||
@@ -284,7 +285,10 @@ class Resource
|
||||
|
||||
protected static function applyGlobalSearchAttributeConstraint(Builder $query, array $searchAttributes, string $searchQuery, bool &$isFirst): Builder
|
||||
{
|
||||
$searchOperator = match ($query->getConnection()->getDriverName()) {
|
||||
/** @var Connection $databaseConnection */
|
||||
$databaseConnection = $query->getConnection();
|
||||
|
||||
$searchOperator = match ($databaseConnection->getDriverName()) {
|
||||
'pgsql' => 'ilike',
|
||||
default => 'like',
|
||||
};
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
"spatie/laravel-package-tools": "^1.9"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Filament\\Forms\\": "src"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Filament\Forms\Components;
|
||||
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\AwsS3v3\AwsS3Adapter;
|
||||
use SplFileInfo;
|
||||
@@ -46,7 +47,10 @@ class BaseFileUpload extends Field
|
||||
return;
|
||||
}
|
||||
|
||||
$component->getContainer()->getParentComponent()->appendNewUploadField();
|
||||
/** @var MultipleFileUpload $parentComponent */
|
||||
$parentComponent = $component->getContainer()->getParentComponent();
|
||||
|
||||
$parentComponent->appendNewUploadField();
|
||||
});
|
||||
|
||||
$this->dehydrated(fn (BaseFileUpload $component): bool => ! $component->isMultiple());
|
||||
@@ -222,10 +226,14 @@ class BaseFileUpload extends Field
|
||||
|
||||
protected function handleUploadedFileUrlRetrieval($file): ?string
|
||||
{
|
||||
/** @var FilesystemAdapter $storage */
|
||||
$storage = $this->getDisk();
|
||||
|
||||
/** @var \League\Flysystem\Filesystem $storageDriver */
|
||||
$storageDriver = $storage->getDriver();
|
||||
|
||||
if (
|
||||
$storage->getDriver()->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storageDriver->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storage->getVisibility($file) === 'private'
|
||||
) {
|
||||
return $storage->temporaryUrl(
|
||||
@@ -266,4 +274,44 @@ class BaseFileUpload extends Field
|
||||
|
||||
return $file->{$storeMethod}($this->getDirectory(), $this->getDiskName());
|
||||
}
|
||||
|
||||
public function isMultiple(): bool
|
||||
{
|
||||
return $this->getContainer()->getParentComponent() instanceof MultipleFileUpload;
|
||||
}
|
||||
|
||||
protected function handleUploadedFileRemoval($file): void
|
||||
{
|
||||
$this->state(null);
|
||||
}
|
||||
|
||||
protected function handleUploadedFileDeletion($file): void
|
||||
{
|
||||
}
|
||||
|
||||
public function removeUploadedFile(): static
|
||||
{
|
||||
$file = $this->getState();
|
||||
|
||||
if ($callback = $this->removeUploadedFileUsing) {
|
||||
$this->evaluate($callback, [
|
||||
'file' => $file,
|
||||
]);
|
||||
} else {
|
||||
$this->handleUploadedFileRemoval($file);
|
||||
}
|
||||
|
||||
if ($this->isMultiple()) {
|
||||
$container = $this->getContainer();
|
||||
|
||||
/** @var MultipleFileUpload $parentComponent */
|
||||
$parentComponent = $container->getParentComponent();
|
||||
|
||||
$parentComponent->removeUploadedFile(
|
||||
$container->getStatePath(isAbsolute: false),
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Filament\Forms\Components;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -18,7 +19,7 @@ class BelongsToManyMultiSelect extends MultiSelect
|
||||
parent::setUp();
|
||||
|
||||
$this->afterStateHydrated(function (BelongsToManyMultiSelect $component): void {
|
||||
if (count($this->getState() ?? [])) {
|
||||
if (count($component->getState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,7 +56,7 @@ class BelongsToManyMultiSelect extends MultiSelect
|
||||
$relationship = $component->getRelationship();
|
||||
$relatedKeyName = $relationship->getRelatedKeyName();
|
||||
|
||||
return $relationship->getRelated()
|
||||
return $relationship->getRelated()->query()
|
||||
->whereIn($relatedKeyName, $values)
|
||||
->pluck($component->getDisplayColumnName(), $relatedKeyName)
|
||||
->toArray();
|
||||
@@ -64,7 +65,7 @@ class BelongsToManyMultiSelect extends MultiSelect
|
||||
$this->getSearchResultsUsing(function (BelongsToManyMultiSelect $component, ?string $query) use ($callback): array {
|
||||
$relationship = $component->getRelationship();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($component->getDisplayColumnName());
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($component->getDisplayColumnName());
|
||||
|
||||
if ($callback) {
|
||||
$relationshipQuery = $this->evaluate($callback, [
|
||||
@@ -73,7 +74,11 @@ class BelongsToManyMultiSelect extends MultiSelect
|
||||
}
|
||||
|
||||
$query = strtolower($query);
|
||||
$searchOperator = match ($relationshipQuery->getConnection()->getDriverName()) {
|
||||
|
||||
/** @var Connection $databaseConnection */
|
||||
$databaseConnection = $relationshipQuery->getConnection();
|
||||
|
||||
$searchOperator = match ($databaseConnection->getDriverName()) {
|
||||
'pgsql' => 'ilike',
|
||||
default => 'like',
|
||||
};
|
||||
@@ -91,7 +96,7 @@ class BelongsToManyMultiSelect extends MultiSelect
|
||||
|
||||
$relationship = $component->getRelationship();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($component->getDisplayColumnName());
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($component->getDisplayColumnName());
|
||||
|
||||
if ($callback) {
|
||||
$relationshipQuery = $this->evaluate($callback, [
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Filament\Forms\Components;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -52,15 +53,15 @@ class BelongsToSelect extends Select
|
||||
$this->getOptionLabelUsing(function (BelongsToSelect $component, $value) {
|
||||
$relationship = $component->getRelationship();
|
||||
|
||||
$record = $relationship->getRelated()->where($relationship->getOwnerKeyName(), $value)->first();
|
||||
$record = $relationship->getRelated()->query()->where($relationship->getOwnerKeyName(), $value)->first();
|
||||
|
||||
return $record ? $record->getAttributeValue($component->getDisplayColumnName()) : null;
|
||||
return $record?->getAttributeValue($component->getDisplayColumnName());
|
||||
});
|
||||
|
||||
$this->getSearchResultsUsing(function (BelongsToSelect $component, ?string $query) use ($callback): array {
|
||||
$relationship = $component->getRelationship();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($component->getDisplayColumnName());
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($component->getDisplayColumnName());
|
||||
|
||||
if ($callback) {
|
||||
$relationshipQuery = $this->evaluate($callback, [
|
||||
@@ -69,7 +70,11 @@ class BelongsToSelect extends Select
|
||||
}
|
||||
|
||||
$query = strtolower($query);
|
||||
$searchOperator = match ($relationshipQuery->getConnection()->getDriverName()) {
|
||||
|
||||
/** @var Connection $databaseConnection */
|
||||
$databaseConnection = $relationshipQuery->getConnection();
|
||||
|
||||
$searchOperator = match ($databaseConnection->getDriverName()) {
|
||||
'pgsql' => 'ilike',
|
||||
default => 'like',
|
||||
};
|
||||
@@ -87,7 +92,7 @@ class BelongsToSelect extends Select
|
||||
|
||||
$relationship = $component->getRelationship();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($component->getDisplayColumnName());
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($component->getDisplayColumnName());
|
||||
|
||||
if ($callback) {
|
||||
$relationshipQuery = $this->evaluate($callback, [
|
||||
|
||||
@@ -6,6 +6,8 @@ use Filament\Forms\ComponentContainer;
|
||||
use Filament\Forms\Components\Builder\Block;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use function Filament\Forms\array_move_after;
|
||||
use function Filament\Forms\array_move_before;
|
||||
|
||||
class Builder extends Field
|
||||
{
|
||||
@@ -93,7 +95,7 @@ class Builder extends Field
|
||||
return;
|
||||
}
|
||||
|
||||
$items = Arr::moveElementAfter($component->getNormalisedState(), $uuidToMoveDown);
|
||||
$items = array_move_after($component->getNormalisedState(), $uuidToMoveDown);
|
||||
|
||||
$livewire = $component->getLivewire();
|
||||
data_set($livewire, $statePath, $items);
|
||||
@@ -113,7 +115,7 @@ class Builder extends Field
|
||||
return;
|
||||
}
|
||||
|
||||
$items = Arr::moveElementBefore($component->getNormalisedState(), $uuidToMoveUp);
|
||||
$items = array_move_before($component->getNormalisedState(), $uuidToMoveUp);
|
||||
|
||||
$livewire = $component->getLivewire();
|
||||
data_set($livewire, $statePath, $items);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Filament\Forms\Components\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Filament\Forms\Components\Component;
|
||||
use Filament\Forms\Components\Field;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Unique;
|
||||
@@ -18,7 +18,7 @@ trait CanBeValidated
|
||||
|
||||
public function exists(string | Closure | null $table = null, string | Closure | null $column = null, ?Closure $callback = null): static
|
||||
{
|
||||
$this->rule(function (Component $component) use ($callback, $column, $table) {
|
||||
$this->rule(function (Field $component) use ($callback, $column, $table) {
|
||||
$table = $component->evaluate($table) ?? $component->getModelClass();
|
||||
$column = $component->evaluate($column) ?? $component->getName();
|
||||
|
||||
@@ -31,14 +31,14 @@ trait CanBeValidated
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}, fn (Component $component): bool => (bool) ($component->evaluate($table) ?? $component->getModelClass()));
|
||||
}, fn (Field $component): bool => (bool) ($component->evaluate($table) ?? $component->getModelClass()));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function nullable(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->required(function (Component $component) use ($condition): bool {
|
||||
$this->required(function (Field $component) use ($condition): bool {
|
||||
return ! $component->evaluate($condition);
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ trait CanBeValidated
|
||||
|
||||
public function unique(string | Closure | null $table = null, string | Closure | null $column = null, Model | Closure $ignorable = null, ?Closure $callback = null): static
|
||||
{
|
||||
$this->rule(function (Component $component) use ($callback, $column, $ignorable, $table) {
|
||||
$this->rule(function (Field $component) use ($callback, $column, $ignorable, $table) {
|
||||
$table = $component->evaluate($table) ?? $component->getModelClass();
|
||||
$column = $component->evaluate($column) ?? $component->getName();
|
||||
$ignorable = $component->evaluate($ignorable);
|
||||
@@ -145,7 +145,7 @@ trait CanBeValidated
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}, fn (Component $component): bool => (bool) ($component->evaluate($table) ?? $component->getModelClass()));
|
||||
}, fn (Field $component): bool => (bool) ($component->evaluate($table) ?? $component->getModelClass()));
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ trait CanBeValidated
|
||||
|
||||
protected function dateComparisonRule(string $rule, string | Closure $date, bool $isStatePathAbsolute = false): static
|
||||
{
|
||||
$this->rule(function (Component $component) use ($date, $isStatePathAbsolute, $rule): string {
|
||||
$this->rule(function (Field $component) use ($date, $isStatePathAbsolute, $rule): string {
|
||||
$date = $component->evaluate($date);
|
||||
|
||||
if (! (strtotime($date) && $isStatePathAbsolute)) {
|
||||
@@ -203,14 +203,14 @@ trait CanBeValidated
|
||||
}
|
||||
|
||||
return "{$rule}:{$date}";
|
||||
}, fn (Component $component): bool => (bool) $component->evaluate($date));
|
||||
}, fn (Field $component): bool => (bool) $component->evaluate($date));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fieldComparisonRule(string $rule, string | Closure $statePath, bool $isStatePathAbsolute = false): static
|
||||
{
|
||||
$this->rule(function (Component $component) use ($isStatePathAbsolute, $rule, $statePath): string {
|
||||
$this->rule(function (Field $component) use ($isStatePathAbsolute, $rule, $statePath): string {
|
||||
$statePath = $component->evaluate($statePath);
|
||||
|
||||
if (! $isStatePathAbsolute) {
|
||||
@@ -222,7 +222,7 @@ trait CanBeValidated
|
||||
}
|
||||
|
||||
return "{$rule}:{$statePath}";
|
||||
}, fn (Component $component): bool => (bool) $component->evaluate($statePath));
|
||||
}, fn (Field $component): bool => (bool) $component->evaluate($statePath));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ trait CanLimitItemsLength
|
||||
|
||||
$this->rule('array');
|
||||
$this->rule(function (Component $component): string {
|
||||
/** @var static $component */
|
||||
|
||||
$count = $component->getMaxItems();
|
||||
|
||||
return "max:{$count}";
|
||||
@@ -31,6 +33,8 @@ trait CanLimitItemsLength
|
||||
|
||||
$this->rule('array');
|
||||
$this->rule(function (Component $component): string {
|
||||
/** @var static $component */
|
||||
|
||||
$count = $component->getMinItems();
|
||||
|
||||
return "min:{$count}";
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Filament\Forms\Components\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\AwsS3v3\AwsS3Adapter;
|
||||
use SplFileInfo;
|
||||
@@ -103,10 +104,14 @@ trait HasFileAttachments
|
||||
|
||||
protected function handleUploadedAttachmentUrlRetrieval($file): ?string
|
||||
{
|
||||
/** @var FilesystemAdapter $storage */
|
||||
$storage = $this->getFileAttachmentsDisk();
|
||||
|
||||
/** @var \League\Flysystem\Filesystem $storageDriver */
|
||||
$storageDriver = $storage->getDriver();
|
||||
|
||||
if (
|
||||
$storage->getDriver()->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storageDriver->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storage->getVisibility($file) === 'private'
|
||||
) {
|
||||
return $storage->temporaryUrl(
|
||||
|
||||
@@ -188,40 +188,4 @@ class FileUpload extends BaseFileUpload
|
||||
{
|
||||
return (bool) $this->evaluate($this->isAvatar);
|
||||
}
|
||||
|
||||
public function isMultiple(): bool
|
||||
{
|
||||
return $this->getContainer()->getParentComponent() instanceof MultipleFileUpload;
|
||||
}
|
||||
|
||||
protected function handleUploadedFileRemoval($file): void
|
||||
{
|
||||
$this->state(null);
|
||||
}
|
||||
|
||||
protected function handleUploadedFileDeletion($file): void
|
||||
{
|
||||
}
|
||||
|
||||
public function removeUploadedFile(): static
|
||||
{
|
||||
$file = $this->getState();
|
||||
|
||||
if ($callback = $this->removeUploadedFileUsing) {
|
||||
$this->evaluate($callback, [
|
||||
'file' => $file,
|
||||
]);
|
||||
} else {
|
||||
$this->handleUploadedFileRemoval($file);
|
||||
}
|
||||
|
||||
if ($this->isMultiple()) {
|
||||
$container = $this->getContainer();
|
||||
$container->getParentComponent()->removeUploadedFile(
|
||||
$container->getStatePath(isAbsolute: false),
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Filament\Forms\Components;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use function Filament\Forms\array_move_after;
|
||||
use function Filament\Forms\array_move_before;
|
||||
|
||||
class Repeater extends Field
|
||||
{
|
||||
@@ -73,7 +75,7 @@ class Repeater extends Field
|
||||
return;
|
||||
}
|
||||
|
||||
$items = Arr::moveElementAfter($component->getNormalisedState(), $uuidToMoveDown);
|
||||
$items = array_move_after($component->getNormalisedState(), $uuidToMoveDown);
|
||||
|
||||
$livewire = $component->getLivewire();
|
||||
data_set($livewire, $statePath, $items);
|
||||
@@ -93,7 +95,7 @@ class Repeater extends Field
|
||||
return;
|
||||
}
|
||||
|
||||
$items = Arr::moveElementBefore($component->getNormalisedState(), $uuidToMoveUp);
|
||||
$items = array_move_before($component->getNormalisedState(), $uuidToMoveUp);
|
||||
|
||||
$livewire = $component->getLivewire();
|
||||
data_set($livewire, $statePath, $items);
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
namespace Filament\Forms\Concerns;
|
||||
|
||||
use Filament\Forms\Components\Contracts\HasFileAttachments;
|
||||
use Filament\Forms\Components\Field;
|
||||
|
||||
trait SupportsComponentFileAttachments
|
||||
{
|
||||
public function getComponentFileAttachmentUrl(string $statePath): ?string
|
||||
{
|
||||
foreach ($this->getComponents() as $component) {
|
||||
if ($component instanceof HasFileAttachments && $component->getStatePath() === $statePath) {
|
||||
if ($component instanceof HasFileAttachments && $component instanceof Field && $component->getStatePath() === $statePath) {
|
||||
$attachment = $this->getLivewire()->getComponentFileAttachment($statePath);
|
||||
|
||||
if (! $attachment) {
|
||||
|
||||
@@ -40,43 +40,4 @@ class FormsServiceProvider extends PackageServiceProvider
|
||||
|
||||
return array_merge($commands, $aliases);
|
||||
}
|
||||
|
||||
public function packageBooted(): void
|
||||
{
|
||||
Arr::macro('moveElementAfter', function (array $array, $keyToMoveAfter): array {
|
||||
$keys = array_keys($array);
|
||||
|
||||
$indexToMoveAfter = array_search($keyToMoveAfter, $keys);
|
||||
$keyToMoveBefore = $keys[$indexToMoveAfter + 1];
|
||||
|
||||
$keys[$indexToMoveAfter + 1] = $keyToMoveAfter;
|
||||
$keys[$indexToMoveAfter] = $keyToMoveBefore;
|
||||
|
||||
$newArray = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$newArray[$key] = $array[$key];
|
||||
}
|
||||
|
||||
return $newArray;
|
||||
});
|
||||
|
||||
Arr::macro('moveElementBefore', function (array $array, $keyToMoveBefore): array {
|
||||
$keys = array_keys($array);
|
||||
|
||||
$indexToMoveBefore = array_search($keyToMoveBefore, $keys);
|
||||
$keyToMoveAfter = $keys[$indexToMoveBefore - 1];
|
||||
|
||||
$keys[$indexToMoveBefore - 1] = $keyToMoveBefore;
|
||||
$keys[$indexToMoveBefore] = $keyToMoveAfter;
|
||||
|
||||
$newArray = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$newArray[$key] = $array[$key];
|
||||
}
|
||||
|
||||
return $newArray;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Forms;
|
||||
|
||||
if (! function_exists('Filament\Forms\array_move_after')) {
|
||||
function array_move_after(array $array, $keyToMoveAfter): array {
|
||||
$keys = array_keys($array);
|
||||
|
||||
$indexToMoveAfter = array_search($keyToMoveAfter, $keys);
|
||||
$keyToMoveBefore = $keys[$indexToMoveAfter + 1];
|
||||
|
||||
$keys[$indexToMoveAfter + 1] = $keyToMoveAfter;
|
||||
$keys[$indexToMoveAfter] = $keyToMoveBefore;
|
||||
|
||||
$newArray = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$newArray[$key] = $array[$key];
|
||||
}
|
||||
|
||||
return $newArray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (! function_exists('Filament\Forms\array_move_before')) {
|
||||
function array_move_before(array $array, $keyToMoveBefore): array {
|
||||
$keys = array_keys($array);
|
||||
|
||||
$indexToMoveBefore = array_search($keyToMoveBefore, $keys);
|
||||
$keyToMoveAfter = $keys[$indexToMoveBefore - 1];
|
||||
|
||||
$keys[$indexToMoveBefore - 1] = $keyToMoveBefore;
|
||||
$keys[$indexToMoveBefore] = $keyToMoveAfter;
|
||||
|
||||
$newArray = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$newArray[$key] = $array[$key];
|
||||
}
|
||||
|
||||
return $newArray;
|
||||
}
|
||||
}
|
||||
+26
-7
@@ -3,8 +3,12 @@
|
||||
namespace Filament\Forms\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use League\Flysystem\AwsS3v3\AwsS3Adapter;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\FileAdder;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
use SplFileInfo;
|
||||
|
||||
@@ -89,16 +93,24 @@ class SpatieMediaLibraryFileUpload extends FileUpload
|
||||
|
||||
protected function handleUpload($file)
|
||||
{
|
||||
if (! ($model = $this->getModel())) {
|
||||
$model = $this->getModel();
|
||||
|
||||
if (! $model) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$media = $model
|
||||
->addMediaFromString($file->get())
|
||||
if (! method_exists($model, 'addMediaFromString')) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
/** @var FileAdder $mediaAdder */
|
||||
$mediaAdder = $model->addMediaFromString($file->get());
|
||||
|
||||
$media = $mediaAdder
|
||||
->usingFileName($file->getFilename())
|
||||
->toMediaCollection($this->getCollection());
|
||||
|
||||
return $media->uuid;
|
||||
return $media->getAttributeValue('uuid');
|
||||
}
|
||||
|
||||
protected function handleUploadedFileDeletion($file): void
|
||||
@@ -119,8 +131,12 @@ class SpatieMediaLibraryFileUpload extends FileUpload
|
||||
|
||||
protected function handleUploadedFileUrlRetrieval($file): ?string
|
||||
{
|
||||
/** @var FilesystemAdapter $storage */
|
||||
$storage = $this->getDisk();
|
||||
|
||||
/** @var \League\Flysystem\Filesystem $storageDriver */
|
||||
$storageDriver = $storage->getDriver();
|
||||
|
||||
if (! $this->getModel()) {
|
||||
return null;
|
||||
}
|
||||
@@ -129,13 +145,16 @@ class SpatieMediaLibraryFileUpload extends FileUpload
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var ?Media $media */
|
||||
$media = Media::findByUuid($file);
|
||||
|
||||
if (
|
||||
$storage->getDriver()->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storageDriver->getAdapter() instanceof AwsS3Adapter &&
|
||||
$this->getVisibility() === 'private'
|
||||
) {
|
||||
return Media::findByUuid($file)?->getTemporaryUrl(now()->addMinutes(5));
|
||||
return $media?->getTemporaryUrl(now()->addMinutes(5));
|
||||
}
|
||||
|
||||
return Media::findByUuid($file)?->getUrl();
|
||||
return $media?->getUrl();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -42,8 +42,13 @@ class SpatieMediaLibraryImageColumn extends ImageColumn
|
||||
return $state;
|
||||
}
|
||||
|
||||
return $this->getRecord()
|
||||
->getFirstMediaUrl($this->getCollection(), $this->getConversion());
|
||||
$record = $this->getRecord();
|
||||
|
||||
if (! method_exists($record, 'getFirstMediaUrl')) {
|
||||
return $state;
|
||||
}
|
||||
|
||||
return $record->getFirstMediaUrl($this->getCollection(), $this->getConversion());
|
||||
}
|
||||
|
||||
public function applyEagreLoading(Builder $query): Builder
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
namespace Filament\Pages;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Pages\Actions\ButtonAction;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $form
|
||||
*/
|
||||
class SettingsPage extends Page implements Forms\Contracts\HasForms
|
||||
{
|
||||
use Forms\Concerns\InteractsWithForms;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Filament\Forms\Components;
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Tags\HasTags;
|
||||
use Spatie\Tags\Tag;
|
||||
|
||||
class SpatieTagsInput extends TagsInput
|
||||
@@ -22,6 +23,10 @@ class SpatieTagsInput extends TagsInput
|
||||
return;
|
||||
}
|
||||
|
||||
if (! method_exists($model, 'tagsWithType')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $component->getType();
|
||||
$tags = $model->tagsWithType($type);
|
||||
|
||||
@@ -42,6 +47,10 @@ class SpatieTagsInput extends TagsInput
|
||||
$model = $this->getModel();
|
||||
$tags = $this->getState();
|
||||
|
||||
if (! (method_exists($model, 'syncTagsWithType') && method_exists($model, 'syncTags'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($type = $this->getType()) {
|
||||
$model->syncTagsWithType($tags, $type);
|
||||
|
||||
@@ -69,7 +78,7 @@ class SpatieTagsInput extends TagsInput
|
||||
return Tag::query()
|
||||
->when(
|
||||
filled($type),
|
||||
fn (Builder $query) => $query->withType($type),
|
||||
fn (Builder $query) => $query->where('type', $type),
|
||||
fn (Builder $query) => $query->whereNull('type'),
|
||||
)
|
||||
->pluck('name')
|
||||
|
||||
@@ -8,8 +8,14 @@ class SpatieTagsColumn extends TagsColumn
|
||||
|
||||
public function getTags(): array
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if (! method_exists($record, 'tagsWithType')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$type = $this->getType();
|
||||
$tags = $this->getRecord()->tagsWithType($type);
|
||||
$tags = $record->tagsWithType($type);
|
||||
|
||||
return $tags->pluck('name')->toArray();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Filament\Tables\Columns\Concerns;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
@@ -32,7 +33,10 @@ trait InteractsWithTableQuery
|
||||
return $query;
|
||||
}
|
||||
|
||||
$searchOperator = match ($query->getConnection()->getDriverName()) {
|
||||
/** @var Connection $databaseConnection */
|
||||
$databaseConnection = $query->getConnection();
|
||||
|
||||
$searchOperator = match ($databaseConnection->getDriverName()) {
|
||||
'pgsql' => 'ilike',
|
||||
default => 'like',
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Filament\Tables\Columns;
|
||||
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\AwsS3v3\AwsS3Adapter;
|
||||
|
||||
@@ -94,10 +95,14 @@ class ImageColumn extends Column
|
||||
return $state;
|
||||
}
|
||||
|
||||
/** @var FilesystemAdapter $storage */
|
||||
$storage = $this->getDisk();
|
||||
|
||||
/** @var \League\Flysystem\Filesystem $storageDriver */
|
||||
$storageDriver = $storage->getDriver();
|
||||
|
||||
if (
|
||||
$storage->getDriver()->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storageDriver->getAdapter() instanceof AwsS3Adapter &&
|
||||
$storage->getVisibility($state) === 'private'
|
||||
) {
|
||||
return $storage->temporaryUrl(
|
||||
|
||||
@@ -6,6 +6,9 @@ use Filament\Forms\ComponentContainer;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $mountedTableActionForm
|
||||
*/
|
||||
trait HasActions
|
||||
{
|
||||
public $mountedTableAction = null;
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace Filament\Tables\Concerns;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Tables\Actions\BulkAction;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $mountedTableBulkActionForm
|
||||
*/
|
||||
trait HasBulkActions
|
||||
{
|
||||
public $mountedTableBulkAction = null;
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
namespace Filament\Tables\Concerns;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* @property ComponentContainer $tableFiltersForm
|
||||
*/
|
||||
trait HasFilters
|
||||
{
|
||||
protected array $cachedTableFilters;
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
namespace Filament\Tables\Concerns;
|
||||
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
trait HasRecords
|
||||
{
|
||||
protected Collection | LengthAwarePaginator | null $records = null;
|
||||
protected Collection | Paginator | null $records = null;
|
||||
|
||||
protected function getFilteredTableQuery(): Builder
|
||||
{
|
||||
@@ -22,7 +23,7 @@ trait HasRecords
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function getTableRecords(): Collection | LengthAwarePaginator
|
||||
public function getTableRecords(): Collection | Paginator
|
||||
{
|
||||
if ($this->records) {
|
||||
return $this->records;
|
||||
@@ -37,14 +38,21 @@ trait HasRecords
|
||||
$this->applySortingToTableQuery($query);
|
||||
|
||||
if ($this->isTablePaginationEnabled()) {
|
||||
return $this->records = $query->paginate(
|
||||
/** @var LengthAwarePaginator $records */
|
||||
$records = $query->paginate(
|
||||
$this->getTableRecordsPerPage(),
|
||||
['*'],
|
||||
$this->getTablePaginationPageName(),
|
||||
)->onEachSide(1);
|
||||
);
|
||||
|
||||
$records->onEachSide(1);
|
||||
|
||||
$this->records = $records;
|
||||
} else {
|
||||
return $this->records = $query->get();
|
||||
$this->records = $query->get();
|
||||
}
|
||||
|
||||
return $this->records;
|
||||
}
|
||||
|
||||
protected function resolveTableRecord(?string $key): ?Model
|
||||
|
||||
@@ -4,7 +4,11 @@ namespace Filament\Tables\Concerns;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* @method Builder getTableQuery()
|
||||
*/
|
||||
trait InteractsWithTable
|
||||
{
|
||||
use CanPaginateRecords;
|
||||
|
||||
@@ -7,6 +7,8 @@ use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Actions\BulkAction;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
interface HasTable extends HasForms
|
||||
@@ -43,7 +45,7 @@ interface HasTable extends HasForms
|
||||
|
||||
public function getTableFiltersForm(): ComponentContainer;
|
||||
|
||||
public function getTableRecords(): Collection | LengthAwarePaginator;
|
||||
public function getTableRecords(): Collection | Paginator;
|
||||
|
||||
public function getTableSortColumn(): ?string;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Filament\Tables\Filters;
|
||||
use Filament\Forms\Components\MultiSelect;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -31,16 +32,22 @@ class MultiSelectFilter extends Filter
|
||||
}
|
||||
|
||||
if ($this->queriesRelationships()) {
|
||||
/** @var BelongsTo $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
return $query->whereHas(
|
||||
$this->getRelationshipName(),
|
||||
fn (Builder $query) => $query->whereIn(
|
||||
$this->getRelationship()->getOwnerKeyName(),
|
||||
$relationship->getOwnerKeyName(),
|
||||
$data['values'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return $query->whereIn($this->getColumn(), $data['values']);
|
||||
/** @var Builder $query */
|
||||
$query = $query->whereIn($this->getColumn(), $data['values']);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function column(string $name): static
|
||||
@@ -93,10 +100,12 @@ class MultiSelectFilter extends Filter
|
||||
|
||||
protected function getRelationshipOptions(): array
|
||||
{
|
||||
/** @var BelongsTo $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$displayColumnName = $this->getRelationshipDisplayColumnName();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($displayColumnName);
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($displayColumnName);
|
||||
|
||||
return $relationshipQuery
|
||||
->pluck($displayColumnName, $relationship->getOwnerKeyName())
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Filament\Tables\Filters;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -31,9 +32,12 @@ class SelectFilter extends Filter
|
||||
}
|
||||
|
||||
if ($this->queriesRelationships()) {
|
||||
/** @var BelongsTo $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
return $query->whereRelation(
|
||||
$this->getRelationshipName(),
|
||||
$this->getRelationship()->getOwnerKeyName(),
|
||||
$relationship->getOwnerKeyName(),
|
||||
$data['value'],
|
||||
);
|
||||
}
|
||||
@@ -91,10 +95,12 @@ class SelectFilter extends Filter
|
||||
|
||||
protected function getRelationshipOptions(): array
|
||||
{
|
||||
/** @var BelongsTo $relationship */
|
||||
$relationship = $this->getRelationship();
|
||||
|
||||
$displayColumnName = $this->getRelationshipDisplayColumnName();
|
||||
|
||||
$relationshipQuery = $relationship->getRelated()->orderBy($displayColumnName);
|
||||
$relationshipQuery = $relationship->getRelated()->query()->orderBy($displayColumnName);
|
||||
|
||||
return $relationshipQuery
|
||||
->pluck($displayColumnName, $relationship->getOwnerKeyName())
|
||||
|
||||
+1
-1
@@ -7,4 +7,4 @@ parameters:
|
||||
- packages/spatie-laravel-tags-plugin/src
|
||||
- packages/spatie-laravel-translatable-plugin/src
|
||||
- packages/tables/src
|
||||
level: 0
|
||||
level: 5
|
||||
|
||||
Reference in New Issue
Block a user