Yeni Tenant Teması Oluşturma
Multi-Tenant Merkezi Tema Sistemi
Özet
Ne Yapıyoruz?
- • Merkezi tema yapısı kuruyoruz
- • "Simple" altın standart tema olacak
- • Yeni tenantlar merkezi yapıyı kullanacak
- • Eski sistem (Muzibu/İxtif) çalışmaya devam edecek
Dokunulmayacaklar
- •
Modules/*/themes/muzibu/ - •
Modules/*/themes/ixtif/ - • Mevcut ThemeService fallback mantığı
Temel Kurallar
1. Merkezi Konum
Tüm yeni tema dosyaları resources/views/themes/ altında olacak.
2. Simple = Fallback
Tenant'ta dosya yoksa themes/simple/ kullanılır. Bu tema mükemmel olmalı.
3. Tenant Override
Tenant sadece farklı olan dosyaları override eder. Örn: sadece homepage.blade.php
4. CSS Variables
Renkler CSS variables ile. Tenant sadece :root değişkenlerini değiştirir.
5. Geriye Uyumluluk
ThemeService önce merkezi, sonra modül içi, sonra simple'a bakar.
Klasör Yapısı
resources/views/themes/ │ ├── simple/ ← ALTIN STANDART │ │ │ ├── layouts/ │ │ ├── app.blade.php ← Master template │ │ ├── header.blade.php ← Navigation bar │ │ ├── footer.blade.php ← Site footer │ │ └── partials/ │ │ ├── head.blade.php ← <head> içeriği │ │ ├── scripts.blade.php ← JS yüklemeleri │ │ ├── breadcrumb.blade.php ← Breadcrumb │ │ └── meta.blade.php ← SEO meta tags │ │ │ ├── homepage.blade.php ← Anasayfa │ │ │ ├── blog/ ← Blog modülü │ │ ├── index.blade.php ← Liste │ │ ├── show.blade.php ← Detay │ │ └── partials/ │ │ └── card.blade.php ← Blog kartı │ │ │ ├── page/ ← Sayfa modülü │ │ └── show.blade.php │ │ │ ├── shop/ ← Mağaza modülü │ │ ├── index.blade.php │ │ ├── show.blade.php │ │ ├── cart.blade.php │ │ └── partials/ │ │ └── product-card.blade.php │ │ │ ├── portfolio/ ← Portfolyo modülü │ │ ├── index.blade.php │ │ └── show.blade.php │ │ │ ├── announcement/ ← Duyuru modülü │ │ ├── index.blade.php │ │ └── show.blade.php │ │ │ ├── components/ ← Ortak componentler │ │ ├── card.blade.php │ │ ├── pagination.blade.php │ │ ├── search-box.blade.php │ │ ├── social-share.blade.php │ │ ├── image.blade.php │ │ └── alert.blade.php │ │ │ ├── auth/ ← Giriş/Kayıt │ │ ├── login.blade.php │ │ ├── register.blade.php │ │ └── forgot-password.blade.php │ │ │ └── config.json ← Tema ayarları │ ├── tenant-{id}/ ← YENİ TENANT ÖRNEK │ ├── homepage.blade.php ← Sadece özel anasayfa │ ├── config.json ← Tenant ayarları │ └── blog/ ← İsterse override │ └── show.blade.php │ ├── muzibu/ ← MEVCUT (dokunma) │ └── ... │ └── ixtif/ ← MEVCUT (dokunma) └── ...
Oluşturulacak Dosyalar
| Dosya | Açıklama | Öncelik |
|---|---|---|
| Layouts | ||
layouts/app.blade.php |
Master template - HTML yapısı | P1 |
layouts/header.blade.php |
Navigation bar, logo, menü | P1 |
layouts/footer.blade.php |
Footer, linkler, copyright | P1 |
layouts/partials/head.blade.php |
Meta, CSS, fonts | P1 |
layouts/partials/scripts.blade.php |
JS dosyaları, Livewire | P1 |
layouts/partials/breadcrumb.blade.php |
Breadcrumb navigation | P2 |
| Homepage | ||
homepage.blade.php |
Anasayfa - hero, sections | P1 |
| Blog Modülü | ||
blog/index.blade.php |
Blog listesi | P1 |
blog/show.blade.php |
Blog detay | P1 |
blog/partials/card.blade.php |
Blog kartı component | P2 |
| Page Modülü | ||
page/show.blade.php |
Sayfa detay | P1 |
| Shop Modülü | ||
shop/index.blade.php |
Ürün listesi | P2 |
shop/show.blade.php |
Ürün detay | P2 |
shop/cart.blade.php |
Sepet sayfası | P2 |
| Portfolio Modülü | ||
portfolio/index.blade.php |
Portfolyo listesi | P3 |
portfolio/show.blade.php |
Portfolyo detay | P3 |
| Announcement Modülü | ||
announcement/index.blade.php |
Duyuru listesi | P3 |
announcement/show.blade.php |
Duyuru detay | P3 |
| Components | ||
components/card.blade.php |
Genel kart component | P2 |
components/pagination.blade.php |
Sayfalama | P2 |
components/search-box.blade.php |
Arama kutusu | P3 |
components/social-share.blade.php |
Sosyal medya paylaşım | P3 |
| Auth | ||
auth/login.blade.php |
Giriş sayfası | P3 |
auth/register.blade.php |
Kayıt sayfası | P3 |
| Config | ||
config.json |
Tema ayarları (renkler, font, vs) | P2 |
Layouts Yapısı
app.blade.php
<!DOCTYPE html> <html lang="{{ app()->getLocale() }}" > <head> @include('themes.simple.layouts.partials.head') @stack('styles') </head> <body class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100"> @include('themes.simple.layouts.header') <main class="min-h-screen"> @yield('content') </main> @include('themes.simple.layouts.footer') @include('themes.simple.layouts.partials.scripts') @stack('scripts') </body> </html>
partials/head.blade.php
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ $title ?? setting('site_name') }}</title> {{-- SEO Meta --}} <x-seo-meta /> {{-- Favicon --}} <link rel="icon" href="{{ setting('favicon') }}"> {{-- CSS --}} <link rel="stylesheet" href="{{ tenant_css() }}"> <link rel="stylesheet" href="{{ asset('css/fontawesome.min.css') }}"> {{-- Tema CSS Variables --}} <style> :root { --color-primary: {{ $themeConfig['primary'] ?? '#8b5cf6' }}; --color-secondary: {{ $themeConfig['secondary'] ?? '#64748b' }}; --color-accent: {{ $themeConfig['accent'] ?? '#f59e0b' }}; } </style> {{-- Livewire Styles --}} @livewireStyles
header.blade.php
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50"> <nav class="container mx-auto px-4 py-4"> <div class="flex items-center justify-between"> {{-- Logo --}} <a href="{{ url('/') }}" class="flex items-center"> <img src="{{ setting('logo') }}" alt="{{ setting('site_name') }}" class="h-10"> </a> {{-- Navigation --}} <div class="hidden md:flex items-center space-x-6"> @foreach($menuItems as $item) <a href="{{ $item->url }}" class="text-gray-600 hover:text-primary"> {{ $item->title }} </a> @endforeach </div> {{-- Mobile Menu Button --}} <button class="md:hidden" @click="mobileMenuOpen = !mobileMenuOpen"> <i class="fas fa-bars text-xl"></i> </button> </div> </nav> </header>
Homepage Yapısı
@extends('themes.simple.layouts.app') @section('content') {{-- Hero Section --}} <section class="bg-gradient-to-r from-primary to-secondary py-20"> <div class="container mx-auto px-4 text-center text-white"> <h1 class="text-4xl md:text-6xl font-bold mb-4"> {{ setting('site_slogan') ?? 'Hoş Geldiniz' }} </h1> <p class="text-xl opacity-90"> {{ setting('site_description') }} </p> </div> </section> {{-- Featured Content --}} <section class="py-16"> <div class="container mx-auto px-4"> <h2 class="text-3xl font-bold mb-8 text-center">Öne Çıkanlar</h2> <div class="grid md:grid-cols-3 gap-6"> @foreach($featuredItems as $item) @include('themes.simple.components.card', ['item' => $item]) @endforeach </div> </div> </section> {{-- Latest Blog Posts --}} @if(isset($latestPosts) && $latestPosts->count()) <section class="py-16 bg-gray-100 dark:bg-gray-800"> <div class="container mx-auto px-4"> <h2 class="text-3xl font-bold mb-8">Son Yazılar</h2> <div class="grid md:grid-cols-3 gap-6"> @foreach($latestPosts as $post) @include('themes.simple.blog.partials.card', ['post' => $post]) @endforeach </div> </div> </section> @endif @endsection
Modül View Yapıları
blog/index.blade.php
@extends('themes.simple.layouts.app') @section('content') <div class="container mx-auto px-4 py-8"> @include('themes.simple.layouts.partials.breadcrumb') <h1 class="text-3xl font-bold mb-8">Blog</h1> <div class="grid md:grid-cols-3 gap-6"> @foreach($posts as $post) @include('themes.simple.blog.partials.card') @endforeach </div> {{ $posts->links() }} </div> @endsection
blog/show.blade.php
@extends('themes.simple.layouts.app') @section('content') <article class="container mx-auto px-4 py-8"> @include('themes.simple.layouts.partials.breadcrumb') {{-- Hero Image --}} @if($post->hero) <img src="{{ thumb($post->hero, 1200, 600) }}" class="w-full rounded-xl mb-8"> @endif <h1 class="text-4xl font-bold mb-4"> {{ $post->title }} </h1> <div class="prose dark:prose-invert max-w-none"> {!! $post->content !!} </div> @include('themes.simple.components.social-share') </article> @endsection
CSS Variables Sistemi
config.json Örneği
{
"name": "simple",
"title": "Simple Theme",
"version": "1.0.0",
"colors": {
"primary": "#8b5cf6",
"secondary": "#64748b",
"accent": "#f59e0b",
"success": "#22c55e",
"danger": "#ef4444",
"warning": "#f59e0b",
"info": "#3b82f6"
},
"fonts": {
"heading": "Inter",
"body": "Inter"
},
"layout": {
"container_width": "1280px",
"header_height": "64px",
"sidebar_width": "280px"
},
"features": {
"dark_mode": true,
"sticky_header": true,
"back_to_top": true
}
}
Tenant Override Örneği
// themes/tenant-123/config.json { "name": "tenant-123", "extends": "simple", ← Simple'ı extend ediyor "colors": { "primary": "#f97316", ← Sadece primary değişti "accent": "#eab308" } // Diğer tüm ayarlar simple'dan gelir }
ThemeService Güncellemesi
Yeni Fallback Sırası
// getThemeViewPath() güncellemesi public function getThemeViewPath(string $view, string $module = null): string { $themeName = $this->getActiveTheme()->name; $paths = []; if ($module) { // 1. Merkezi tenant view $paths[] = "themes.{$themeName}.{$module}.{$view}"; // 2. Modül içi tenant view (eski sistem - geriye uyumluluk) $paths[] = "{$module}::themes.{$themeName}.{$view}"; // 3. Merkezi simple view if ($themeName !== 'simple') { $paths[] = "themes.simple.{$module}.{$view}"; } // 4. Modül içi simple view (eski sistem) $paths[] = "{$module}::themes.simple.{$view}"; // 5. Front fallback (son çare) $paths[] = "{$module}::front.{$view}"; } else { // Modülsüz view'lar için $paths[] = "themes.{$themeName}.{$view}"; if ($themeName !== 'simple') { $paths[] = "themes.simple.{$view}"; } } foreach ($paths as $path) { if (view()->exists($path)) { return $path; } } throw new ViewNotFoundException("View not found: {$view}"); }
Dikkat
Bu değişiklik mevcut Muzibu ve İxtif temalarını etkilemez.
Onlar Modules/*/themes/ içinde kalmaya devam eder ve
2. sırada kontrol edilir.
Yeni Tenant Teması Oluşturma
Klasör Oluştur
mkdir -p resources/views/themes/tenant-{ID}/
config.json Oluştur
{
"name": "tenant-{ID}",
"extends": "simple",
"colors": {
"primary": "#YOUR_COLOR"
}
}
homepage.blade.php Oluştur (Opsiyonel)
@extends('themes.simple.layouts.app') @section('content') {{-- Tenant'a özel anasayfa içeriği --}} ... @endsection
Veritabanına Kaydet
-- themes tablosuna ekle INSERT INTO themes (name, title, folder_name, is_active) VALUES ('tenant-{ID}', 'Tenant Adı', 'tenant-{ID}', 1); -- tenant'ı güncelle UPDATE tenants SET theme_id = {THEME_ID} WHERE id = {TENANT_ID};
Cache Temizle
php artisan cache:clear php artisan view:clear php artisan config:clear
Sonuç
Artık tenant-{ID} teması aktif. Sadece homepage.blade.php ve
config.json oluşturdun. Tüm iç sayfalar otomatik olarak
simple temasından geliyor.