Laravel Türkiye Discord Kanalı Forumda kod paylaşılırken dikkat edilmesi gerekenler!Birlikte proje geliştirmek ister misiniz?
  • YardımLaravel
  • İlişkili Tablodaki Verinin Silinmesini Observer ile Yakalamak

Basit bir eticaret sisteminde products ve images tablolarım var. Birbirlerine hasMany ilişkisi ile bağlılar.
Ürün silindiğinde resim de silinecek. Görselin veritabanındaki kaydı silindiğinde dosyayı da silmek için observer oluşturdum ama observer tetiklenmiyor.

Selamlar, Observer oluşturduktan sonra AppServiceProvider içerisindeki (ya da register edilmiş herhangi bir ServiceProvider) boot metodu içerisinde register ettiniz mi? Eğer etmediyseniz aşağıdaki gibi register edebilirsiniz;

Image::observe(ImageObserver::class);

Ya da ayrı bir Observer oluşturmak yerine model içerisinde bu işlemi gerçekleştirebilirsiniz;

    protected static function boot()
    {
        parent::boot();

        self::deleted(function (self $image) {
            Storage::delete($image->path);
        });
    }

Laravel dökümantasyonundaki gibi modelde php arttibute kullanımıyla kullandım. Bunu da deneyeceğim

Denedim bir değişim yok. Observer benim kullanımımda doğru zaten. Direkt olarak Image sınıfını kullanarak bir görsel sildiğimde observer tetikleniyor. Ama bir ürünü silip relation ile görsel silindiğinde observer tetiklenmiyor. Şuan sıkıntı bu.

    • mgsmus

      Seviye 1382

    aybarsalvarci Observer bir Eloquent özelliği. Düz SQL ile silme yaparsanız tetiklenmez.

    Bu örnekte observer ve model eventları tetiklenir:

    $products = Product::whereIn('id', $ids)->get();
    
    foreach($products as $product) {
        // deleting ve deleted eventları tetiklenir, dolayısıyla observer da çalışır.
        // çünkü model ile işlem yapılıyor:
        $product->delete();
    }

    Aşağıdakinde ise model eventları tetiklenmez çünkü SQL ile toplu silme yapılıyor, model kullanılmıyor ve dolayısıyla Eloquent özellikleri devreye girmiyor:

    $products = Product::whereIn('id', $ids)
        ->delete();

    Ayrıca images tablosunda product_id için ON DELETE CASCADE kullandıysanız (cascadeOnDelete() yöntemi ile) ilişkili kayıtlar veri tabanı düzeyinde silineceği için resimler silinmez.

    @mgsmus Anladım teşekkür ederim. Görselleri teker teker silmem gerekiyor bu durumda.

      • mgsmus

        Seviye 1382

      aybarsalvarci Evet. Ürünler silinirken her bir ürünün görselleri de (eğer bir model ise) aynı şekilde tek tek silinmeli.

      ProductObserver:

      public function deleted(Product $product): void
      {
          foreach($product->images as $image) {
              // $image karşılık gelen modelde de deleting ve deleted tetiklenir:
              $image->delete();
          }
      }

      Elbette bu tamamen her iki modelde de event/observer ile yapmak isterseniz.

      Şöyle de yapılabilir:

      $product = Product::findOrFail($id);
      
      $images = $product->images()
          ->pluck('path', 'id')
          ->toArray();
      
      DB::transaction(function() {
          if($images) {
              $product->images()
                  ->whereIn('id', array_keys($images))
                  ->delete();
          }
          
          $product->delete();
      });
      
      if($images) {
          Storage::delete(array_values($images));
      }

      Anladım. Detaylı anlatım için teşekkür ederim.