b2in/packages/flux-cms/core/database/seeders-reference/CmsContentSeeder.php
2026-04-10 17:18:17 +02:00

186 lines
4.9 KiB
PHP

<?php
namespace Database\Seeders;
use FluxCms\Core\Models\CmsContent;
use Illuminate\Database\Seeder;
use Illuminate\Support\Arr;
class CmsContentSeeder extends Seeder
{
/**
* Files to skip entirely (handled by dedicated seeders or irrelevant).
*
* @var array<string>
*/
protected array $skipFiles = [
'faqs',
'sections',
'search_index',
'validation',
'auth',
'passwords',
'pagination',
];
/**
* Keys to skip within specific groups (handled by dedicated models).
*
* @var array<string, array<string>>
*/
protected array $skipKeys = [
'components' => ['news_band', 'industries_band'],
];
public function run(): void
{
$locales = ['de', 'en'];
$deFiles = glob(lang_path('de/*.php'));
if (! $deFiles) {
return;
}
foreach ($deFiles as $filePath) {
$group = pathinfo($filePath, PATHINFO_FILENAME);
if (in_array($group, $this->skipFiles)) {
continue;
}
$translations = [];
foreach ($locales as $locale) {
$localePath = lang_path("{$locale}/{$group}.php");
if (file_exists($localePath)) {
$translations[$locale] = require $localePath;
}
}
if (empty($translations)) {
continue;
}
$deData = $translations['de'] ?? [];
$enData = $translations['en'] ?? [];
$flatDe = $this->flatten($deData);
$flatEn = $this->flatten($enData);
$allKeys = array_keys($flatDe);
foreach (array_keys($flatEn) as $enKey) {
if (! in_array($enKey, $allKeys)) {
$allKeys[] = $enKey;
}
}
$order = 0;
foreach ($allKeys as $key) {
if ($this->shouldSkipKey($group, $key)) {
continue;
}
$deValue = $flatDe[$key] ?? null;
$enValue = $flatEn[$key] ?? null;
$type = $this->detectType($deValue ?? $enValue);
$translatedValue = [];
if ($deValue !== null) {
$translatedValue['de'] = is_string($deValue) ? $this->cleanHtml($deValue) : $deValue;
}
if ($enValue !== null) {
$translatedValue['en'] = is_string($enValue) ? $this->cleanHtml($enValue) : $enValue;
}
CmsContent::updateOrCreate(
['group' => $group, 'key' => $key],
[
'type' => $type,
'value' => $translatedValue,
'order' => $order++,
]
);
}
}
}
/**
* Flatten a nested array into dot-notation keys.
* Arrays of objects (indexed arrays) are stored as JSON type.
*
* @return array<string, mixed>
*/
protected function flatten(array $array, string $prefix = ''): array
{
$result = [];
foreach ($array as $key => $value) {
$fullKey = $prefix ? "{$prefix}.{$key}" : (string) $key;
if (is_array($value) && ! Arr::isAssoc($value)) {
$result[$fullKey] = $value;
} elseif (is_array($value)) {
$result = array_merge($result, $this->flatten($value, $fullKey));
} else {
$result[$fullKey] = $value;
}
}
return $result;
}
protected function detectType(mixed $value): string
{
if (is_array($value)) {
return 'json';
}
if (! is_string($value)) {
return 'text';
}
if (preg_match('/<[a-z][\s\S]*>/i', $value)) {
return 'html';
}
if (preg_match('/\.(jpg|jpeg|png|gif|webp|svg)(\?.*)?$/i', $value)) {
return 'image';
}
if (preg_match('/\.(pdf|doc|docx)$/i', $value)) {
return 'link';
}
return 'text';
}
/**
* Clean HTML by converting font-weight spans to <strong> tags.
* Preserves text-gradient-premium spans and email-protection spans.
*/
protected function cleanHtml(string $value): string
{
$value = preg_replace(
'/<span\s+class="[^"]*(?:font-semibold|font-bold)[^"]*">(\s*)(.*?)<\/span>/si',
'$1<strong>$2</strong>',
$value
);
return $value;
}
protected function shouldSkipKey(string $group, string $key): bool
{
if (! isset($this->skipKeys[$group])) {
return false;
}
foreach ($this->skipKeys[$group] as $skipPrefix) {
if ($key === $skipPrefix || str_starts_with($key, "{$skipPrefix}.")) {
return true;
}
}
return false;
}
}