Opt in to database transactions

This commit is contained in:
Dan Harrin
2024-03-01 12:16:02 +00:00
parent df0b23af8a
commit 44123d399a
15 changed files with 185 additions and 46 deletions
-2
View File
@@ -47,8 +47,6 @@ class CreateAction extends Action
$this->record(null);
$this->databaseTransaction();
$this->action(function (array $arguments, Form $form): void {
$model = $this->getModel();
-2
View File
@@ -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()) {
+59
View File
@@ -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;
}
+3 -2
View File
@@ -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;
}
+8
View File
@@ -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();