Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Çok Kiracılı Laravel SaaS için Çift Bildirim Sistemi Oluşturma
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Çok Kiracılı Laravel SaaS için Çift Bildirim Sistemi Oluşturma

Yazılım

Çok Kiracılı Laravel SaaS için Çift Bildirim Sistemi Oluşturma

teknomers
Son güncelleme: 11 Mayıs 2026 12:40
teknomers
Paylaş
Paylaş

SaaS uygulamalarınızın kullanıcılarla etkili bir şekilde iletişim kurabilmesi önemlidir. Sadece e-posta değil, kullanıcıya yönelik bildirimlerin de olması gerekmektedir. Bu bildirimler, kaydedilmeli, okunma takibi yapılmalı, çok dilli destek sunmalı ve yöneticilere spesifik kullanıcı gruplarını hedefleme olanağı sağlanmalıdır.

Kohana.io‘yu geliştirdim; bu küçük işletmeler için bir CRM/ERP çözümüdür. Bildirim modülü, aynı tabloyu ve kullanıcı arayüzünü paylaşan iki mimari sistem olarak evrildi: yönetici yayınları ve otomatik sistem bildirimleri.

Şimdi bu modülü, açık kaynak bir SaaS çerçevesi olan LaraFoundry‘ye aktaracağım. Bu yazıda, tam uygulama sürecini inceleyeceğiz.


İçindekiler

  1. Çift Mimari
  2. Veritabanı Şeması
  3. Çok Dilli İçerik
  4. Alıcı Segmentasyonu
  5. Yönetici CRUD & İş Akışı
  6. Sistem Bildirimleri
  7. Gönderim & Okunma İstatistikleri
  8. Frontend Kullanıcı Deneyimi
  9. Test Süreçleri
  10. Tasarım Kararları


Çift Mimari

LaraFoundry, bir sistemde bir arada varolan iki bildirim türüne sahiptir:

Yönetici bildirimleri – yönetici panelinde manuel olarak oluşturulur:

  • Veritabanında JSON formatında saklanan çok dilli başlıklar ve içerikler
  • Alıcı filtreleri: ülke, cinsiyet, yaş aralığı, kayıt tarihi, etkinlik seviyesi, doğrulama durumu
  • Taslak → Gönderilme iş akışı ile görünürlük takibi
  • Teslimat ve okunma istatistikleri

Sistem bildirimleri – kuyruklu işler tarafından programatik olarak oluşturulur:

  • Dahili çeviri anahtarları ve dinamik parametrelerle
  • Olaylar gerçekleştiğinde otomatik olarak gönderilir (şirket oluşturuldu, davet kabul edildi, ödeme başarısız)
  • 30 gün sonra otomatik olarak süresi dolan bildirimler
  • Kullanıcı arayüzi için zengin meta veriler

Her iki tür de aynı kullanıcının bildirim panelinde görünmektedir. Kullanıcı, bir bildirimin nereden geldiğini bilmez veya önemsemez.



Veritabanı Şeması


Bildirimler Tablosu

Schema::create('notifications', function (Blueprint $table) {
        $table->id();
        $table->string('code')->index();
        $table->enum('notification_type', ['admin', 'system']);
        $table->enum('status', ['draft', 'sent']);

        // Sistem bildirimleri için - çeviri anahtarları
        $table->string('title_key')->nullable();
        $table->string('body_key')->nullable();
        $table->json('params')->nullable();

        // Yönetici bildirimleri için - saklanan çeviriler
        $table->json('title_translations')->nullable();
        $table->json('body_translations')->nullable();

        // Yönetici bildirimleri için - hedefleme
        $table->json('recipient_filters')->nullable();
        $table->json('data')->nullable();

        // Zamanlama
        $table->timestamp('visible_from')->nullable();
        $table->timestamp('visible_until')->nullable();
        $table->timestamps();
    });

