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: GDPR Silme, users Tablosundan SILME Değildir
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 » GDPR Silme, users Tablosundan SILME Değildir

Yazılım

GDPR Silme, users Tablosundan SILME Değildir

teknomers
Son güncelleme: 22 Nisan 2026 11:45
teknomers
Paylaş
Paylaş

Okul platformumuzda bir ebeveyn silme talebi gönderdiğinde, bir yönetici bunu onaylar ve sunucumuzda anonymiseContact($contactId) fonksiyonu çalıştırılır. Bu fonksiyonun doğru bir şekilde çalışması beş deneme gerektirdi.

Bu yazı, GDPR’nın 17. Maddesi (silme hakkı) uygulamasının neden bu kadar zor olduğunu, nihayetinde karara vardığımız karar ağacını ve yol boyunca karşılaştığımız spesifik hata modlarını anlatmaktadır. Kod Laravel ve Postgres ile yazılmıştır ancak kararlar evrenseldir.


Naif Uygulama

“Bir kullanıcının kişisel verilerini sil” için ilk akla gelen taslak, tek bir SQL ifadesidir:

RosterContact::where('id', $contactId)->delete();
        

Bu yaklaşımın başarısız olmasının üç ayrı sebebi vardır. Her biri farklı bir tasarım kararına işaret eder.


Problem 1: NULL Olmayan Yabancı Anahtar Tuzağı

Öğrenci tablosunun aşağıdaki sütunu var:

$table->unsignedBigInteger('roster_contact_id'); // NULL OLMAYAN
        

Student::where('roster_contact_id', $contactId)->update(['roster_contact_id' => null]) çağrısı, aşağıdaki hatayı üretir:

SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column
        "roster_contact_id" of relation "students" violates not-null constraint
        

İletişim bilgilerini referans alan her tablo aynı sorunu taşır: issues, leave_requests, consent_responses, meeting_bookings, students. Naif bir cascade silme işlemi bunların hepsini yok edecektir ki bu yanlıştır, çünkü okul bu kayıtların mülkiyetine sahiptir ve bunları saklamakla yasal olarak yükümlüdür (devamsızlık geçmişi, şikayet kayıtları, onay izleme).

Çözüm, roster_contact_id‘yi her akıştaki tabloda nullable yapmak:

Schema::table('students', function (Blueprint $table) {
            $table->unsignedBigInteger()->nullable()->change();
        });
        // ... aynı şekilde leave_requests, consent_responses, meeting_bookings için de
        

Sadece böylece bağlantıyı null yapabiliriz ve satırı koruyabiliriz.


Problem 2: Dört Yönlü Karar

FK’ler nullable olduğunda, kullanıcının dokunduğu her tablo için kasıtlı bir karar verilmesi gerekir. Dört olası sonuç vardır, iki değil:

SonuçNe zaman kullanılmalıÖrnek
Hard deleteVeri silindikten sonra hiçbir değeri yok ve sadece kullanıcıya aitErişim kodları, push bildirim jetonları
Anonymise (satırı tut, PII’yi temizle)Verinin operasyonel veya denetim değeri var ancak kullanıcıyı tanımlamamalıdır.Kişi kaydı, kullanıcı tarafından yazılan mesajlar
Detach (FK’yi null yap, satırı tut)Veri başka bir tarafa aittir (okul, platform) ama kullanıcıyla bağlantılıdırIssue’lar, leave requests, consent responses
Retain (dokunulmaz)Veri doğrudan tanımlayıcı değildiCSAT puanları, aggregate metrikler

Platformumuz için karar matrisimiz aşağıdaki gibidir:

Access codes           → hard delete
Push tokens, device ID → null out (PII)
Contact record         → anonymise (name → "Deleted Contact", PII nulled)
Issue messages         → anonymise (null author_id, replace meta.actor_name)
Activity log entries   → anonymise (scrub contact_name in JSON payload)
Issues, leaves,
consents, bookings     → detach (null roster_contact_id, keep row)
Students               → detach (school owns the student record)
CSAT responses         → retain (never carried direct PII)
        

Bu matris, kullanıcılarınız için yayımlamaya değerdir. 15. Madde (erişim hakkı) ve 30. Madde (işlem kayıtları) çerçevesinde, bir veri sahibi silme işlemi yapıldığında ne olduğunu sorma hakkına sahiptir. “Verilerinizi siliyoruz” gibi belirsiz bir cevap yeterli değildir.


Uygulama

İşte anonymiseContact() fonksiyonunun basitleştirilmiş hali. İşlem sırasına ve işlem sınırına dikkat edin:

