Implement Product and ProductCategory Resources: Created new Filament resources for managing products and product categories, including pages for listing, creating, and editing. Added schemas for forms and tables, integrated translation support, and established relationships between products and categories. Enhanced the navigation structure to include product and service links in the frontend menu, improving overall site organization and user experience.
This commit is contained in:
@@ -29,7 +29,7 @@ class ImportHtmlTemplates extends Command
|
||||
'span' => 'text.content',
|
||||
'address' => 'text.address',
|
||||
'button' => 'text.button',
|
||||
'a' => 'url.link',
|
||||
'a' => 'text.link_text',
|
||||
'li' => 'text.list_item',
|
||||
'label' => 'text.label',
|
||||
];
|
||||
@@ -260,7 +260,7 @@ class ImportHtmlTemplates extends Command
|
||||
$links = $xpath->query('.//a', $node);
|
||||
foreach ($links as $link) {
|
||||
$href = $link->getAttribute('href');
|
||||
if (empty($href) || str_starts_with($href, '#') || str_starts_with($href, 'javascript:') || str_starts_with($href, 'mailto:') || str_starts_with($href, 'tel:') || str_starts_with($href, '{')) continue;
|
||||
if (empty($href) || str_starts_with($href, 'javascript:') || str_starts_with($href, 'mailto:') || str_starts_with($href, 'tel:') || str_starts_with($href, '{')) continue;
|
||||
|
||||
$fixedHref = $this->fixAssetPath($href);
|
||||
if (str_ends_with($fixedHref, '.html')) {
|
||||
@@ -268,7 +268,7 @@ class ImportHtmlTemplates extends Command
|
||||
if($fixedHref == 'index') $fixedHref = '/';
|
||||
}
|
||||
|
||||
$key = $this->generateKey('url.link', $counts);
|
||||
$key = $this->generateKey('text.link_url', $counts);
|
||||
$data[$key] = $fixedHref;
|
||||
$link->setAttribute('href', "{" . $key . "}");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\ProductCategories\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Filament\Admin\Resources\ProductCategories\ProductCategoryResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateProductCategory extends CreateRecord
|
||||
{
|
||||
protected static string $resource = ProductCategoryResource::class;
|
||||
|
||||
protected function afterCreate(): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
TranslationTabs::saveTranslations($this->record, $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\ProductCategories\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Filament\Admin\Resources\ProductCategories\ProductCategoryResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditProductCategory extends EditRecord
|
||||
{
|
||||
protected static string $resource = ProductCategoryResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeFill(array $data): array
|
||||
{
|
||||
return array_merge($data, TranslationTabs::fillFromRecord($this->record));
|
||||
}
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
TranslationTabs::saveTranslations($this->record, $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\ProductCategories\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\ProductCategories\ProductCategoryResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListProductCategories extends ListRecords
|
||||
{
|
||||
protected static string $resource = ProductCategoryResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\ProductCategories;
|
||||
|
||||
use App\Filament\Admin\Resources\ProductCategories\Pages\CreateProductCategory;
|
||||
use App\Filament\Admin\Resources\ProductCategories\Pages\EditProductCategory;
|
||||
use App\Filament\Admin\Resources\ProductCategories\Pages\ListProductCategories;
|
||||
use App\Filament\Admin\Resources\ProductCategories\Schemas\ProductCategoryForm;
|
||||
use App\Filament\Admin\Resources\ProductCategories\Tables\ProductCategoriesTable;
|
||||
use App\Models\ProductCategory;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class ProductCategoryResource extends Resource
|
||||
{
|
||||
protected static ?string $model = ProductCategory::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedTag;
|
||||
|
||||
protected static \UnitEnum|string|null $navigationGroup = 'Ürün ve Hizmetler';
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return 'Kategoriler';
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return 'Kategori';
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return 'Kategoriler';
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return ProductCategoryForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return ProductCategoriesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListProductCategories::route('/'),
|
||||
'create' => CreateProductCategory::route('/create'),
|
||||
'edit' => EditProductCategory::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\ProductCategories\Schemas;
|
||||
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Models\ProductCategory;
|
||||
|
||||
class ProductCategoryForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->schema([
|
||||
Section::make('General')
|
||||
->schema([
|
||||
TextInput::make('slug')
|
||||
->required()
|
||||
->unique(ignoreRecord: true),
|
||||
Select::make('parent_id')
|
||||
->label('Parent Category')
|
||||
->options(function () {
|
||||
return ProductCategory::all()->mapWithKeys(function ($category) {
|
||||
$title = is_array($category->title)
|
||||
? ($category->title[app()->getLocale()] ?? first($category->title))
|
||||
: $category->title;
|
||||
return [$category->id => $title];
|
||||
});
|
||||
})
|
||||
->searchable(),
|
||||
TextInput::make('sort_order')
|
||||
->numeric()
|
||||
->default(0),
|
||||
Toggle::make('is_active')
|
||||
->default(true),
|
||||
])->columns(2),
|
||||
|
||||
Section::make('Translations')
|
||||
->schema([
|
||||
TranslationTabs::make([
|
||||
'title' => [
|
||||
'type' => 'text',
|
||||
'label' => 'Title',
|
||||
'required' => true,
|
||||
],
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\ProductCategories\Tables;
|
||||
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\ToggleColumn;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
|
||||
class ProductCategoriesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('title')
|
||||
->formatStateUsing(fn ($state) => is_array($state) ? ($state[app()->getLocale()] ?? reset($state)) : $state)
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('slug')
|
||||
->searchable(),
|
||||
TextColumn::make('parent.title') // This might fail if parent title is JSON
|
||||
->formatStateUsing(fn ($state) => is_array($state) ? ($state[app()->getLocale()] ?? reset($state)) : $state)
|
||||
->label('Parent'),
|
||||
TextColumn::make('sort_order')
|
||||
->sortable(),
|
||||
ToggleColumn::make('is_active'),
|
||||
TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Products\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Filament\Admin\Resources\Products\ProductResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateProduct extends CreateRecord
|
||||
{
|
||||
protected static string $resource = ProductResource::class;
|
||||
|
||||
protected function afterCreate(): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
TranslationTabs::saveTranslations($this->record, $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Products\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Filament\Admin\Resources\Products\ProductResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditProduct extends EditRecord
|
||||
{
|
||||
protected static string $resource = ProductResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeFill(array $data): array
|
||||
{
|
||||
return array_merge($data, TranslationTabs::fillFromRecord($this->record));
|
||||
}
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
TranslationTabs::saveTranslations($this->record, $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Products\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\Products\ProductResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListProducts extends ListRecords
|
||||
{
|
||||
protected static string $resource = ProductResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Products;
|
||||
|
||||
use App\Filament\Admin\Resources\Products\Pages\CreateProduct;
|
||||
use App\Filament\Admin\Resources\Products\Pages\EditProduct;
|
||||
use App\Filament\Admin\Resources\Products\Pages\ListProducts;
|
||||
use App\Filament\Admin\Resources\Products\Schemas\ProductForm;
|
||||
use App\Filament\Admin\Resources\Products\Tables\ProductsTable;
|
||||
use App\Models\Product;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class ProductResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Product::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCube;
|
||||
|
||||
protected static \UnitEnum|string|null $navigationGroup = 'Ürün ve Hizmetler';
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return 'Ürün ve Hizmet Listesi';
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return 'Ürün/Hizmet';
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return 'Ürün ve Hizmetler';
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return ProductForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return ProductsTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListProducts::route('/'),
|
||||
'create' => CreateProduct::route('/create'),
|
||||
'edit' => EditProduct::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Products\Schemas;
|
||||
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Models\ProductCategory;
|
||||
|
||||
class ProductForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->schema([
|
||||
Section::make('General')
|
||||
->schema([
|
||||
TextInput::make('slug')
|
||||
->required()
|
||||
->unique(ignoreRecord: true),
|
||||
Select::make('type')
|
||||
->options([
|
||||
'product' => 'Product',
|
||||
'service' => 'Service',
|
||||
])
|
||||
->required(),
|
||||
Select::make('product_category_id')
|
||||
->label('Category')
|
||||
->options(function () {
|
||||
return ProductCategory::all()->mapWithKeys(function ($category) {
|
||||
$title = is_array($category->title)
|
||||
? ($category->title[app()->getLocale()] ?? first($category->title))
|
||||
: $category->title;
|
||||
return [$category->id => $title];
|
||||
});
|
||||
})
|
||||
->searchable(),
|
||||
FileUpload::make('hero_image')
|
||||
->image()
|
||||
->directory('products'),
|
||||
TextInput::make('view_template')
|
||||
->label('Custom View Path')
|
||||
->placeholder('e.g. front.products.custom-page')
|
||||
->helperText('Leave empty to use the default landing page.'),
|
||||
TextInput::make('sort_order')
|
||||
->numeric()
|
||||
->default(0),
|
||||
Toggle::make('is_active')
|
||||
->default(true),
|
||||
])->columns(2),
|
||||
|
||||
Section::make('Translations')
|
||||
->schema([
|
||||
TranslationTabs::make([
|
||||
'title' => [
|
||||
'type' => 'text',
|
||||
'label' => 'Title',
|
||||
'required' => true,
|
||||
],
|
||||
'content' => [
|
||||
'type' => 'richtext',
|
||||
'label' => 'Content',
|
||||
],
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\Products\Tables;
|
||||
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\ToggleColumn;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
|
||||
class ProductsTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
ImageColumn::make('hero_image'),
|
||||
TextColumn::make('title')
|
||||
->formatStateUsing(fn ($state) => is_array($state) ? ($state[app()->getLocale()] ?? reset($state)) : $state)
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('type')
|
||||
->badge()
|
||||
->color(fn (string $state): string => match ($state) {
|
||||
'product' => 'info',
|
||||
'service' => 'success',
|
||||
default => 'gray',
|
||||
}),
|
||||
TextColumn::make('category.title')
|
||||
->formatStateUsing(fn ($state) => is_array($state) ? ($state[app()->getLocale()] ?? reset($state)) : $state)
|
||||
->label('Category'),
|
||||
ToggleColumn::make('is_active'),
|
||||
TextColumn::make('sort_order')
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -130,11 +130,20 @@ class SettingForm
|
||||
->helperText(__('settings.value_helper'))
|
||||
->required()
|
||||
->visible(fn (Get $get) => $get('type') === 'file')
|
||||
->image()
|
||||
->disk('public')
|
||||
->directory('settings')
|
||||
->acceptedFileTypes(['image/*', 'application/pdf', 'text/*'])
|
||||
->maxSize(10240) // 10MB
|
||||
->columnSpanFull(),
|
||||
->openable()
|
||||
->downloadable()
|
||||
->previewable(true)
|
||||
->columnSpanFull()
|
||||
->afterStateHydrated(function ($component, $state, $record) {
|
||||
if ($record && $record->type === 'file') {
|
||||
$component->state($record->value);
|
||||
}
|
||||
}),
|
||||
|
||||
DatePicker::make('value_date')
|
||||
->label(__('settings.value'))
|
||||
|
||||
@@ -16,7 +16,74 @@ class PageController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// 1) is_homepage işaretli ve published (en son güncellenen)
|
||||
// 1. Statik "home" Template Kontrolü
|
||||
if (view()->exists('templates.home')) {
|
||||
$page = Page::with(['headerTemplate', 'footerTemplate'])
|
||||
->where('is_homepage', true)
|
||||
->where('status', 'published')
|
||||
->latest('updated_at')
|
||||
->first();
|
||||
|
||||
$settings = class_exists(Setting::class) ? (Setting::query()->first()) : null;
|
||||
|
||||
$renderedHeader = null;
|
||||
$renderedFooter = null;
|
||||
$metaTitle = null;
|
||||
$metaDescription = null;
|
||||
|
||||
if ($page) {
|
||||
// Meta bilgileri
|
||||
$metaTitle = method_exists($page, 'translate')
|
||||
? ($page->translate('meta_title') ?: $page->translate('title'))
|
||||
: ($page->meta_title ?? $page->title ?? null);
|
||||
|
||||
$metaDescription = method_exists($page, 'translate')
|
||||
? ($page->translate('meta_description') ?: ($page->excerpt ?? null))
|
||||
: ($page->meta_description ?? $page->excerpt ?? null);
|
||||
|
||||
// Header Render
|
||||
$headerTemplate = $page->headerTemplate;
|
||||
if (!$headerTemplate) {
|
||||
$headerTemplate = HeaderTemplate::where('is_active', true)->latest('updated_at')->first();
|
||||
}
|
||||
|
||||
if ($headerTemplate) {
|
||||
$templateDefaults = $headerTemplate->default_data ?? [];
|
||||
$pageData = $page->header_data ?? [];
|
||||
$mergedHeaderData = array_merge($templateDefaults, $pageData);
|
||||
$renderedHeader = TemplateService::replacePlaceholders($headerTemplate->html_content, $mergedHeaderData, $page);
|
||||
}
|
||||
|
||||
// Footer Render
|
||||
$footerTemplate = $page->footerTemplate;
|
||||
if (!$footerTemplate) {
|
||||
$footerTemplate = FooterTemplate::where('is_active', true)->latest('updated_at')->first();
|
||||
}
|
||||
|
||||
if ($footerTemplate) {
|
||||
$templateDefaults = $footerTemplate->default_data ?? [];
|
||||
$pageData = $page->footer_data ?? [];
|
||||
$mergedFooterData = array_merge($templateDefaults, $pageData);
|
||||
$renderedFooter = TemplateService::replacePlaceholders($footerTemplate->html_content, $mergedFooterData, $page);
|
||||
}
|
||||
}
|
||||
|
||||
return view('templates.home', [
|
||||
'page' => $page,
|
||||
'settings' => $settings,
|
||||
'sections' => $page ? ($page->sections ?? []) : [],
|
||||
'templatedSections' => $page ? ($page->templated_sections ?? collect([])) : collect([]),
|
||||
'renderedHeader' => $renderedHeader,
|
||||
'renderedFooter' => $renderedFooter,
|
||||
'meta' => [
|
||||
'title' => $metaTitle ?: ($settings->default_meta_title ?? config('app.name')),
|
||||
'description' => $metaDescription ?: ($settings->default_meta_description ?? null),
|
||||
'image' => $settings->default_meta_image ?? null,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// 2. Normal Dinamik Akış (Mevcut kod devam eder)
|
||||
$page = Page::with(['headerTemplate', 'footerTemplate'])
|
||||
->where('is_homepage', true)
|
||||
->where('status', 'published')
|
||||
@@ -132,8 +199,80 @@ class PageController extends Controller
|
||||
/**
|
||||
* Display a specific page by slug
|
||||
*/
|
||||
public function show($slug)
|
||||
public function show($slug)
|
||||
{
|
||||
// 1. Statik View Kontrolü
|
||||
// Eğer templates/{slug}.blade.php varsa, veritabanında kayıt olsun veya olmasın bu dosyayı render et.
|
||||
if (view()->exists("templates.$slug")) {
|
||||
$page = Page::with(['headerTemplate', 'footerTemplate'])
|
||||
->where('slug', $slug)
|
||||
->where('status', 'published')
|
||||
->first();
|
||||
|
||||
$settings = class_exists(Setting::class) ? (Setting::query()->first()) : null;
|
||||
|
||||
// Meta verilerini hazırla
|
||||
$metaTitle = null;
|
||||
$metaDescription = null;
|
||||
|
||||
if ($page) {
|
||||
$metaTitle = method_exists($page, 'translate')
|
||||
? ($page->translate('meta_title') ?: $page->translate('title'))
|
||||
: ($page->meta_title ?? $page->title ?? null);
|
||||
|
||||
$metaDescription = method_exists($page, 'translate')
|
||||
? ($page->translate('meta_description') ?: ($page->excerpt ?? null))
|
||||
: ($page->meta_description ?? $page->excerpt ?? null);
|
||||
}
|
||||
|
||||
// Statik sayfa için header/footer render işlemleri (Eğer page varsa)
|
||||
$renderedHeader = null;
|
||||
$renderedFooter = null;
|
||||
|
||||
if ($page) {
|
||||
// Render Header Template
|
||||
$headerTemplate = $page->headerTemplate;
|
||||
if (!$headerTemplate) {
|
||||
$headerTemplate = HeaderTemplate::where('is_active', true)->latest('updated_at')->first();
|
||||
}
|
||||
|
||||
if ($headerTemplate) {
|
||||
$templateDefaults = $headerTemplate->default_data ?? [];
|
||||
$pageData = $page->header_data ?? [];
|
||||
$mergedHeaderData = array_merge($templateDefaults, $pageData);
|
||||
$renderedHeader = TemplateService::replacePlaceholders($headerTemplate->html_content, $mergedHeaderData, $page);
|
||||
}
|
||||
|
||||
// Render Footer Template
|
||||
$footerTemplate = $page->footerTemplate;
|
||||
if (!$footerTemplate) {
|
||||
$footerTemplate = FooterTemplate::where('is_active', true)->latest('updated_at')->first();
|
||||
}
|
||||
|
||||
if ($footerTemplate) {
|
||||
$templateDefaults = $footerTemplate->default_data ?? [];
|
||||
$pageData = $page->footer_data ?? [];
|
||||
$mergedFooterData = array_merge($templateDefaults, $pageData);
|
||||
$renderedFooter = TemplateService::replacePlaceholders($footerTemplate->html_content, $mergedFooterData, $page);
|
||||
}
|
||||
}
|
||||
|
||||
return view("templates.$slug", [
|
||||
'page' => $page,
|
||||
'settings' => $settings,
|
||||
'sections' => $page->sections ?? [],
|
||||
'templatedSections' => $page->templated_sections ?? collect([]),
|
||||
'renderedHeader' => $renderedHeader,
|
||||
'renderedFooter' => $renderedFooter,
|
||||
'meta' => [
|
||||
'title' => $metaTitle ?: ($settings->default_meta_title ?? config('app.name')),
|
||||
'description' => $metaDescription ?: ($settings->default_meta_description ?? null),
|
||||
'image' => $settings->default_meta_image ?? null,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// 2. Normal Dinamik Akış (DB Kaydı Zorunlu)
|
||||
$page = Page::with(['headerTemplate', 'footerTemplate'])
|
||||
->where('slug', $slug)
|
||||
->where('status', 'published')
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Product;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
public function show($slug)
|
||||
{
|
||||
$product = Product::where('slug', $slug)
|
||||
->where('is_active', true)
|
||||
->with('category')
|
||||
->firstOrFail();
|
||||
|
||||
if ($product->view_template && view()->exists($product->view_template)) {
|
||||
return view($product->view_template, compact('product'));
|
||||
}
|
||||
|
||||
return view('front.products.show', compact('product'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Traits\HasTranslations;
|
||||
|
||||
class Product extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes, HasTranslations;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'type',
|
||||
'product_category_id',
|
||||
'hero_image',
|
||||
'content',
|
||||
'view_template',
|
||||
'sort_order',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $translatable = [
|
||||
'title',
|
||||
'content',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'title' => 'array',
|
||||
'content' => 'array',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::saving(function ($model) {
|
||||
// Eğer title boşsa ve translations içinden bir title geliyorsa onu ata
|
||||
if (empty($model->title)) {
|
||||
$translations = request()->input('translations');
|
||||
$defaultLocale = app()->getLocale();
|
||||
|
||||
if (is_array($translations)) {
|
||||
if (!empty($translations[$defaultLocale]['title'])) {
|
||||
$model->title = $translations[$defaultLocale]['title'];
|
||||
} else {
|
||||
foreach ($translations as $locale => $fields) {
|
||||
if (!empty($fields['title'])) {
|
||||
$model->title = $fields['title'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($model->title)) {
|
||||
$model->title = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(ProductCategory::class, 'product_category_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Traits\HasTranslations;
|
||||
|
||||
class ProductCategory extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes, HasTranslations;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'parent_id',
|
||||
'sort_order',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $translatable = [
|
||||
'title',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'title' => 'array',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::saving(function ($model) {
|
||||
// Eğer title boşsa ve translations içinden bir title geliyorsa onu ata
|
||||
if (empty($model->title)) {
|
||||
$translations = request()->input('translations');
|
||||
$defaultLocale = app()->getLocale();
|
||||
|
||||
if (is_array($translations)) {
|
||||
// Önce varsayılan dildeki çeviriye bak
|
||||
if (!empty($translations[$defaultLocale]['title'])) {
|
||||
$model->title = $translations[$defaultLocale]['title'];
|
||||
}
|
||||
// Yoksa ilk bulduğu dolu çeviriyi al
|
||||
else {
|
||||
foreach ($translations as $locale => $fields) {
|
||||
if (!empty($fields['title'])) {
|
||||
$model->title = $fields['title'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hala boşsa boş bir array veya string ata (veritabanı hatasını önlemek için)
|
||||
if (empty($model->title)) {
|
||||
$model->title = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(ProductCategory::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(ProductCategory::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function products()
|
||||
{
|
||||
return $this->hasMany(Product::class);
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class TemplateService
|
||||
|
||||
'url' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->url()
|
||||
// ->url() // Allow #, mailto, tel, relative paths
|
||||
->maxLength(500),
|
||||
|
||||
'tel' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
|
||||
Reference in New Issue
Block a user