Tek tablo içinde iki set nullable kolon bulunur. notification_type enum, hangi setin kullanılacağını belirler. Bu, kullanıcı tarafında kaynak önemli olmadığı için iki tablodan daha basittir.


Bildirim-Kullanıcı Pivot

Schema::create('notification_user', function (Blueprint $table) {
        $table->id();
        $table->foreignId('notification_id')->constrained()->cascadeOnDelete();
        $table->foreignId('user_id')->constrained()->cascadeOnDelete();
        $table->timestamp('read_at')->nullable()->index();
        $table->timestamps();
        $table->unique(['notification_id', 'user_id']);
    });

read_at nullable’dır. Null = okunmamış, Timestamp = okunmuş (ve ne zaman okunduğu kaydedilir). Tekil kısıtlama, tekrarlanan eklemeleri engeller.


Model

class Notification extends Model
    {
        use HasFactory;

        protected function casts(): array
        {
            return [
                'params' => 'array',
                'recipient_filters' => 'array',
                'title_translations' => 'array',
                'body_translations' => 'array',
                'data' => 'array',
                'visible_from' => 'datetime',
                'visible_until' => 'datetime',
            ];
        }

        public function users(): BelongsToMany
        {
            return $this->belongsToMany(User::class)
                ->withPivot('read_at')
                ->withTimestamps();
        }

        // Scopes
        public function scopeDraft($q) { $q->where('status', 'draft'); }
        public function scopeSent($q) { $q->where('status', 'sent'); }
        public function scopeAdmin($q) { $q->where('notification_type', 'admin'); }
        public function scopeSystem($q) { $q->where('notification_type', 'system'); }

        // Helper Methods
        public function isDraft(): bool { return $this->status === 'draft'; }
        public function isSent(): bool { return $this->status === 'sent'; }
        public function isAdmin(): bool { return $this->notification_type === 'admin'; }
        public function isSystem(): bool { return $this->notification_type === 'system'; }
    }


Çok Dilli İçerik

Hem getLocalizedTitle() metodu, her iki bildirim türünü de işler:

public function getLocalizedTitle(string $locale = 'en'): string
    {
        if ($this->isSystem()) {
            return __($this->title_key, $this->params ?? [], $locale);
        }

        return $this->title_translations[$locale]
            ?? $this->title_translations['en']
            ?? '';
    }

Sistem bildirimleri, Laravel’in __() yardımcı fonksiyonunu kullanır; çeviri dosyaları ve parametrelerle standart bir yaklaşım. Yönetici bildirimleri, JSON araması ile düşme yaparak çalışır: talep edilen dil → İngilizce → boş dize.


Yönetici Panelinde Otomatik Çeviri

Yönetici oluşturma formu, bir dil akordeonu taşır. İngilizce zorunludur. Diğer diller opsiyoneldir. Her dil sekmesinin “Çevir” butonu bulunmaktadır:

POST /admin/translate
{
    "source_locale": "en",
    "text": "Sistem bakımı Cuma günü",
    "target_locales": ["uk", "de"]
}

Tek tıkla, tüm boş dil alanları doldurulur. Yönetici inceleyip ayarlarını yapar. Sadece boşalan alanlar doldurulur; zaten dolu çeviriler korunur.

İçerik alanı, dil başına 10,000 karaktere kadar destekler.



Alıcı Segmentasyonu

Yönetici bildirimleri oluşturulurken, yönetici alıcıları yapılandırır:

FiltreSeçenekler
Ülkeavailable_countries konfigürasyonundan
CinsiyetErkek/Kadın
Yaş aralığı16 – 100
Kayıt tarihiHepsi/Bugün/Bu ay/Bu yıl
Son etkinlikHepsi/Daha aktif/Daha az aktif
E-posta doğrulandı mıHepsi/Doğrulandı/Doğrulanmadı
Telefon doğrulandı mıHepsi/Doğrulandı/Doğrulanmadı

Form, filtreler değiştikçe güncellenen canlı bir kullanıcı sayısını gösterir:

