Skip to content

Commit

Permalink
Merge pull request #10257 from filamentphp/fix/translatable-file-upload
Browse files Browse the repository at this point in the history
fix: Translatable losing contents of file upload
  • Loading branch information
danharrin authored Dec 12, 2023
2 parents 84fee5f + f7391c5 commit e93e8bc
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 258 deletions.
45 changes: 8 additions & 37 deletions packages/panels/src/Resources/Pages/CreateRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,6 @@ protected function authorizeAccess(): void
}

protected function fillForm(): void
{
/** @internal Read the DocBlock above the following method. */
$this->fillFormWithDefaultsAndCallHooks();
}

/**
* @internal Never override or call this method. If you completely override `fillForm()`, copy the contents of this method into your override.
*/
protected function fillFormWithDefaultsAndCallHooks(): void
{
$this->callHook('beforeFill');

Expand All @@ -91,40 +82,20 @@ public function create(bool $another = false): void

$data = $this->mutateFormDataBeforeCreate($data);

/** @internal Read the DocBlock above the following method. */
$this->createRecordAndCallHooks($data);
} catch (Halt $exception) {
return;
}

/** @internal Read the DocBlock above the following method. */
$this->sendCreatedNotificationAndRedirect(shouldCreateAnotherInsteadOfRedirecting: $another);
}

/**
* @internal Never override or call this method. If you completely override `create()`, copy the contents of this method into your override.
*
* @param array<string, mixed> $data
*/
protected function createRecordAndCallHooks(array $data): void
{
$this->callHook('beforeCreate');
$this->callHook('beforeCreate');

$this->record = $this->handleRecordCreation($data);
$this->record = $this->handleRecordCreation($data);

$this->form->model($this->getRecord())->saveRelationships();
$this->form->model($this->getRecord())->saveRelationships();

$this->callHook('afterCreate');
}
$this->callHook('afterCreate');
} catch (Halt $exception) {
return;
}

