| null */ public ?array $data = []; public ?string $previousUrl = null; public static function getNavigationIcon(): string | Htmlable | null { return static::$navigationIcon ?? FilamentIcon::resolve('panels::resources.pages.edit-record.navigation-item') ?? 'heroicon-o-pencil-square'; } public function getBreadcrumb(): string { return static::$breadcrumb ?? __('filament-panels::resources/pages/edit-record.breadcrumb'); } public function getContentTabLabel(): ?string { return __('filament-panels::resources/pages/edit-record.content.tab.label'); } public function mount(int | string $record): void { $this->record = $this->resolveRecord($record); $this->authorizeAccess(); $this->fillForm(); $this->previousUrl = url()->previous(); } protected function authorizeAccess(): void { abort_unless(static::getResource()::canEdit($this->getRecord()), 403); } public function hydrate(): void { $this->authorizeAccess(); } protected function fillForm(): void { /** @internal Read the DocBlock above the following method. */ $this->fillFormWithDataAndCallHooks($this->getRecord()); } /** * @internal Never override or call this method. If you completely override `fillForm()`, copy the contents of this method into your override. * * @param array $extraData */ protected function fillFormWithDataAndCallHooks(Model $record, array $extraData = []): void { $this->callHook('beforeFill'); $data = $this->mutateFormDataBeforeFill([ ...$record->attributesToArray(), ...$extraData, ]); $this->form->fill($data); $this->callHook('afterFill'); } /** * @param array $attributes */ public function refreshFormData(array $attributes): void { $data = [ ...$this->data, ...Arr::only($this->getRecord()->attributesToArray(), $attributes), ]; $this->form->fill($data); } /** * @param array $data * @return array */ protected function mutateFormDataBeforeFill(array $data): array { return $data; } public function save(bool $shouldRedirect = true, bool $shouldSendSavedNotification = true): void { $this->authorizeAccess(); try { $this->beginDatabaseTransaction(); $this->callHook('beforeValidate'); $data = $this->form->getState(afterValidate: function () { $this->callHook('afterValidate'); $this->callHook('beforeSave'); }); $data = $this->mutateFormDataBeforeSave($data); $this->handleRecordUpdate($this->getRecord(), $data); $this->callHook('afterSave'); } catch (Halt $exception) { $exception->shouldRollbackDatabaseTransaction() ? $this->rollBackDatabaseTransaction() : $this->commitDatabaseTransaction(); return; } catch (Throwable $exception) { $this->rollBackDatabaseTransaction(); throw $exception; } $this->commitDatabaseTransaction(); $this->rememberData(); if ($shouldSendSavedNotification) { $this->getSavedNotification()?->send(); } if ($shouldRedirect && ($redirectUrl = $this->getRedirectUrl())) { $this->redirect($redirectUrl, navigate: FilamentView::hasSpaMode($redirectUrl)); } } public function saveFormComponentOnly(Component $component): void { $this->authorizeAccess(); try { $this->beginDatabaseTransaction(); $this->callHook('beforeValidate'); $data = ComponentContainer::make($component->getLivewire()) ->schema([$component]) ->model($component->getRecord()) ->statePath($this->getFormStatePath()) ->getState(); $this->callHook('afterValidate'); $data = $this->mutateFormDataBeforeSave($data); $this->callHook('beforeSave'); $this->handleRecordUpdate($this->getRecord(), $data); $this->callHook('afterSave'); } catch (Halt $exception) { $exception->shouldRollbackDatabaseTransaction() ? $this->rollBackDatabaseTransaction() : $this->commitDatabaseTransaction(); return; } catch (Throwable $exception) { $this->rollBackDatabaseTransaction(); throw $exception; } $this->commitDatabaseTransaction(); $this->rememberData(); } protected function getSavedNotification(): ?Notification { $title = $this->getSavedNotificationTitle(); if (blank($title)) { return null; } return Notification::make() ->success() ->title($title); } protected function getSavedNotificationTitle(): ?string { return $this->getSavedNotificationMessage() ?? __('filament-panels::resources/pages/edit-record.notifications.saved.title'); } /** * @deprecated Use `getSavedNotificationTitle()` instead. */ protected function getSavedNotificationMessage(): ?string { return null; } /** * @param array $data */ protected function handleRecordUpdate(Model $record, array $data): Model { $record->update($data); return $record; } /** * @param array $data * @return array */ protected function mutateFormDataBeforeSave(array $data): array { return $data; } protected function configureAction(Action $action): void { $this->configureActionRecord($action); match (true) { $action instanceof DeleteAction => $this->configureDeleteAction($action), $action instanceof ForceDeleteAction => $this->configureForceDeleteAction($action), $action instanceof ReplicateAction => $this->configureReplicateAction($action), $action instanceof RestoreAction => $this->configureRestoreAction($action), $action instanceof ViewAction => $this->configureViewAction($action), default => null, }; } protected function configureViewAction(ViewAction $action): void { $resource = static::getResource(); $action ->authorize($resource::canView($this->getRecord())) ->infolist(fn (Infolist $infolist): Infolist => static::getResource()::infolist($infolist->columns(2))) ->form(fn (Form $form): Form => static::getResource()::form($form)); if ($resource::hasPage('view')) { $action->url(fn (): string => static::getResource()::getUrl('view', ['record' => $this->getRecord()])); } } protected function configureForceDeleteAction(ForceDeleteAction $action): void { $resource = static::getResource(); $action ->authorize($resource::canForceDelete($this->getRecord())) ->successRedirectUrl($resource::getUrl('index')); } protected function configureReplicateAction(ReplicateAction $action): void { $action ->authorize(static::getResource()::canReplicate($this->getRecord())); } protected function configureRestoreAction(RestoreAction $action): void { $action ->authorize(static::getResource()::canRestore($this->getRecord())); } protected function configureDeleteAction(DeleteAction $action): void { $resource = static::getResource(); $action ->authorize($resource::canDelete($this->getRecord())) ->successRedirectUrl($resource::getUrl('index')); } public function getTitle(): string | Htmlable { if (filled(static::$title)) { return static::$title; } return __('filament-panels::resources/pages/edit-record.title', [ 'label' => $this->getRecordTitle(), ]); } /** * @return array */ protected function getFormActions(): array { return [ $this->getSaveFormAction(), $this->getCancelFormAction(), ]; } protected function getSaveFormAction(): Action { return Action::make('save') ->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label')) ->submit('save') ->keyBindings(['mod+s']); } protected function getSubmitFormAction(): Action { return $this->getSaveFormAction(); } protected function getCancelFormAction(): Action { return Action::make('cancel') ->label(__('filament-panels::resources/pages/edit-record.form.actions.cancel.label')) ->alpineClickHandler('document.referrer ? window.history.back() : (window.location.href = ' . Js::from($this->previousUrl ?? static::getResource()::getUrl()) . ')') ->color('gray'); } public function form(Form $form): Form { return $form; } /** * @return array */ protected function getForms(): array { return [ 'form' => $this->form(static::getResource()::form( $this->makeForm() ->operation('edit') ->model($this->getRecord()) ->statePath($this->getFormStatePath()) ->columns($this->hasInlineLabels() ? 1 : 2) ->inlineLabel($this->hasInlineLabels()), )), ]; } public function getFormStatePath(): ?string { return 'data'; } protected function getRedirectUrl(): ?string { return null; } public static function shouldRegisterNavigation(array $parameters = []): bool { return parent::shouldRegisterNavigation($parameters) && static::getResource()::canEdit($parameters['record']); } }