Filtreleri karşılayan alıcılar: 847 kullanıcı

500 ms’de debounclanır. Kullanıcı sayısı 0 ise gönderim butonu devre dışı bırakılır.


Filtre Uygulaması

Filtreler recipient_filters içinde JSON olarak saklanır ve AdminUsersFilter aracılığıyla uygulanır:

public function send(Notification $notification, AdminUsersFilter $filter): RedirectResponse
    {
        DB::transaction(function() use ($notification, $filter) {
            $users = User::query()
                ->filter($filter)
                ->where('email', '!=', config('own.admin_email'))
                ->pluck('id');

            $notification->users()->attach($users->mapWithKeys(fn($id) => [
                $id => ['created_at' => now(), 'updated_at' => now()],
            ]));

            $notification->update(['status' => 'sent']);
        });
    }

HasUserFilterRules trait’i, kullanıcı hedeflemesi gerektiren herhangi bir özellikte yeniden kullanılabilir ortak doğrulama kurallarını sağlar.



Yönetici CRUD ve İş Akışı


Yollar

GET    /admin/notifications               → index
GET    /admin/notifications/create         → create
POST   /admin/notifications                → store (draft)
GET    /admin/notifications/{id}/edit      → edit
PUT    /admin/notifications/{id}           → update
DELETE /admin/notifications/{id}           → destroy
POST   /admin/notifications/{id}/send      → send


İş Akışı

  1. Oluştur – Yönetici formu doldurur, bildirim taslak olarak kaydedilir.
  2. Düzenle – Taslak halindeyken çevirileri, filtreleri ve zamanlamayı değiştirme.
  3. Gönder – Filtreleri uygula, eşleşen kullanıcıları ekle, durumu ‘gönderildi’ olarak ayarla.
  4. Yeniden Gönder – Daha önce gönderilmiş bildirime yeniden filtre uygula ve yeni eşleşmeleri ekle.
  5. Sil – Tüm kullanıcıları ayır ve bildirimi sil (işlem içerisindedir).

Form isteği her şeyi doğrular:

class AdminNotificationStoreRequest extends FormRequest
    {
        use HasUserFilterRules;

        public function rules(): array
        {
            $rules = [
                'code' => ['required', 'string', Rule::in(array_keys(config('own.notification_types')))],
                'title_translations.en' => 'required|string|max:255',
                'visible_from' => 'nullable|date',
                'visible_until' => 'nullable|date',
            ];

            foreach (config('app.available_languages') as $locale => $name) {
                if ($locale !== 'en') {
                    $rules["title_translations.{$locale}"] = 'nullable|string|max:255';
                }
                $rules["body_translations.{$locale}"] = 'nullable|string|max:10000';
            }

            return array_merge($rules, $this->userFilterRules());
        }
    }

İngilizce başlık zorunludur. Diğerleri opsiyoneldir. Filtre kuralları trait’ten birleştirilmiştir.



Sistem Bildirimleri

Sistem bildirimleri, olaylar gerçekleştiğinde kuyruklu işler tarafından oluşturulur:

class NotifyEmployeeAboutRejection implements ShouldQueue
    {
        public function handle(): void
        {
            $notification = Notification::create([
                'code' => 'invitation_rejected_employee_' . $this->id,
                'notification_type' => 'system',
                'status' => 'sent',
                'title_key' => 'Invitation Rejected',
                'body_key' => 'You declined the invitation to join :company.',
                'params' => ['company' => $this->company->name],
                'visible_from' => now(),
                'visible_until' => now()->addDays(config('own.system_notification_lifetime_days')),
            ]);

            $notification->users()->attach($this->user->id);

            $this->user->notify(new InvitationRejectedEmployeeNotification(...));
        }
    }

Paterni: oluştur → ekle → opsiyonel olarak e-posta gönder. Tekil code tekrarları engeller.


Sistem Bildirim Tetikleyicileri

