mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
Opt in to database transactions
This commit is contained in:
@@ -47,8 +47,6 @@ class CreateAction extends Action
|
||||
|
||||
$this->record(null);
|
||||
|
||||
$this->databaseTransaction();
|
||||
|
||||
$this->action(function (array $arguments, Form $form): void {
|
||||
$model = $this->getModel();
|
||||
|
||||
|
||||
@@ -47,8 +47,6 @@ class EditAction extends Action
|
||||
return $data;
|
||||
});
|
||||
|
||||
$this->databaseTransaction();
|
||||
|
||||
$this->action(function (): void {
|
||||
$this->process(function (array $data, HasActions $livewire, Model $record) {
|
||||
if ($translatableContentDriver = $livewire->makeFilamentTranslatableContentDriver()) {
|
||||
|
||||
@@ -164,6 +164,65 @@ public function panel(Panel $panel): Panel
|
||||
|
||||
> Please note: this feature is not compatible with [SPA mode](#spa-mode).
|
||||
|
||||
## Enabling database transactions
|
||||
|
||||
By default, Filament does not wrap operations in database transactions, and allows the user to enable this themselves when they have tested to ensure that their operations are safe to be wrapped in a transaction. However, you can enable database transactions at once for all operations by using the `databaseTransactions()` method:
|
||||
|
||||
```php
|
||||
use Filament\Panel;
|
||||
|
||||
public function panel(Panel $panel): Panel
|
||||
{
|
||||
return $panel
|
||||
// ...
|
||||
->databaseTransactions();
|
||||
}
|
||||
```
|
||||
|
||||
For any actions you do not want to be wrapped in a transaction, you can use the `databaseTransactions(false)` method:
|
||||
|
||||
```php
|
||||
CreateAction::make()
|
||||
->databaseTransactions(false)
|
||||
```
|
||||
|
||||
And for any pages like [Create resource](resources/creating-records) and [Edit resource](resources/editing-records), you can define the `$hasDatabaseTransactions` property to `false` on the page class:
|
||||
|
||||
```php
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreatePost extends CreateRecord
|
||||
{
|
||||
protected ?bool $hasDatabaseTransactions = false;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Opting in to database transactions for specific actions and pages
|
||||
|
||||
Instead of enabling database transactions everywhere and opting out of them for specific actions and pages, you can opt in to database transactions for specific actions and pages.
|
||||
|
||||
For actions, you can use the `databaseTransactions()` method:
|
||||
|
||||
```php
|
||||
CreateAction::make()
|
||||
->databaseTransactions()
|
||||
```
|
||||
|
||||
For pages like [Create resource](resources/creating-records) and [Edit resource](resources/editing-records), you can define the `$hasDatabaseTransactions` property to `true` on the page class:
|
||||
|
||||
```php
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreatePost extends CreateRecord
|
||||
{
|
||||
protected ?bool $hasDatabaseTransactions = true;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Registering assets for a panel
|
||||
|
||||
You can register [assets](../support/assets) that will only be loaded on pages within a specific panel, and not in the rest of the app. To do that, pass an array of assets to the `assets()` method:
|
||||
|
||||
@@ -21,7 +21,6 @@ use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
@@ -34,6 +33,7 @@ use function Filament\Support\is_app_url;
|
||||
*/
|
||||
class EditProfile extends Page
|
||||
{
|
||||
use Concerns\CanUseDatabaseTransactions;
|
||||
use Concerns\InteractsWithFormActions;
|
||||
|
||||
/**
|
||||
@@ -145,7 +145,7 @@ class EditProfile extends Page
|
||||
public function save(): void
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
@@ -161,15 +161,15 @@ class EditProfile extends Page
|
||||
|
||||
$this->callHook('afterSave');
|
||||
|
||||
DB::commit();
|
||||
$this->commitDatabaseTransaction();
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
DB::rollBack() :
|
||||
DB::commit();
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (Throwable $exception) {
|
||||
DB::rollBack();
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use Filament\Forms\Form;
|
||||
use Filament\Http\Responses\Auth\Contracts\RegistrationResponse;
|
||||
use Filament\Notifications\Auth\VerifyEmail;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Concerns\CanUseDatabaseTransactions;
|
||||
use Filament\Pages\Concerns\InteractsWithFormActions;
|
||||
use Filament\Pages\SimplePage;
|
||||
use Illuminate\Auth\EloquentUserProvider;
|
||||
@@ -22,7 +23,6 @@ use Illuminate\Auth\SessionGuard;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
@@ -31,6 +31,7 @@ use Illuminate\Validation\Rules\Password;
|
||||
*/
|
||||
class Register extends SimplePage
|
||||
{
|
||||
use CanUseDatabaseTransactions;
|
||||
use InteractsWithFormActions;
|
||||
use WithRateLimiting;
|
||||
|
||||
@@ -75,7 +76,7 @@ class Register extends SimplePage
|
||||
return null;
|
||||
}
|
||||
|
||||
$user = DB::transaction(function () {
|
||||
$user = $this->wrapInDatabaseTransaction(function () {
|
||||
$data = $this->form->getState();
|
||||
|
||||
return $this->getUserModel()::create($data);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Pages\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Filament\Facades\Filament;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
trait CanUseDatabaseTransactions
|
||||
{
|
||||
protected ?bool $hasDatabaseTransactions = null;
|
||||
|
||||
public function hasDatabaseTransactions(): bool
|
||||
{
|
||||
return $this->hasDatabaseTransactions ?? Filament::getCurrentPanel()->hasDatabaseTransactions();
|
||||
}
|
||||
|
||||
protected function beginDatabaseTransaction(): void
|
||||
{
|
||||
if (! $this->hasDatabaseTransactions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
}
|
||||
|
||||
protected function commitDatabaseTransaction(): void
|
||||
{
|
||||
if (! $this->hasDatabaseTransactions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
}
|
||||
|
||||
protected function rollBackDatabaseTransaction(): void
|
||||
{
|
||||
if (! $this->hasDatabaseTransactions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::rollBack();
|
||||
}
|
||||
|
||||
protected function wrapInDatabaseTransaction(Closure $callback): mixed
|
||||
{
|
||||
if (! $this->hasDatabaseTransactions()) {
|
||||
return $callback();
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
return DB::transaction($callback);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Throwable;
|
||||
|
||||
@@ -27,6 +26,7 @@ use function Filament\Support\is_app_url;
|
||||
*/
|
||||
abstract class EditTenantProfile extends Page
|
||||
{
|
||||
use Concerns\CanUseDatabaseTransactions;
|
||||
use Concerns\HasRoutes;
|
||||
use Concerns\InteractsWithFormActions;
|
||||
|
||||
@@ -110,7 +110,7 @@ abstract class EditTenantProfile extends Page
|
||||
public function save(): void
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
@@ -126,15 +126,15 @@ abstract class EditTenantProfile extends Page
|
||||
|
||||
$this->callHook('afterSave');
|
||||
|
||||
DB::commit();
|
||||
$this->commitDatabaseTransaction();
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
DB::rollBack() :
|
||||
DB::commit();
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (Throwable $exception) {
|
||||
DB::rollBack();
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
use function Filament\authorize;
|
||||
@@ -26,6 +25,7 @@ use function Filament\Support\is_app_url;
|
||||
*/
|
||||
abstract class RegisterTenant extends SimplePage
|
||||
{
|
||||
use Concerns\CanUseDatabaseTransactions;
|
||||
use Concerns\HasRoutes;
|
||||
use InteractsWithFormActions;
|
||||
|
||||
@@ -72,7 +72,7 @@ abstract class RegisterTenant extends SimplePage
|
||||
public function register(): void
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
@@ -90,15 +90,15 @@ abstract class RegisterTenant extends SimplePage
|
||||
|
||||
$this->callHook('afterRegister');
|
||||
|
||||
DB::commit();
|
||||
$this->commitDatabaseTransaction();
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
DB::rollBack() :
|
||||
DB::commit();
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (Throwable $exception) {
|
||||
DB::rollBack();
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Filament;
|
||||
|
||||
use Closure;
|
||||
use Filament\Actions\MountableAction;
|
||||
use Filament\Support\Components\Component;
|
||||
use Filament\Support\Facades\FilamentColor;
|
||||
use Filament\Support\Facades\FilamentIcon;
|
||||
@@ -19,6 +20,7 @@ class Panel extends Component
|
||||
use Panel\Concerns\HasColors;
|
||||
use Panel\Concerns\HasComponents;
|
||||
use Panel\Concerns\HasDarkMode;
|
||||
use Panel\Concerns\HasDatabaseTransactions;
|
||||
use Panel\Concerns\HasFavicon;
|
||||
use Panel\Concerns\HasFont;
|
||||
use Panel\Concerns\HasGlobalSearch;
|
||||
@@ -78,6 +80,12 @@ class Panel extends Component
|
||||
|
||||
$this->registerRenderHooks();
|
||||
|
||||
if ($this->hasDatabaseTransactions()) {
|
||||
MountableAction::configureUsing(
|
||||
fn (MountableAction $action) => $action->databaseTransaction(),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->plugins as $plugin) {
|
||||
$plugin->boot($this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Panel\Concerns;
|
||||
|
||||
use Closure;
|
||||
|
||||
trait HasDatabaseTransactions
|
||||
{
|
||||
protected bool | Closure $hasDatabaseTransactions = false;
|
||||
|
||||
public function databaseTransactions(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->hasDatabaseTransactions = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasDatabaseTransactions(): bool
|
||||
{
|
||||
return (bool) $this->evaluate($this->hasDatabaseTransactions);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use Filament\Actions\ActionGroup;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Concerns\CanUseDatabaseTransactions;
|
||||
use Filament\Pages\Concerns\HasUnsavedDataChangesAlert;
|
||||
use Filament\Pages\Concerns\InteractsWithFormActions;
|
||||
use Filament\Support\Exceptions\Halt;
|
||||
@@ -14,7 +15,6 @@ use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
use function Filament\Support\is_app_url;
|
||||
@@ -24,6 +24,7 @@ use function Filament\Support\is_app_url;
|
||||
*/
|
||||
class CreateRecord extends Page
|
||||
{
|
||||
use CanUseDatabaseTransactions;
|
||||
use HasUnsavedDataChangesAlert;
|
||||
use InteractsWithFormActions;
|
||||
|
||||
@@ -76,7 +77,7 @@ class CreateRecord extends Page
|
||||
$this->authorizeAccess();
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
@@ -94,15 +95,15 @@ class CreateRecord extends Page
|
||||
|
||||
$this->callHook('afterCreate');
|
||||
|
||||
DB::commit();
|
||||
$this->commitDatabaseTransaction();
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
DB::rollBack() :
|
||||
DB::commit();
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (Throwable $exception) {
|
||||
DB::rollBack();
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Filament\Actions\ViewAction;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Concerns\CanUseDatabaseTransactions;
|
||||
use Filament\Pages\Concerns\HasUnsavedDataChangesAlert;
|
||||
use Filament\Pages\Concerns\InteractsWithFormActions;
|
||||
use Filament\Support\Exceptions\Halt;
|
||||
@@ -20,7 +21,6 @@ use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
use function Filament\Support\is_app_url;
|
||||
@@ -30,6 +30,7 @@ use function Filament\Support\is_app_url;
|
||||
*/
|
||||
class EditRecord extends Page
|
||||
{
|
||||
use CanUseDatabaseTransactions;
|
||||
use Concerns\HasRelationManagers;
|
||||
use Concerns\InteractsWithRecord {
|
||||
configureAction as configureActionRecord;
|
||||
@@ -132,7 +133,7 @@ class EditRecord extends Page
|
||||
$this->authorizeAccess();
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
@@ -148,15 +149,15 @@ class EditRecord extends Page
|
||||
|
||||
$this->callHook('afterSave');
|
||||
|
||||
DB::commit();
|
||||
$this->commitDatabaseTransaction();
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
DB::rollBack() :
|
||||
DB::commit();
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (Throwable $exception) {
|
||||
DB::rollBack();
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\ComponentContainer;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Concerns\CanUseDatabaseTransactions;
|
||||
use Filament\Support\Exceptions\Halt;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
use function Filament\Support\is_app_url;
|
||||
@@ -19,6 +19,7 @@ use function Filament\Support\is_app_url;
|
||||
*/
|
||||
class SettingsPage extends Page
|
||||
{
|
||||
use CanUseDatabaseTransactions;
|
||||
use Concerns\InteractsWithFormActions;
|
||||
|
||||
protected static string $settings;
|
||||
@@ -60,7 +61,7 @@ class SettingsPage extends Page
|
||||
public function save(): void
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
@@ -79,15 +80,15 @@ class SettingsPage extends Page
|
||||
|
||||
$this->callHook('afterSave');
|
||||
|
||||
DB::commit();
|
||||
$this->commitDatabaseTransaction();
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
DB::rollBack() :
|
||||
DB::commit();
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (Throwable $exception) {
|
||||
DB::rollBack();
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -42,8 +42,6 @@ class CreateAction extends Action
|
||||
|
||||
$this->successNotificationTitle(__('filament-actions::create.single.notifications.created.title'));
|
||||
|
||||
$this->databaseTransaction();
|
||||
|
||||
$this->action(function (array $arguments, Form $form, HasTable $livewire): void {
|
||||
$model = $this->getModel();
|
||||
|
||||
|
||||
@@ -49,8 +49,6 @@ class EditAction extends Action
|
||||
return $data;
|
||||
});
|
||||
|
||||
$this->databaseTransaction();
|
||||
|
||||
$this->action(function (): void {
|
||||
$this->process(function (array $data, Model $record, Table $table) {
|
||||
$relationship = $table->getRelationship();
|
||||
|
||||
Reference in New Issue
Block a user