diff --git a/'s/home/truncgil/web/citrus.truncgil.com/public_html/database/seeders/DatabaseSeeder.php b/'s/home/truncgil/web/citrus.truncgil.com/public_html/database/seeders/DatabaseSeeder.php deleted file mode 100644 index 15a4df5..0000000 --- a/'s/home/truncgil/web/citrus.truncgil.com/public_html/database/seeders/DatabaseSeeder.php +++ /dev/null @@ -1,29 +0,0 @@ -call([ - SettingSeeder::class, - ]); - - // User::factory(10)->create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); - } -} - diff --git a/app/Http/Controllers/BlogController.php b/app/Http/Controllers/BlogController.php index 381d298..2065556 100644 --- a/app/Http/Controllers/BlogController.php +++ b/app/Http/Controllers/BlogController.php @@ -3,20 +3,57 @@ namespace App\Http\Controllers; use App\Models\Setting; -use App\Models\Post; +use App\Models\Blog; +use App\Models\HeaderTemplate; +use App\Models\FooterTemplate; +use App\Services\TemplateService; class BlogController extends Controller { public function index() { $settings = class_exists(Setting::class) ? (Setting::query()->first()) : null; - $posts = class_exists(Post::class) - ? Post::query()->where('status', 'published')->latest('published_at')->paginate(12) + $posts = class_exists(Blog::class) + ? Blog::with(['category', 'author']) + ->withCount('comments') + ->published() + ->latest('published_at') + ->paginate(12) : collect(); + // Render Header Template - En son aktif olan header template'i kullan + $renderedHeader = null; + $headerTemplate = HeaderTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + + if ($headerTemplate) { + $templateDefaults = $headerTemplate->default_data ?? []; + $renderedHeader = TemplateService::replacePlaceholders( + $headerTemplate->html_content, + $templateDefaults + ); + } + + // Render Footer Template - En son aktif olan footer template'i kullan + $renderedFooter = null; + $footerTemplate = FooterTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + + if ($footerTemplate) { + $templateDefaults = $footerTemplate->default_data ?? []; + $renderedFooter = TemplateService::replacePlaceholders( + $footerTemplate->html_content, + $templateDefaults + ); + } + return view('blog.index', [ 'settings' => $settings, 'posts' => $posts, + 'renderedHeader' => $renderedHeader, + 'renderedFooter' => $renderedFooter, 'meta' => [ 'title' => __('blog.meta-index-title'), 'description' => __('blog.meta-index-description'), @@ -27,18 +64,54 @@ class BlogController extends Controller public function show(string $slug) { $settings = class_exists(Setting::class) ? (Setting::query()->first()) : null; - if (!class_exists(Post::class)) { + if (!class_exists(Blog::class)) { abort(404); } - $post = Post::query()->where('slug', $slug)->where('status', 'published')->firstOrFail(); + $post = Blog::with(['category', 'author']) + ->withCount('comments') + ->published() + ->where('slug', $slug) + ->firstOrFail(); + + // Render Header Template - En son aktif olan header template'i kullan + $renderedHeader = null; + $headerTemplate = HeaderTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + + if ($headerTemplate) { + $templateDefaults = $headerTemplate->default_data ?? []; + $renderedHeader = TemplateService::replacePlaceholders( + $headerTemplate->html_content, + $templateDefaults, + $post + ); + } + + // Render Footer Template - En son aktif olan footer template'i kullan + $renderedFooter = null; + $footerTemplate = FooterTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + + if ($footerTemplate) { + $templateDefaults = $footerTemplate->default_data ?? []; + $renderedFooter = TemplateService::replacePlaceholders( + $footerTemplate->html_content, + $templateDefaults, + $post + ); + } return view('blog.show', [ 'settings' => $settings, 'post' => $post, + 'renderedHeader' => $renderedHeader, + 'renderedFooter' => $renderedFooter, 'meta' => [ 'title' => method_exists($post, 'translate') ? ($post->translate('meta_title') ?: $post->translate('title')) : ($post->meta_title ?? $post->title), 'description' => method_exists($post, 'translate') ? ($post->translate('meta_description') ?: $post->translate('excerpt')) : ($post->meta_description ?? $post->excerpt), - 'image' => $post->featured_image ?? null, + 'image' => $post->featured_image ? asset('storage/' . $post->featured_image) : null, ], ]); } diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 496857d..d7d8be4 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -4,6 +4,8 @@ namespace App\Http\Controllers; use App\Models\Page; use App\Models\Setting; +use App\Models\HeaderTemplate; +use App\Models\FooterTemplate; use App\Services\TemplateService; use Illuminate\Http\Request; @@ -66,14 +68,23 @@ class PageController extends Controller // Render Header Template $renderedHeader = null; - if ($page->headerTemplate) { + $headerTemplate = $page->headerTemplate; + + // Eğer sayfa için header seçilmemişse, en son aktif header template'i kullan + if (!$headerTemplate) { + $headerTemplate = HeaderTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + } + + if ($headerTemplate) { // Merge template defaults with page data (page data overrides defaults) - $templateDefaults = $page->headerTemplate->default_data ?? []; + $templateDefaults = $headerTemplate->default_data ?? []; $pageData = $page->header_data ?? []; $mergedHeaderData = array_merge($templateDefaults, $pageData); $renderedHeader = TemplateService::replacePlaceholders( - $page->headerTemplate->html_content, + $headerTemplate->html_content, $mergedHeaderData, $page ); @@ -81,14 +92,23 @@ class PageController extends Controller // Render Footer Template $renderedFooter = null; - if ($page->footerTemplate) { + $footerTemplate = $page->footerTemplate; + + // Eğer sayfa için footer seçilmemişse, en son aktif footer template'i kullan + if (!$footerTemplate) { + $footerTemplate = FooterTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + } + + if ($footerTemplate) { // Merge template defaults with page data (page data overrides defaults) - $templateDefaults = $page->footerTemplate->default_data ?? []; + $templateDefaults = $footerTemplate->default_data ?? []; $pageData = $page->footer_data ?? []; $mergedFooterData = array_merge($templateDefaults, $pageData); $renderedFooter = TemplateService::replacePlaceholders( - $page->footerTemplate->html_content, + $footerTemplate->html_content, $mergedFooterData, $page ); @@ -148,14 +168,23 @@ public function show($slug) // Render Header Template $renderedHeader = null; - if ($page->headerTemplate) { + $headerTemplate = $page->headerTemplate; + + // Eğer sayfa için header seçilmemişse, en son aktif header template'i kullan + if (!$headerTemplate) { + $headerTemplate = HeaderTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + } + + if ($headerTemplate) { // Merge template defaults with page data (page data overrides defaults) - $templateDefaults = $page->headerTemplate->default_data ?? []; + $templateDefaults = $headerTemplate->default_data ?? []; $pageData = $page->header_data ?? []; $mergedHeaderData = array_merge($templateDefaults, $pageData); $renderedHeader = TemplateService::replacePlaceholders( - $page->headerTemplate->html_content, + $headerTemplate->html_content, $mergedHeaderData, $page ); @@ -163,14 +192,23 @@ public function show($slug) // Render Footer Template $renderedFooter = null; - if ($page->footerTemplate) { + $footerTemplate = $page->footerTemplate; + + // Eğer sayfa için footer seçilmemişse, en son aktif footer template'i kullan + if (!$footerTemplate) { + $footerTemplate = FooterTemplate::where('is_active', true) + ->latest('updated_at') + ->first(); + } + + if ($footerTemplate) { // Merge template defaults with page data (page data overrides defaults) - $templateDefaults = $page->footerTemplate->default_data ?? []; + $templateDefaults = $footerTemplate->default_data ?? []; $pageData = $page->footer_data ?? []; $mergedFooterData = array_merge($templateDefaults, $pageData); $renderedFooter = TemplateService::replacePlaceholders( - $page->footerTemplate->html_content, + $footerTemplate->html_content, $mergedFooterData, $page ); diff --git a/app/Models/Blog.php b/app/Models/Blog.php index adcc672..fde3ee0 100644 --- a/app/Models/Blog.php +++ b/app/Models/Blog.php @@ -94,7 +94,10 @@ class Blog extends Model public function scopePublished($query) { return $query->where('status', 'published') - ->where('published_at', '<=', now()); + ->where(function($q) { + $q->whereNull('published_at') + ->orWhere('published_at', '<=', now()); + }); } public function scopeFeatured($query) diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php index 925ed94..8a4973a 100644 --- a/app/Services/TemplateService.php +++ b/app/Services/TemplateService.php @@ -5,6 +5,7 @@ namespace App\Services; use App\Models\HeaderTemplate; use App\Models\SectionTemplate; use App\Models\FooterTemplate; +use Illuminate\Database\Eloquent\Model; use Filament\Forms\Components\{ TextInput, Textarea, Select, Checkbox, CheckboxList, Radio, Toggle, ToggleButtons, DateTimePicker, DatePicker, @@ -31,19 +32,47 @@ class TemplateService $fields = []; foreach ($placeholders as $placeholder) { - $parts = explode('.', $placeholder); + // Handle special.* prefix - remove it for processing + $normalizedPlaceholder = $placeholder; + if (str_starts_with($placeholder, 'special.')) { + $normalizedPlaceholder = substr($placeholder, 8); // Remove 'special.' prefix + } - if (count($parts) !== 2) { + $parts = explode('.', $normalizedPlaceholder); + + if (count($parts) < 2) { continue; // Skip invalid placeholders } - [$type, $name] = $parts; + $type = $parts[0]; + + // Skip special.* placeholders - they are special placeholders (page.*, menu, staticMenu), not form fields + if (str_starts_with($placeholder, 'special.')) { + continue; + } // Skip custom.* placeholders - they are blade components, not form fields if ($type === 'custom') { continue; } + // Skip page.* placeholders - they are special placeholders, not form fields + if ($type === 'page') { + continue; + } + + // Skip menu and staticMenu placeholders - they are special placeholders, not form fields + if ($normalizedPlaceholder === 'menu' || $normalizedPlaceholder === 'staticMenu') { + continue; + } + + // For form fields, we expect type.field format (2 parts) + if (count($parts) !== 2) { + continue; // Skip invalid placeholders + } + + [$type, $name] = $parts; + $label = str($name)->title()->replace('_', ' ')->toString(); // Get existing value from existingData if provided @@ -248,11 +277,12 @@ class TemplateService /** * Parse placeholders from HTML content - * Format: {type.field_name} or {type.field-name} + * Format: {type.field_name} or {type.field-name} or {special.type.field} */ public static function parsePlaceholders(string $html): array { - preg_match_all('/\{([a-z]+\.[a-z][a-z_-]*)\}/i', $html, $matches); + // Pattern: {type.field} or {special.type.field} or {type.field.field2} etc. + preg_match_all('/\{([a-z]+(?:\.[a-z][a-z_-]*)+)\}/i', $html, $matches); return array_unique($matches[1] ?? []); } @@ -275,44 +305,48 @@ class TemplateService * Also supports {menu} and {staticMenu} placeholders for menu rendering * Also supports {custom.component_name} for custom blade components * Also supports {page.content}, {page.title}, {page.excerpt} for page data + * + * @param string $html HTML content with placeholders + * @param array $data Data array for placeholders + * @param Model|null $model Optional model instance (Page, Blog, etc.) for dynamic data */ - public static function replacePlaceholders(string $html, array $data, ?\App\Models\Page $page = null): string + public static function replacePlaceholders(string $html, array $data, ?Model $model = null): string { - // Handle special {page.content} placeholder - Sayfa içeriğini gösterir + // Handle special {page.content} placeholder - Sayfa/model içeriğini gösterir if (str_contains($html, '{page.content}')) { $pageContent = ''; - if ($page) { + if ($model) { // Translation desteği varsa çeviriyi kullan - if (method_exists($page, 'translate')) { - $pageContent = $page->translate('content') ?: $page->content; + if (method_exists($model, 'translate')) { + $pageContent = $model->translate('content') ?: ($model->content ?? ''); } else { - $pageContent = $page->content; + $pageContent = $model->content ?? ''; } } $html = str_replace('{page.content}', $pageContent ?? '', $html); } - // Handle special {page.title} placeholder - Sayfa başlığını gösterir + // Handle special {page.title} placeholder - Sayfa/model başlığını gösterir if (str_contains($html, '{page.title}')) { $pageTitle = ''; - if ($page) { - if (method_exists($page, 'translate')) { - $pageTitle = $page->translate('title') ?: $page->title; + if ($model) { + if (method_exists($model, 'translate')) { + $pageTitle = $model->translate('title') ?: ($model->title ?? ''); } else { - $pageTitle = $page->title; + $pageTitle = $model->title ?? ''; } } $html = str_replace('{page.title}', $pageTitle ?? '', $html); } - // Handle special {page.excerpt} placeholder - Sayfa özetini gösterir + // Handle special {page.excerpt} placeholder - Sayfa/model özetini gösterir if (str_contains($html, '{page.excerpt}')) { $pageExcerpt = ''; - if ($page) { - if (method_exists($page, 'translate')) { - $pageExcerpt = $page->translate('excerpt') ?: $page->excerpt; + if ($model) { + if (method_exists($model, 'translate')) { + $pageExcerpt = $model->translate('excerpt') ?: ($model->excerpt ?? ''); } else { - $pageExcerpt = $page->excerpt; + $pageExcerpt = $model->excerpt ?? ''; } } $html = str_replace('{page.excerpt}', $pageExcerpt ?? '', $html); @@ -373,8 +407,10 @@ class TemplateService } // First, parse all placeholders in the format {type.field_name} or {type.field-name} + // Also supports {special.page.title} format (special.* prefix) // Exclude custom.* from this regex to avoid double processing - preg_match_all('/\{([a-z]+\.[a-z][a-z_-]*)\}/i', $html, $matches); + // Pattern: {type.field} or {special.type.field} or {type.field.field2} etc. + preg_match_all('/\{([a-z]+(?:\.[a-z][a-z_-]*)+)\}/i', $html, $matches); $placeholders = array_unique($matches[1] ?? []); // Filter out custom.* placeholders as they're already handled above @@ -384,11 +420,74 @@ class TemplateService // Replace each placeholder foreach ($placeholders as $placeholder) { + // Handle special.* prefix - remove it and process as normal placeholder + // e.g., {special.page.title} -> {page.title}, {special.menu} -> {menu} + $originalPlaceholder = $placeholder; + if (str_starts_with($placeholder, 'special.')) { + $placeholder = substr($placeholder, 8); // Remove 'special.' prefix (8 characters) + } + // Extract type from placeholder $parts = explode('.', $placeholder); $type = $parts[0] ?? null; $field = $parts[1] ?? null; + // Handle special placeholders that were prefixed with special.* + // These are page data placeholders: page.content, page.title, page.excerpt + if ($type === 'page') { + if ($field === 'content' && str_contains($html, '{' . $originalPlaceholder . '}')) { + $pageContent = ''; + if ($page) { + if (method_exists($page, 'translate')) { + $pageContent = $page->translate('content') ?: $page->content; + } else { + $pageContent = $page->content; + } + } + $html = str_replace('{' . $originalPlaceholder . '}', $pageContent ?? '', $html); + continue; + } + + if ($field === 'title' && str_contains($html, '{' . $originalPlaceholder . '}')) { + $pageTitle = ''; + if ($page) { + if (method_exists($page, 'translate')) { + $pageTitle = $page->translate('title') ?: $page->title; + } else { + $pageTitle = $page->title; + } + } + $html = str_replace('{' . $originalPlaceholder . '}', $pageTitle ?? '', $html); + continue; + } + + if ($field === 'excerpt' && str_contains($html, '{' . $originalPlaceholder . '}')) { + $pageExcerpt = ''; + if ($page) { + if (method_exists($page, 'translate')) { + $pageExcerpt = $page->translate('excerpt') ?: $page->excerpt; + } else { + $pageExcerpt = $page->excerpt; + } + } + $html = str_replace('{' . $originalPlaceholder . '}', $pageExcerpt ?? '', $html); + continue; + } + } + + // Handle special menu placeholders that were prefixed with special.* + if ($placeholder === 'menu' && str_contains($html, '{' . $originalPlaceholder . '}')) { + $renderedMenu = \App\Services\MenuService::render(); + $html = str_replace('{' . $originalPlaceholder . '}', $renderedMenu, $html); + continue; + } + + if ($placeholder === 'staticMenu' && str_contains($html, '{' . $originalPlaceholder . '}')) { + $renderedStaticMenu = view('components.static-menu')->render(); + $html = str_replace('{' . $originalPlaceholder . '}', $renderedStaticMenu, $html); + continue; + } + // Handle setting.* placeholders - get value from Setting model if ($type === 'setting' && $field) { $value = setting($field, ''); @@ -444,11 +543,19 @@ class TemplateService } // Replace both {text.title} and {{text.title}} formats - $html = str_replace( - ["{{$placeholder}}", "{{{$placeholder}}}"], - [$value, $value], - $html - ); + // Also replace original placeholder if it was prefixed with special.* + $replacements = [ + "{{$placeholder}}", + "{{{$placeholder}}}" + ]; + + if ($originalPlaceholder !== $placeholder) { + // Original placeholder had special.* prefix, replace it too + $replacements[] = "{{$originalPlaceholder}}"; + $replacements[] = "{{{$originalPlaceholder}}}"; + } + + $html = str_replace($replacements, array_fill(0, count($replacements), $value), $html); } return $html; diff --git a/resources/views/components/custom/blog.blade.php b/resources/views/components/custom/blog.blade.php new file mode 100644 index 0000000..e172d1e --- /dev/null +++ b/resources/views/components/custom/blog.blade.php @@ -0,0 +1,88 @@ +@php + // Blog modelinden published içerikleri çek + $blogs = \App\Models\Blog::with(['category', 'author']) + ->withCount('comments') + ->published() + ->latest('published_at') + ->limit(6) + ->get(); +@endphp + +
+
+ + @if($blogs->count() > 0) +
+
+
+ @foreach($blogs as $blog) + @php + $title = method_exists($blog, 'translate') ? $blog->translate('title') : $blog->title; + $excerpt = method_exists($blog, 'translate') ? $blog->translate('excerpt') : ($blog->excerpt ?? ''); + $categoryName = $blog->category ? ($blog->category->name ?? '') : ''; + $publishedDate = $blog->published_at ? $blog->published_at->format('d M Y') : ''; + $imageUrl = $blog->featured_image ? asset('storage/' . $blog->featured_image) : asset('assets/img/photos/b4.jpg'); + $blogUrl = route('blog.show', $blog->slug); + $commentCount = $blog->comments_count ?? 0; + @endphp + +
+ + +
+ + @endforeach +
+ +
+ +
+ + @else +
+

{{ __('blog.empty', ['default' => 'No blog posts available at the moment.']) }}

+
+ @endif +
+
+ \ No newline at end of file