OlayBildirim
Şirket oluşturulduSahip, uygulama içi + e-posta alır
Şirket silindiSahip, uygulama içi + e-posta alır
Davet gönderildiDavet edilen, e-posta alır (+ kayıtlıysa uygulama içi)
Davet kabul edildiSahip bilgilendirilir
Davet reddedildiHer iki taraf farklı bildirimlerle bilgilendirilir
Çalışan kaldırıldıÇalışan bilgilendirilir
Ödeme başarıyla sonuçlandıSahip bilgilendirilir
Ödeme başarısız olduSahip bilgilendirilir
Yönetici giriş denemesiYönetici, e-posta + Telegram alır

Her bildirim 30 gün sonra otomatik olarak süresi dolacaktır (system_notification_lifetime_days konfigürasyonu). Eski bildirimler, doğal olarak temizleme işler olmadan kaybolur.



Gönderim ve Okunma İstatistikleri

Yönetici paneli, her bildirim için istatistikler sunar:

| Tür    | Başlık                      | Alındı | Okundu       | Durum    |
|---------|----------------------------|----------|--------------|-----------|
| bilgi    | Sistem bakımı Cuma       | 847      | 312 (36.8%)  | Aktif    |
| uyarı   | Yeni fiyatlandırma 1 Mart  | 1,204    | 891 (74.0%)  | Planlandı |
| bilgi    | v2.0'a Hoşgeldiniz         | 2,100    | 2,034 (96.9%)| Süresi Dolmuş   |

Tüm bu bilgiler, pivot tablodan hesaplanır:

// AdminNotificationResource
'total_recipients' => $this->users->count(),
'read_count' => $this->users->whereNotNull('pivot.read_at')->count(),
'read_percentage' => $total > 0 
    ? round(($read / $total) * 100, 1)
    : 0,


Görünürlük Durumu

Zaman damgalarından hesaplanır ve renkli bir rozet olarak gösterilir:

'visibility_status' => match (true) {
        $this->visible_until?->isPast() => 'expired',
        $this->visible_from?->isFuture() => 'scheduled',
        default => 'active',
    },

Taslak bildirimler “Henüz gönderilmedi” yazısı gösterir. can_send bayrağı, gönderim butonunun görünümünü kontrol eder.



Frontend Kullanıcı Deneyimi


Kullanıcı Bildirim Paneli

Bildirimin sayfası, hem yönetici hem de sistem bildirimlerini birleşik bir listede gösterir:

  • Başlık: “Bildirimler (3 okunmamış)” + “Hepsini oku” butonu
  • Liste: Sayfalandırılmış (25 sayfa başına), her öğe genişletilebilir
  • Öğe: Tür rozet, başlık, zaman damgası, okunma göstergesi
  • Genelleme: İçerik metni görünür, otomatik olarak okunmuş olarak işaretlenir
// NotificationItem.vue
const toggle = () => {
    expanded.value = !expanded.value;
    if (expanded.value && !notification.read_at) {
        markAsRead(notification.id);
    }
};


Gerçek Zamanlı Okunmamış Sayımı

30 saniyede bir anket, üstteki bildirim çanını günceller:

GET /notifications/unread → {unread: true, count: 4}

Küresel bir store’a senkronize edilir. WebSocket’lere ihtiyaç yoktur; anket, sohbet dışı bildirimler için basit ve güvenilir bir yöntemdir.


Toplu İşlemler

“Hepsini oku” doğrudan DB güncellemesi kullanır:

$count = $request->user()->notifications()
        ->wherePivot('read_at', null)
        ->update(['notification_user.read_at' => now()]);

Bildirim sayısından bağımsız tek bir sorgu.


Kullanıcı API Uç Noktaları

GET  /notifications              → sayfalı liste
GET  /notifications/unread       → okunmamış sayım
GET  /notifications/unread-recent → son 5 okunmamış (açılır menü için)
GET  /notifications/recent       → en son 5 tanesi (widget için)
POST /notifications/{id}/read    → tekil işaretle
POST /notifications/mark-all-read → hepsini oku