private function anonymiseContact(int $contactId): void
{
    $tenantId = tenant();
    $contact  = RosterContact::where(, $contactId)->first();
    if (! $contact) {
        return;
    }

    $actor  = auth()->user(); // onaylayan admin
    $school = School::where(, $tenantId)->first();

    // 1. Satırları silmeden önce ek dosya referanslarını toplayın
    $attachmentFiles = IssueAttachment::whereHas(,
        fn ($q) => $q->where(, $contactId)
    )->get([,])->all();

    // 2. Açık sorunları otomatik olarak kapatın + atamaları kaldırın (bakım – aşağıya bakın)
    $this->autoCloseOpenIssues($contactId, $actor);

    // 3. Gelecek toplantı rezervasyonlarını iptal edin
    $this->cancelFutureBookings($contactId, $actor);

    // 4. Temel anonimleştirme, bir işlem içinde
    DB::transaction(function () use ($tenantId, $contactId, $contact) {
        // Hard delete erişim kodları (silme sonrası hiçbir değeri yok)
        AccessCode::where(, $contactId)->delete();

        // Bu ileti tarafından yazılan mesajları anonimleştir
        IssueMessage::where(, RosterContact::class)
            ->where(, $contactId)
            ->each(function(IssueMessage $m) {
                $meta = $m->meta ??[];
                if (isset($meta[])) {
                    $meta[] =;
                }
                $m->update([
                    => null,
                    => null,
                    => $meta,
                ]);
            });

        // Faaliyet kaydı paylaşımlarında iletişim adını temizle
        IssueActivity::whereIn(, Issue::where(
            , $contactId
        )->pluck())->each(function(IssueActivity $a) {
            $data = $a->data ??[];
            if (isset($data[])) {
                $data[] =;
            }
            $a->update([=> $data]);
        });

        // Ayrışma — aşağıdaki kayıtlardaki FK'yi null yapın
        Issue::where(, $contactId)
            ->update([=> null]);
        LeaveRequest::where(, $contactId)
            ->update([=> null]);
        ConsentResponse::where(, $contactId)
            ->update([=> null]);
        MeetingBooking::where(, $contactId)
            ->update([=> null]);
        Student::where(, $contactId)
            ->update([=> null]);

        // Nihayetinde, iletişimi kendisini anonimleştir
        $contact->update([
            => ,
            => null,
            => null,
            => null,
            => null,
            => null,
            => null,
            => now(),
            => ,
        ]);
    });

    // 5. DB işlemi gerçekleştikten sonra ek dosyaları diskten silin
    foreach ($attachmentFiles as $f)
        {
            try {
                Storage::disk($f[])->delete($f[]);
            } catch (\Throwable $e) {
                // Yetim dosya, hayalet DB kaydından daha güvenli. Loglayın ve devam edin.
                Log::warning(, [...]);
            }
        }
    }

Bu kodda gözle görülmeyen dört karar bulunmaktadır.


Dosya temizleme işlem dışında gerçekleşir

Diskten dosya silmek işlemsel değildir. Eğer DB işlemi başarılı olur ve Storage::delete() çağrısı başarısız olursa, yetim bir dosya oluşur — bu can sıkıcı ama kurtarılabilir. Eğer DB hala işlem aşamasındaysa ve dosya silme işlemi başarılı olur ancak DB geri alınırsa, mevcut olmayan bir dosyayı işaret eden hayalet bir DB satırı oluşur — kullanıcıya görünür hatalar yaratır ve temizlenmesi daha zor olur.

İlk hata modunu tercih ediyoruz. Yetim dosyalar dışarıdan temizlenebilir; hayalet satırlar UI’yi kırar.


Ek dosya yolları işlemden önce toplanır

İşlem içinde issue_attachments’de DELETE işlemini gerçekleştiririz. Bu, sorgulamamız gereken kayıtları siler. Yolları işlemden önce toplamak, veritabanı kısmı başarılı olduğunda hâlâ listeye sahip olmamızı sağlar.


Otomatik bakım anonimleştirmeden ayrıdır

Bir iletişim silindiğinde, açık sorunların kapanması, gelecek toplantı rezervasyonlarının iptal edilmesi ve personel atamalarının kaldırılması gerekir. Bunu anonimleştirme işleminden önce yapıyoruz ve bunun yanına onaylayan adminin adını yazıyoruz, böylece her otomatik kapatma, net bir aktör ve close_reason: contact_erased bayrağı ile kendi aktivite kayıt girişini üretir.

Bunu anonimleştirmenin içinde yaparsak, bu kapanmaların aktörü null ya da "Deleted Contact" olarak düşer ki bu da “Bir soruna bir şey oldu ama bunu yapan kimse yok.” anlamına gelir. Bu, denetim kanıtı için istemediğiniz bir durumdur.


Tekrar deneme semantiği önemlidir

İlk anonymiseContact() dağıtımımız, daha önce açıkladığımız NULL OLAMAYAN FK nedeniyle çökmüştü. DB işlemi temiz bir şekilde geri alındı, ancak DataPrivacyRequest satırı failed olarak işaretlendi ve hata mesajı saklandı.

Admin UI’ye iki bayrak ekledik:

  • Başarısız talepler “Retry & execute” butonu gösterirken “Approve & execute” butonu göstermez.
  • Onay işlemi, hem pending hem de failed durumlarını kabul eder.

