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.

This commit is contained in:
Ümit Tunç
2025-12-27 09:26:47 +03:00
parent 67187e646c
commit 70cba75441
6 changed files with 211 additions and 129 deletions
+67 -23
View File
@@ -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];
@@ -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
@@ -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')
+134 -1
View File
@@ -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
*/
+7 -2
View File
@@ -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
+2 -2
View File
@@ -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',