From 70cba754414c0f9cf827be49f1d92435ff6bfcb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Sat, 27 Dec 2025 09:26:47 +0300 Subject: [PATCH] Refactor Template Processing and Update Setting Model: Enhanced the ImportHtmlTemplates command to improve header processing with dynamic logo handling based on navbar type. Updated the Setting model to include new accessors for various field types, ensuring better data retrieval. Adjusted the SettingSeeder to clear logo paths for better management. --- app/Console/Commands/ImportHtmlTemplates.php | 90 +++++++++--- .../Resources/Settings/Pages/EditSetting.php | 2 +- .../Settings/Schemas/SettingForm.php | 100 ------------- app/Models/Setting.php | 135 +++++++++++++++++- app/Services/TemplateService.php | 9 +- database/seeders/SettingSeeder.php | 4 +- 6 files changed, 211 insertions(+), 129 deletions(-) diff --git a/app/Console/Commands/ImportHtmlTemplates.php b/app/Console/Commands/ImportHtmlTemplates.php index 9aeb840..886010c 100644 --- a/app/Console/Commands/ImportHtmlTemplates.php +++ b/app/Console/Commands/ImportHtmlTemplates.php @@ -143,41 +143,82 @@ class ImportHtmlTemplates extends Command // A. Header Özel Alanları if ($templateType === 'header') { + // Navbar sınıfını kontrol et + $navbar = $xpath->query('.//*[contains(@class, "navbar")]', $node)->item(0); + $isNavbarLight = $navbar && str_contains($navbar->getAttribute('class'), 'navbar-light'); + $isNavbarDark = $navbar && str_contains($navbar->getAttribute('class'), 'navbar-dark'); + // Logo $brands = $xpath->query('.//*[contains(@class, "navbar-brand")]//img', $node); foreach ($brands as $img) { $src = $this->fixAssetPath($img->getAttribute('src')); $class = $img->getAttribute('class'); + $settingKey = 'setting.logo'; + if (str_contains($class, 'logo-dark')) { - $data['setting.logo_dark'] = $src; - $img->setAttribute('src', '{setting.logo_dark}'); + $settingKey = 'setting.logo_dark'; } elseif (str_contains($class, 'logo-light')) { - $data['setting.logo_light'] = $src; - $img->setAttribute('src', '{setting.logo_light}'); - } else { - $data['setting.logo'] = $src; - $img->setAttribute('src', '{setting.logo}'); + $settingKey = 'setting.logo_light'; + } elseif ($isNavbarLight) { + // Navbar light ise (açık zemin), koyu logo gerekir + $settingKey = 'setting.logo_dark'; + } elseif ($isNavbarDark) { + // Navbar dark ise (koyu zemin), açık logo gerekir + $settingKey = 'setting.logo_light'; + } + + $data[$settingKey] = $src; + $img->setAttribute('src', '{' . $settingKey . '}'); + } + + // Menu ({custom.menu}) + $navs = $xpath->query('.//*[contains(@class, "navbar-nav")]', $node); + foreach ($navs as $nav) { + // Sadece ana menüyü hedefle (genellikle navbar-collapse içinde olur) + // İçeriği tamamen temizle ve yerine placeholder koy + $parent = $nav->parentNode; + if ($parent) { + // Eğer parent offcanvas-body veya navbar-collapse ise + $textNode = $node->ownerDocument->createTextNode('___CUSTOM_MENU___'); + $parent->replaceChild($textNode, $nav); } } - // Menu - $navs = $xpath->query('.//*[contains(@class, "navbar-nav")]', $node); - foreach ($navs as $nav) { - if (!$nav->parentNode) continue; - // İçini temizle - while ($nav->hasChildNodes()) { - $nav->removeChild($nav->firstChild); + // Navbar Other ({custom.navbar}) + $others = $xpath->query('.//*[contains(@class, "navbar-other")]', $node); + foreach ($others as $other) { + $parent = $other->parentNode; + if ($parent) { + // Navbar other genellikle butonları içerir, bunu custom.navbar ile değiştiriyoruz + // Ancak dil seçici de bunun içinde olabilir. + // Kullanıcı örneğinde {custom.navbar} ve {custom.language-selector} yan yana. + // Biz şimdilik navbar-other'ı komple custom.navbar yapalım. + // Dil seçici için ayrıca bir mantık kuralım. + + // Dil seçiciyi kontrol et, eğer varsa onu çıkarıp yanına ekleyebiliriz ama + // genellikle navbar-other kapsayıcıdır. + + $textNode = $node->ownerDocument->createTextNode('___CUSTOM_NAVBAR___'); + $parent->replaceChild($textNode, $other); + + // Hemen sonrasına language selector ekleyelim (varsayım) + // Veya kullanıcı manuel eklesin. + // Kullanıcının isteği: {custom.navbar} {custom.language-selector} + $langNode = $node->ownerDocument->createTextNode('___CUSTOM_LANGUAGE___'); + $parent->appendChild($langNode); } - // Placeholder metni ekle - Token olarak - $nav->nodeValue = '___SPECIAL_MENU___'; } - // Dil Seçici - $langs = $xpath->query('.//*[contains(@class, "language")]', $node); - foreach ($langs as $lang) { - $lang->nodeValue = '___SETTING_LANGUAGES___'; - } + // Dil Seçici ({custom.language-selector}) - Eğer navbar-other dışında varsa + $langs = $xpath->query('.//*[contains(@class, "language-select") or contains(@class, "dropdown-language")]', $node); + foreach ($langs as $lang) { + $parent = $lang->parentNode; + if ($parent) { + $textNode = $node->ownerDocument->createTextNode('___CUSTOM_LANGUAGE___'); + $parent->replaceChild($textNode, $lang); + } + } } // B. Genel İşlemler @@ -250,8 +291,11 @@ class ImportHtmlTemplates extends Command // SPECIAL TOKENS FIX if ($templateType === 'header') { - $html = str_replace('___SPECIAL_MENU___', '{special.menu}', $html); - $html = str_replace('___SETTING_LANGUAGES___', '{setting.available_languages}', $html); + $html = str_replace('___SPECIAL_MENU___', '{custom.menu}', $html); // Geriye dönük uyumluluk + $html = str_replace('___CUSTOM_MENU___', '{custom.menu}', $html); + $html = str_replace('___CUSTOM_NAVBAR___', '{custom.navbar}', $html); + $html = str_replace('___CUSTOM_LANGUAGE___', '{custom.language-selector}', $html); + $html = str_replace('___SETTING_LANGUAGES___', '{custom.language-selector}', $html); } return ['html' => $html, 'data' => $data]; diff --git a/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php b/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php index 57a47ae..be7cd98 100644 --- a/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php +++ b/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php @@ -51,7 +51,7 @@ class EditSetting extends EditRecord protected function getRedirectUrl(): string { - return $this->getResource()::getUrl('index'); + return $this->getResource()::getUrl('edit', ['record' => $this->getRecord()]); } protected function getSavedNotificationTitle(): ?string diff --git a/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php b/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php index e70976b..a3163be 100644 --- a/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php +++ b/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php @@ -116,12 +116,6 @@ class SettingForm ->required() ->visible(fn (Get $get) => in_array($get('type'), ['string', 'text', 'json'])) ->rows(fn (Get $get) => $get('type') === 'text' ? 5 : 3) - ->formatStateUsing(function ($state, $record) { - if ($record && in_array($record->type, ['string', 'text', 'json'])) { - return $record->value; - } - return $state; - }) ->columnSpanFull(), Toggle::make('value_boolean') @@ -129,12 +123,6 @@ class SettingForm ->helperText(__('settings.value_helper')) ->required() ->visible(fn (Get $get) => $get('type') === 'boolean') - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'boolean') { - return filter_var($record->value, FILTER_VALIDATE_BOOLEAN); - } - return false; - }) ->columnSpanFull(), FileUpload::make('value_file') @@ -146,12 +134,6 @@ class SettingForm ->directory('settings') ->acceptedFileTypes(['image/*', 'application/pdf', 'text/*']) ->maxSize(10240) // 10MB - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'file') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), DatePicker::make('value_date') @@ -161,12 +143,6 @@ class SettingForm ->visible(fn (Get $get) => $get('type') === 'date') ->displayFormat('d/m/Y') ->format('Y-m-d') - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'date') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), DateTimePicker::make('value_datetime') @@ -177,12 +153,6 @@ class SettingForm ->displayFormat('d/m/Y H:i') ->format('Y-m-d H:i:s') ->seconds(false) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'datetime') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), Repeater::make('value_array') @@ -213,13 +183,6 @@ class SettingForm ->reorderable() ->collapsible() ->itemLabel(fn (array $state): ?string => $state['key'] ?? null) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'array') { - $decoded = json_decode($record->value, true); - return is_array($decoded) ? $decoded : []; - } - return is_array($state) ? $state : []; - }) ->columnSpanFull(), ColorPicker::make('value_color_picker') @@ -227,12 +190,6 @@ class SettingForm ->helperText(__('settings.value_helper')) ->required() ->visible(fn (Get $get) => $get('type') === 'color_picker') - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'color_picker') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), CodeEditor::make('value_code_editor') @@ -241,12 +198,6 @@ class SettingForm ->required() ->visible(fn (Get $get) => $get('type') === 'code_editor') ->language(CodeEditorLanguage::Php) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'code_editor') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), RichEditor::make('value_rich_editor') @@ -272,12 +223,6 @@ class SettingForm 'underline', 'undo', ]) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'rich_editor') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), MarkdownEditor::make('value_markdown_editor') @@ -285,12 +230,6 @@ class SettingForm ->helperText(__('settings.value_helper')) ->required() ->visible(fn (Get $get) => $get('type') === 'markdown_editor') - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'markdown_editor') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), TagsInput::make('value_tags_input') @@ -299,13 +238,6 @@ class SettingForm ->required() ->visible(fn (Get $get) => $get('type') === 'tags_input') ->separator(',') - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'tags_input') { - $decoded = json_decode($record->value, true); - return is_array($decoded) ? $decoded : []; - } - return is_array($state) ? $state : []; - }) ->columnSpanFull(), CheckboxList::make('value_checkbox_list') @@ -318,13 +250,6 @@ class SettingForm 'option2' => __('settings.checkbox_option_2'), 'option3' => __('settings.checkbox_option_3'), ]) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'checkbox_list') { - $decoded = json_decode($record->value, true); - return is_array($decoded) ? $decoded : []; - } - return is_array($state) ? $state : []; - }) ->columnSpanFull(), Radio::make('value_radio') @@ -337,12 +262,6 @@ class SettingForm 'option2' => __('settings.radio_option_2'), 'option3' => __('settings.radio_option_3'), ]) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'radio') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), ToggleButtons::make('value_toggle_buttons') @@ -355,12 +274,6 @@ class SettingForm 'option2' => __('settings.toggle_option_2'), 'option3' => __('settings.toggle_option_3'), ]) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'toggle_buttons') { - return $record->value; - } - return $state; - }) ->columnSpanFull(), Slider::make('value_slider') @@ -370,12 +283,6 @@ class SettingForm ->visible(fn (Get $get) => $get('type') === 'slider') ->range(minValue: 0, maxValue: 100) ->step(1) - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'slider') { - return (int) $record->value; - } - return $state ?? 0; - }) ->columnSpanFull(), KeyValue::make('value_key_value') @@ -383,13 +290,6 @@ class SettingForm ->helperText(__('settings.value_helper')) ->required() ->visible(fn (Get $get) => $get('type') === 'key_value') - ->formatStateUsing(function ($state, $record) { - if ($record && $record->type === 'key_value') { - $decoded = json_decode($record->value, true); - return is_array($decoded) ? $decoded : []; - } - return is_array($state) ? $state : []; - }) ->columnSpanFull(), Toggle::make('is_active') diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 4ad7442..1b38656 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -69,7 +69,12 @@ class Setting extends Model */ public function setValueFileAttribute($value) { - $this->attributes['value'] = $value; + // Filament bazen dosya yolunu array olarak döndürebilir, bu durumda ilk elemanı alıyoruz + if (is_array($value)) { + $this->attributes['value'] = array_values($value)[0] ?? null; + } else { + $this->attributes['value'] = $value; + } } /** @@ -176,6 +181,134 @@ class Setting extends Model $this->attributes['value'] = is_array($value) ? json_encode($value) : $value; } + /** + * Get value for text field + */ + public function getValueTextAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for boolean field + */ + public function getValueBooleanAttribute() + { + return isset($this->attributes['value']) ? filter_var($this->attributes['value'], FILTER_VALIDATE_BOOLEAN) : false; + } + + /** + * Get value for file field + */ + public function getValueFileAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for date field + */ + public function getValueDateAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for datetime field + */ + public function getValueDatetimeAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for array field + */ + public function getValueArrayAttribute() + { + return isset($this->attributes['value']) ? json_decode($this->attributes['value'], true) : []; + } + + /** + * Get value for color picker field + */ + public function getValueColorPickerAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for code editor field + */ + public function getValueCodeEditorAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for rich editor field + */ + public function getValueRichEditorAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for markdown editor field + */ + public function getValueMarkdownEditorAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for tags input field + */ + public function getValueTagsInputAttribute() + { + return isset($this->attributes['value']) ? json_decode($this->attributes['value'], true) : []; + } + + /** + * Get value for checkbox list field + */ + public function getValueCheckboxListAttribute() + { + return isset($this->attributes['value']) ? json_decode($this->attributes['value'], true) : []; + } + + /** + * Get value for radio field + */ + public function getValueRadioAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for toggle buttons field + */ + public function getValueToggleButtonsAttribute() + { + return $this->attributes['value'] ?? null; + } + + /** + * Get value for slider field + */ + public function getValueSliderAttribute() + { + return isset($this->attributes['value']) ? (int) $this->attributes['value'] : 0; + } + + /** + * Get value for key value field + */ + public function getValueKeyValueAttribute() + { + return isset($this->attributes['value']) ? json_decode($this->attributes['value'], true) : []; + } + /** * Get setting by key */ diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php index 3701a91..bd5b036 100644 --- a/app/Services/TemplateService.php +++ b/app/Services/TemplateService.php @@ -50,6 +50,11 @@ class TemplateService if (str_starts_with($placeholder, 'special.')) { continue; } + + // Skip setting.* placeholders - they are global settings managed via Settings module + if ($type === 'setting') { + continue; + } // Skip custom.* placeholders - they are blade components, not form fields if ($type === 'custom') { @@ -282,7 +287,7 @@ class TemplateService public static function parsePlaceholders(string $html): array { // Pattern: {type.field} or {special.type.field} or {type.field.field2} etc. - preg_match_all('/\{([a-z]+(?:\.[a-z][a-z_-]*)+)\}/i', $html, $matches); + preg_match_all('/\{([a-z]+(?:\.[a-z][a-z0-9_-]*)+)\}/i', $html, $matches); return array_unique($matches[1] ?? []); } @@ -410,7 +415,7 @@ class TemplateService // Also supports {special.page.title} format (special.* prefix) // Exclude custom.* from this regex to avoid double processing // Pattern: {type.field} or {special.type.field} or {type.field.field2} etc. - preg_match_all('/\{([a-z]+(?:\.[a-z][a-z_-]*)+)\}/i', $html, $matches); + preg_match_all('/\{([a-z]+(?:\.[a-z][a-z0-9_-]*)+)\}/i', $html, $matches); $placeholders = array_unique($matches[1] ?? []); // Filter out custom.* placeholders as they're already handled above diff --git a/database/seeders/SettingSeeder.php b/database/seeders/SettingSeeder.php index 3aa6b93..bbb80cf 100644 --- a/database/seeders/SettingSeeder.php +++ b/database/seeders/SettingSeeder.php @@ -587,7 +587,7 @@ class SettingSeeder extends Seeder ], [ 'key' => 'logo_light', - 'value' => 'assets/img/logo-light.png', + 'value' => '', 'type' => 'file', 'group' => 'theme', 'label' => 'Light Logo', @@ -597,7 +597,7 @@ class SettingSeeder extends Seeder ], [ 'key' => 'logo_dark', - 'value' => 'assets/img/logo-dark.png', + 'value' => '', 'type' => 'file', 'group' => 'theme', 'label' => 'Dark Logo',