Şemayı düzeltmek için (FK’leri nullable yaparak), admin “Retry” butonuna tıkladı ve talep başarıyla tamamlandı. Bu yeniden deneme seçeneği olmadan her düzeltme, manuel DB düzenlemeleri ya da ebeveynden yeni bir talep göndermesini istemek gerektiriyordu – bu, bir işlemin tam olarak hissettirilmesi gereken bir durumda oldukça kötü bir kullanıcı deneyimidir.


Hangi veriler sonsuza dek kalır

GDPR silme özelliği açısından çelişkili bir konudur: Silme işleminin kaydı kendisi saklanmalıdır.

Üç tür kaydı sonsuza kadar saklarız:

  1. data_privacy_requests satırı – talep anında iletişim bilgilerinin saklandığı bu, uyum kanıtıdır.
  2. Bir privacy_erasure aktivite kaydı – neyin silindiğinin özeti (sorunlar kapatıldı, rezervasyonlar iptal edildi) onaylayan admin adına atfedilir.
  3. Silinmeden önceki yedek anlık görüntüler – bu olaylar tanımlayıcı PII içeren ancak kendiliğinden düzgün döngülerle 90 günde bir yaşlanan kayıtlardır. Bu, DPA’nızda açıklanmalıdır.

Makale 33 kapsamında denetim otoriteleri, taleplerin ve bunların nasıl ele alındığının kaydını görmeyi bekler. Bir kullanıcı “Beni ne zaman sildiniz ve kim onayladı?” diye soruyorsa, bu geçerli bir sorudur ve “bu tarihte, bu admin tarafından, işte kanıt” şeklinde geçerli bir cevabı vardır.


Karar Ağacının Yayınlanması

Yukarıdaki matrisin her sözcüğü artık kullanıcıya açık Gizlilik Politikasında, “Silme talebiniz onaylandığında ne olur” başlığı altında bulunmaktadır. Yayınlamanın üç amacı vardır:

  • Kullanıcılar, haklarını kullanırken gerçek bir beklentiye sahip olurlar.
  • Politikayı okuyan denetçiler mühendisliğin gerçek bir dayanağı olduğunu görürler.
  • Bu, içsel netlik sağladığı için — yayımlanacakları şeyleri karar vermediğiniz sürece açıklayamazsınız.

17. Maddeyi uygulamaya başlamadan önce, kodla başlamayın. Önce karar matrisini yazın, bunu kullanıcı dostu bir dilde yazın ve kod ardından gelsin. Beş denememiz büyük ölçüde doğru sırayı takip edememekten kaynaklandı.


Özet

GDPR silme işlemi, veri türüne göre dört sonuç üreten mühendislik kararıdır, ikili değil. Uygulama, nullable yabancı anahtarlar, kasıtlı bir işlem sırası, DB ve depolamanın düzgün bir şekilde ayrılmasını gerektirir, operasyonel yan etkiler için ayrı bir bakım, hata kurtarma için yeniden deneme imkânı ve silme işleminin kendisine dair sonsuz bir denetim izi gerektirir. Naif bir DELETE FROM users yasal, operasyonel ve denetim sonuçları bakımından yanlış sonuca ulaşır.

Önce matrisi yazın. Sonra kodu yazın.

Kaynak: Orijinal Makale

Contents
  • Naif Uygulama
  • Problem 1: NULL Olmayan Yabancı Anahtar Tuzağı
  • Problem 2: Dört Yönlü Karar
  • Uygulama
    • Dosya temizleme işlem dışında gerçekleşir
    • Ek dosya yolları işlemden önce toplanır
    • Otomatik bakım anonimleştirmeden ayrıdır
    • Tekrar deneme semantiği önemlidir
  • Hangi veriler sonsuza dek kalır
  • Karar Ağacının Yayınlanması
  • Özet
Laravel Core CRUD Paketi: Laravel Core CRUD ile Temiz Mimari
Gerçek Projelerde Laravel Performansı Hakkında Zor Yoldan Öğrendiğimiz 6 Şey
Daha Büyük Sunucular Satın Almayı Bırakın: Laravel Yük Dengeleyici Mimarisi Kurma
AI Kapsamlarında N+1 Problemi: Laravel + OpenAI’nin Ölçeklendirilmesi
Laravel’i VPS’ye GitHub Actions ile Dağıtma (Kesintisiz CI/CD)
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Acil: Microsoft, kritik ASP.NET açığı için güncellemeler yayımladı
Sonraki Makale Valvoline Kuponları: Nisan 2026’daki İndirim Fırsatlarını Kaçırmayın!

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Final Fantasy 7 Yeniliklerinde Sephiroth’a Beklenmedik Dokunuş
Oyun
Sriram Krishnan, Beyaz Saray’daki AI danışmanlığından ayrılıyor
Yapay Zeka
En Sevimli Oyunlar: Wholesome Direct 2026’dan Seçtiklerimiz
Liste
Oyun ses çubuğu 5 metre uzaktan ele geçirilebiliyor, risk yok mu?
Donanım
Teknolojinin Gizliliği Kaybettiği Günlere Özlem Duyuluyor
Liste
Trump yönetimi OpenAI’de hisse alabilir mi?
Yapay Zeka
//

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?