Yönetici Oluşturma Formu

Üç bölümden oluşur:

  1. Ana bilgi: bildirim türü (bilgi/uyarı), visible_from, visible_until
  2. Çeviriler: dil akordeonu, otomatik çeviri butonu
  3. Alıcılar: canlı kullanıcı sayası ile filtre formu

Form, gönderilmiş bildirimler için devre dışıdır; sadece yeniden gönderim eylemi açıktır.



Test Süreçleri

Bildirmler, Pest ile uçtan uca test edilir:


CRUD

test('admin can create a draft notification', function() {
        actingAs($admin)
            ->post(route('admin.notifications.store'), [
                'code' => 'info',
                'title_translations' => ['en' => 'Test notification'],
                'body_translations' => ['en' => 'Test body'],
                // ... filtre varsayılanları
            ])
            ->assertRedirect(route('admin.notifications.index'));

        expect(Notification::first()->status)->toBe('draft');
    });


Segmentasyon

test('only matching users receive notification', function() {
        User::factory()->create(['country' => 'DE']);
        User::factory()->create(['country' => 'DE']);
        User::factory()->create(['country' => 'US']);

        $notification = Notification::factory()->create([
            'recipient_filters' => ['country' => 'DE'],
        ]);

        actingAs($admin)->post(route('admin.notifications.send', $notification));

        expect($notification->users)->toHaveCount(2);
    });


Okunma İzleme

test('expanding notification marks it as read', function() {
        actingAs($user)
            ->post(route('notifications.read', $notification));

        expect($user->notifications()->first()->pivot->read_at)->not->toBeNull();
    });


Sistem Bildirimleri

test('rejection job creates notification and sends email', function() {
        NotifyEmployeeAboutRejection::dispatch($user, $company);

        expect(Notification::system()->count())->toBe(1);
        Mail::assertSent(InvitationRejectedEmployeeNotification::class);
    });

Gerçek istekler. Gerçek veritabanı. Bildirim oluşturmayı taklit etmeden.



Tasarım Kararları


Neden İki Tablo Yerine Tek Tabloda?

Yönetici ve sistem bildirimleri farklı oluşturma akışlarına sahip olmasına rağmen, tüketim açısından aynı işleyişe sahiptir. Kullanıcı, bildirim listesine baktığında kaynağı önemsememektedir. Tek bir tablo, tek bir sorgu, tek bir kaynak, tek bir komponent anlamına gelir. notification_type enum + nullable kolonlar, bu kullanım durumu için daha kısa bir çözüm sunar.


Neden JSON Çevirileri Kullanıldı?

Yönetici bildirimleri bir kez yazılır. Taslak, çevir, gönder, tamam. Gönderimden sonra çevirileri değiştirmek gerekmez. Düz bir JSON kolon, oluşturma sonrasında değiştirilmeyen iş için, polymorphic bir çeviri tablosunun karmaşıklığından kaçınır.


Neden Anketleme Yerine WebSocket?

Bildirmler, sohbet mesajları değildir. 30 saniyelik bir gecikme kabul edilebilir. Anket, uygulanması, dağıtımı ve hata ayıklaması daha basit olan bir yöntemdir. Sunucuda WebSocket olmadan çalışır; bağlantı yönetimi gerektirmez. Gecikme gereksinimleri değişirse, WebSocket’lere geçiş yapmak kolaydır, API uç noktaları aynı kalır.


Neden Genişletince Otomatik Okundu Olarak İşaretlenir?

Kullanıcıları ayrı bir “okundu” butonuna tıklamaya zorlamak, ek bir zorluk çıkarır. Eğer bir bildirimi okuma amacıyla genişlettiyseniz, onu okudunuz demektir. Zaman damgası, ne zaman okuduğunuzu kaydederek analitik için değerlidir.