/**
* @internal Never override or call this method. If you completely override `create()`, copy the contents of this method into your override.
*/
protected function sendCreatedNotificationAndRedirect(bool $shouldCreateAnotherInsteadOfRedirecting = true): void
{
$this->getCreatedNotification()?->send();

if ($shouldCreateAnotherInsteadOfRedirecting) {
if ($another) {
// Ensure that the form record is anonymized so that relationships aren't loaded.
$this->form->model($this->getRecord()::class);
$this->record = null;
Expand Down
38 changes: 10 additions & 28 deletions packages/panels/src/Resources/Pages/EditRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,41 +128,23 @@ public function save(bool $shouldRedirect = true): void
$this->authorizeAccess();

try {
/** @internal Read the DocBlock above the following method. */
$this->validateFormAndUpdateRecordAndCallHooks();
} catch (Halt $exception) {
return;
}
$this->callHook('beforeValidate');

/** @internal Read the DocBlock above the following method. */
$this->sendSavedNotificationAndRedirect(shouldRedirect: $shouldRedirect);
}

/**
* @internal Never override or call this method. If you completely override `save()`, copy the contents of this method into your override.
*/
protected function validateFormAndUpdateRecordAndCallHooks(): void
{
$this->callHook('beforeValidate');
$data = $this->form->getState();

$data = $this->form->getState();
$this->callHook('afterValidate');

$this->callHook('afterValidate');
$data = $this->mutateFormDataBeforeSave($data);

$data = $this->mutateFormDataBeforeSave($data);
$this->callHook('beforeSave');

$this->callHook('beforeSave');
$this->handleRecordUpdate($this->getRecord(), $data);

$this->handleRecordUpdate($this->getRecord(), $data);

$this->callHook('afterSave');
}
$this->callHook('afterSave');
} catch (Halt $exception) {
return;
}

/**
* @internal Never override or call this method. If you completely override `save()`, copy the contents of this method into your override.
*/
protected function sendSavedNotificationAndRedirect(bool $shouldRedirect = true): void
{
$this->getSavedNotification()?->send();

if ($shouldRedirect && ($redirectUrl = $this->getRedirectUrl())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,39 @@

namespace Filament\Resources\Pages\Concerns;

use Livewire\Attributes\Locked;

trait HasTranslatableFormWithExistingRecordData
{
#[Locked]
public $otherLocaleData = [];

protected function fillForm(): void
{
$originalActiveLocale = $this->activeLocale;
$this->activeLocale = $this->getDefaultTranslatableLocale();

$record = $this->getRecord();
$translatableAttributes = static::getResource()::getTranslatableAttributes();

foreach ($this->getTranslatableLocales() as $locale) {
$this->setActiveLocale($locale);
$translatedData = [];

$data = $this->getRecord()->attributesToArray();
foreach ($translatableAttributes as $attribute) {
$translatedData[$attribute] = $record->getTranslation($attribute, $locale);
}

if ($locale !== $this->activeLocale) {
$this->otherLocaleData[$locale] = $this->mutateFormDataBeforeFill($translatedData);

foreach (static::getResource()::getTranslatableAttributes() as $attribute) {
$data[$attribute] = $this->getRecord()->getTranslation($attribute, $this->activeLocale);
continue;
}

/** @internal Read the DocBlock above the following method. */
$this->fillFormWithDataAndCallHooks($data);
$this->fillFormWithDataAndCallHooks([
...$record->attributesToArray(),
...$translatedData,
]);
}

$this->setActiveLocale($originalActiveLocale);
}

protected function setActiveLocale(?string $locale = null): void
{
$this->activeLocale = filled($locale) ? $locale : $this->getDefaultTranslatableLocale();
$this->cacheForm('form', $this->getForms()['form']);
}

protected function getDefaultTranslatableLocale(): string
Expand All @@ -45,9 +52,4 @@ protected function getDefaultTranslatableLocale(): string

return array_intersect($availableLocales, $resourceLocales)[0] ?? $defaultLocale;
}

public function getFormStatePath(): ?string
{
return "data.{$this->activeLocale}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,172 +2,91 @@

namespace Filament\Resources\Pages\CreateRecord\Concerns;

use Filament\Facades\Filament;
use Filament\Resources\Concerns\HasActiveLocaleSwitcher;
use Filament\Support\Exceptions\Halt;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Locked;

trait Translatable
{
use HasActiveLocaleSwitcher;

protected ?string $oldActiveLocale = null;

protected function fillForm(): void
{
foreach ($this->getTranslatableLocales() as $locale) {
$this->setActiveLocale($locale);

/** @internal Read the DocBlock above the following method. */
$this->fillFormWithDefaultsAndCallHooks();
}
#[Locked]
public $otherLocaleData = [];

$this->setActiveLocale();
public function mountTranslatable(): void
{
$this->activeLocale = static::getResource()::getDefaultTranslatableLocale();
}

public function getTranslatableLocales(): array
{
return static::getResource()::getTranslatableLocales();
}

protected function setActiveLocale(?string $locale = null): void
{
$this->activeLocale = filled($locale) ? $locale : static::getResource()::getDefaultTranslatableLocale();
$this->cacheForm('form', $this->getForms()['form']);
}

public function create(bool $another = false): void
protected function handleRecordCreation(array $data): Model
{
$this->authorizeAccess();

try {
$this->callHook('beforeValidate');

$data = [
$this->activeLocale => $this->form->getState(),
];

$this->callHook('afterValidate');

$originalActiveLocale = $this->activeLocale;

$translatableAttributes = app(static::getModel())->getTranslatableAttributes();

$nonTranslatableData = Arr::except(
$this->data[$originalActiveLocale] ?? [],
$translatableAttributes,
);

foreach ($this->getTranslatableLocales() as $locale) {
if ($locale === $originalActiveLocale) {
continue;
}

try {
$this->setActiveLocale($locale);

$this->data[$locale] = array_merge(
$this->data[$locale] ?? [],
$nonTranslatableData,
);

$this->callHook('beforeValidate');

$data[$locale] = $this->form->getState();

$this->callHook('afterValidate');
} catch (ValidationException $exception) {
// If the validation fails for a non-active locale,
// we'll just ignore it and continue, since it's
// likely that the user hasn't filled out the
// required fields for that locale.
}
}

$this->setActiveLocale($originalActiveLocale);
$record = app(static::getModel());

foreach ($data as $locale => $localeData) {
if ($locale !== $this->activeLocale) {
$localeData = Arr::only(
$localeData,
$translatableAttributes,
);
}
$translatableAttributes = static::getResource()::getTranslatableAttributes();

$data[$locale] = $this->mutateFormDataBeforeCreate($localeData);
}
$record->fill(Arr::except($data, $translatableAttributes));

/** @internal Read the DocBlock above the following method. */
$this->createRecordAndCallHooks($data);
} catch (Halt $exception) {
return;
foreach (Arr::only($data, $translatableAttributes) as $key => $value) {
$record->setTranslation($key, $this->activeLocale, $value);
}

/** @internal Read the DocBlock above the following method. */
$this->sendCreatedNotificationAndRedirect(shouldCreateAnotherInsteadOfRedirecting: $another);
}
$originalData = $this->data;

protected function handleRecordCreation(array $data): Model
{
$record = app(static::getModel());

$translatableAttributes = $record->getTranslatableAttributes();
foreach ($this->otherLocaleData as $locale => $localeData) {
$this->data = [
...$this->data,
...$localeData,
];

foreach ($data as $locale => $localeData) {
if ($locale === $this->activeLocale) {
$record->fill(Arr::except($localeData, $translatableAttributes));
try {
$this->form->validate();
} catch (ValidationException $exception) {
continue;
}

$localeData = $this->mutateFormDataBeforeCreate($localeData);

foreach (Arr::only($localeData, $translatableAttributes) as $key => $value) {
$record->setTranslation($key, $locale, $value);
}
}

if (
static::getResource()::isScopedToTenant() &&
($tenant = Filament::getTenant())
) {
return $this->associateRecordWithTenant($record, $tenant);
}
$this->data = $originalData;

$record->save();

return $record;
}

public function getFormStatePath(): ?string
{
return "data.{$this->activeLocale}";
}

public function updatingActiveLocale(): void
{
$this->oldActiveLocale = $this->activeLocale;
}

public function updatedActiveLocale(string $newActiveLocale): void
{
$this->setActiveLocale($newActiveLocale);

if (blank($this->oldActiveLocale)) {
return;
}

$translatableAttributes = app(static::getModel())->getTranslatableAttributes();
$translatableAttributes = static::getResource()::getTranslatableAttributes();

$this->otherLocaleData[$this->oldActiveLocale] = Arr::only($this->data, $translatableAttributes);

$this->data[$newActiveLocale] = array_merge(
$this->data[$newActiveLocale] ?? [],
Arr::except(
$this->data[$this->oldActiveLocale] ?? [],
$translatableAttributes,
),
);
$this->data = [
...Arr::except($this->data, $translatableAttributes),
...$this->otherLocaleData[$this->activeLocale] ?? [],
];

$this->data[$this->oldActiveLocale] = Arr::only(
$this->data[$this->oldActiveLocale] ?? [],
$translatableAttributes,
);
unset($this->otherLocaleData[$this->activeLocale]);
}
}
Loading

0 comments on commit e93e8bc

Please sign in to comment.