Neden Yöneticiler Alıcılardan Hariç Tutulur?

Bildirimi oluşturan ve gönderen yöneticinin, kendi yayınlarını almasına gerek yoktur. config('own.admin_email') ayarını hariç tutmak, “yaratıcıyı geç” bayrağı eklemeye gerek kalmadan kendini bildirimden çıkarmış olur.



Sonraki Adımlar

Bu modül, bir üretim CRM/ERP’den çıkarılan açık kaynak bir SaaS çerçevesi olan LaraFoundry’nin bir parçasıdır.

GitHub: github.com/dmitryisaenko/larafoundry
Önceki modüller: Kayıt, Kimlik Doğrulama, Çoklu Kiracı, Günlükleme, Çok Dilli, Navigasyon, Vue Ön Yüz, Traitler & Middleware’ler, Yönetici Kullanıcılar, Yönetici Şirketler
Bir sonraki modül derinlemesine incelemesi için takip edin.
LaraFoundry: larafoundry.com

Kaynak: Orijinal Makale

Contents
  • İçindekiler
  • Çift Mimari
  • Veritabanı Şeması
    • Bildirimler Tablosu
    • Bildirim-Kullanıcı Pivot
    • Model
  • Çok Dilli İçerik
    • Yönetici Panelinde Otomatik Çeviri
  • Alıcı Segmentasyonu
    • Filtre Uygulaması
  • Yönetici CRUD ve İş Akışı
    • Yollar
    • İş Akışı
  • Sistem Bildirimleri
    • Sistem Bildirim Tetikleyicileri
  • Gönderim ve Okunma İstatistikleri
    • Görünürlük Durumu
  • Frontend Kullanıcı Deneyimi
    • Kullanıcı Bildirim Paneli
    • Gerçek Zamanlı Okunmamış Sayımı
    • Toplu İşlemler
    • Kullanıcı API Uç Noktaları
    • Yönetici Oluşturma Formu
  • Test Süreçleri
    • CRUD
    • Segmentasyon
    • Okunma İzleme
    • Sistem Bildirimleri
  • Tasarım Kararları
    • Neden İki Tablo Yerine Tek Tabloda?
    • Neden JSON Çevirileri Kullanıldı?
    • Neden Anketleme Yerine WebSocket?
    • Neden Genişletince Otomatik Okundu Olarak İşaretlenir?
    • Neden Yöneticiler Alıcılardan Hariç Tutulur?
  • Sonraki Adımlar
Özel Laravel E-ticaret Yapısını Neden Shopify Üzerine Tercih Ettik (Ve Bu Bizim İçin Gerçekten Ne Kadara Mal Oldu)
Laravel İzin Sertleştirme Scripti – DEV Community
2026’da Laravel Hâlâ Geçerli Bir Backend Seçeneği mi?
Obsidian Admin Tanıtımı: Vue 3 + Laravel Yönetim Çerçevesi
Hervey Bay’de Araç Yıkama Hizmetleri | Profesyonel ve Uygun Fiyatlı Araç Temizliği
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Acil: Yanlış OpenAI Gizlilik Filtre Repo’su 244K İndirme Aldı!
Sonraki Makale Acil: TrickMo Android Banker, Gizli İletişim için TON Blockchain’i Benimsedi

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Laufey’in Savaş Taktiği Eski Yunan Üçlemesinden İlham Aldı
Oyun
Şimşek, coaxial kablodan apartmana girip PC’yi patlattı
Donanım
Outlook’un yıllardır güvenlik açığı, Fedora ve Dovecot güncellemesiyle ortaya çıktı
Donanım
Yaz Geliştirici Festivali 2026: Tüm Yenilikler Ortaya Çıkıyor
Oyun
Madonna’nın Grindr’daki Cesur ve Heyecan Verici Ticareti
Genel
Meta’nın AI Sunucuları İçin Tüm ABD’ye Çadırlar Kurması
Donanım
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?