লারাভেলে সম্পর্কিত সারিগুলি স্বয়ংক্রিয়ভাবে মোছা হচ্ছে (স্বতন্ত্র ওআরএম)


158

আমি যখন এই বাক্য গঠনটি ব্যবহার করে একটি সারি মুছব:

$user->delete();

বাছাইয়ের কলব্যাক সংযুক্ত করার কোনও উপায় আছে, যাতে এটি উদাহরণস্বরূপ স্বয়ংক্রিয়ভাবে এটি করতে পারে:

$this->photo()->delete();

প্রায়শই মডেল-শ্রেণীর ভিতরে।

উত্তর:


205

আমি বিশ্বাস করি এটি শ্রুতচিত্ত ইভেন্টগুলির জন্য একটি উপযুক্ত ব্যবহারের ক্ষেত্রে ( http://laravel.com/docs/eloquent#model-events )। আপনি ক্লিনআপটি করতে "মোছা" ইভেন্টটি ব্যবহার করতে পারেন:

class User extends Eloquent
{
    public function photos()
    {
        return $this->has_many('Photo');
    }

    // this is a recommended way to declare event handlers
    public static function boot() {
        parent::boot();

        static::deleting(function($user) { // before delete() method call this
             $user->photos()->delete();
             // do the rest of the cleanup...
        });
    }
}

রেফারেন্সিয়াল অখণ্ডতা নিশ্চিত করার জন্য আপনার সম্ভবত পুরো জিনিসটি কোনও লেনদেনের মধ্যে রাখা উচিত ..


7
দ্রষ্টব্য: আমি এই কাজটি না করা পর্যন্ত আমি কিছু সময় ব্যয় করেছি। আমাকে কোয়েরিতে যুক্ত করার দরকার ছিল first()যাতে আমি মডেল-ইভেন্ট যেমন অ্যাক্সেস করতে পারি যেমন User::where('id', '=', $id)->first()->delete(); উত্স
মিশেল আইরেস

6
@ মিশেল আইরেস: হ্যাঁ, আপনাকে কোয়েরি বিল্ডারে নয়, একটি মডেল উদাহরণে মুছে ফেলা () কল করতে হবে। বিল্ডারের নিজস্ব মুছে ফেলা () পদ্ধতি রয়েছে যা মূলত কেবল একটি মোছার স্কিল কোয়েরি চালায়, তাই আমি ধারণা করি এটি orm ইভেন্টগুলি সম্পর্কে কিছুই জানে না ...
ivanhoe

3
এটি নরম-মুছে ফেলার উপায়। আমি বিশ্বাস করি যে নতুন / পছন্দসই লারাভেল উপায় হ'ল এই সমস্তটি অ্যাপসোর্সোপ্রাইভাইডারের বুট () পদ্ধতিতে এইভাবে আটকে রাখা: \ অ্যাপ \ ব্যবহারকারী :: মুছে ফেলা (ফাংশন () u) photos $ u-> ফটো () -> মোছা ( );});
ওয়াটারকেম্যান

4
লারাভেল ৫.৫-এ প্রায় কাজ করেছিলাম, আমাকে একটি যুক্ত করতে হয়েছিল foreach($user->photos as $photo), তারপরে $photo->delete()এটি নিশ্চিত করতে যে কোনও কারণে এটি ঘটছে কেবল তার পরিবর্তে প্রতিটি সন্তানের সমস্ত স্তরে তার শিশুদের সরানো হয়েছে had
জর্জ

9
এটি যদিও এটি আর ক্যাসকেড করে না। উদাহরণস্বরূপ যদি Photosথাকে tagsএবং আপনি Photosমডেলটিতেও একই কাজ করেন (অর্থাত্ deletingপদ্ধতিতে $photo->tags()->delete();) এটি কখনই ট্রিগার হয় না। তবে আমি যদি এটিকে একটি forলুপ তৈরি করি এবং এর মতো কিছু করি for($user->photos as $photo) { $photo->delete(); }তবে tagsতাও মুছে ফেলা হবে! সবে FYI
সুপারসান

199

আপনি আপনার মাইগ্রেশনগুলিতে এটি সেট আপ করতে পারেন:

$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

সূত্র: http://laravel.com/docs/5.1/migration#foreign-key-constraints

আপনি "মুছে ফেলতে" এবং "আপডেটে" সীমাবদ্ধতার বৈশিষ্ট্যগুলির জন্য পছন্দসই কর্মটি নির্দিষ্ট করতে পারেন:

$table->foreign('user_id')
      ->references('id')->on('users')
      ->onDelete('cascade');

হ্যাঁ, আমার ধারণা, আমার সেই নির্ভরতাটি স্পষ্ট করা উচিত ছিল।
ক্রিস স্মিটজ

62
তবে যদি আপনি নরম মোছা ব্যবহার করেন না তবে যেহেতু সারিগুলি আসলে মুছে ফেলা হয় না।
কাঁপানো

7
এছাড়াও - এটি ডিবিতে রেকর্ডটি মুছে ফেলবে, তবে এটি আপনার মোছার পদ্ধতিটি চালাবে না, সুতরাং আপনি যদি মুছতে অতিরিক্ত কাজ করছেন (উদাহরণস্বরূপ - ফাইলগুলি মুছুন), এটি চলবে না
amosmos

10
এই পদ্ধতিটি ক্যাসকেড মোছার জন্য ডিবি'র উপর নির্ভর করে, তবে সমস্ত ডিবিই এটি সমর্থন করে না, তাই অতিরিক্ত যত্নের প্রয়োজন। উদাহরণস্বরূপ, মাইআইএসএএম ইঞ্জিন সহ মাইএসকিউএল, বা কোনও ডিফল্ট সেটআপে কোনও নোএসকিউএল ডিবি, এসকিউএলাইট ইত্যাদি করে না problem আপনি যখন পরে কোনও রেকর্ড মুছবেন তখন কোনও ক্যাসকেড ঘটবে না। আমার একবার এই সমস্যা হয়েছিল এবং বিশ্বাস করুন এটি ডিবাগ করা খুব কঠিন।
ivanhoe

1
@kehinde আপনার দেখানো পদ্ধতিটি মুছে ফেলার সম্পর্কগুলিতে মুছে ফেলার ইভেন্টগুলিকে অনুরোধ করে না। আপনার সম্পর্কের উপর পুনরাবৃত্তি হওয়া উচিত এবং স্বতন্ত্রভাবে মুছতে কল করা উচিত।
টম

51

দ্রষ্টব্য : এই উত্তরটি লারাভেল 3 এর জন্য লেখা হয়েছিল । সুতরাং সম্ভবত লারাভেলের সাম্প্রতিক সংস্করণে ভাল কাজ করতে পারে এবং নাও পারে।

ব্যবহারকারীকে প্রকৃতপক্ষে মুছে ফেলার আগে আপনি সম্পর্কিত সমস্ত ফটো মুছতে পারেন।

<?php

class User extends Eloquent
{

    public function photos()
    {
        return $this->has_many('Photo');
    }

    public function delete()
    {
        // delete all related photos 
        $this->photos()->delete();
        // as suggested by Dirk in comment,
        // it's an uglier alternative, but faster
        // Photo::where("user_id", $this->id)->delete()

        // delete the user
        return parent::delete();
    }
}

আশা করি এটা সাহায্য করবে.


1
আপনাকে ব্যবহার করতে হবে: ফরচ ($ এই-> ফটোগুলি $ ফটো হিসাবে) ($ এই-> ফটোগুলির পরিবর্তে $ এই-> ফটোগুলি ()) অন্যথায়, ভাল টিপ!
ব্যারিভদ্ধ

20
এটিকে আরও দক্ষ করতে, একটি ক্যোয়ারী ব্যবহার করুন: ফটো :: যেখানে ("ইউজার_আইডি", $ এটি-> আইডি) -> মুছে দিন (); না হয় nicest উপায়, কিন্তু শুধুমাত্র 1 ক্যোয়ারী, পথ ভাল পারফরম্যান্স যদি একটি ব্যবহারকারী রয়েছে 1.000.000 ফটোর।
শির্ক

5
আসলে আপনি কল করতে পারেন: $ এই-> ফটো () -> মুছুন (); লুপের প্রয়োজন নেই - আইভানহো
আইভানহো

4
@ আইভানহো আমি লক্ষ্য করেছি যে মুছে ফেলা ইভেন্টটি ছবিতে আগুন জ্বলবে না তবে আপনি সংগ্রহটি মুছে ফেললে, তবে, আখিয়ারের পরামর্শ অনুসারে পুনরুক্তি করার ফলে মোছার ঘটনাটি আগুনের কারণ হয়ে যাবে। এটি কি বাগ?
অ্যাডামক্রেল

1
@ আখিয়ার প্রায়, আপনি এটি দিয়ে করতে পারেন $this->photos()->delete()photos()ক্যোয়ারী রচয়িতা বস্তুর ফেরৎ।
সোভেন ভ্যান জোলেন

32

ব্যবহারকারী মডেল সম্পর্কিত:

public function photos()
{
    return $this->hasMany('Photo');
}

রেকর্ড এবং সম্পর্কিত মুছুন:

$user = User::find($id);

// delete related   
$user->photos()->delete();

$user->delete();

4
এটি কাজ করে তবে p ব্যবহারকারীর () -> সম্পর্ক () -> বিচ্ছিন্নতা () ব্যবহার করার ক্ষেত্রে যত্নবান ব্যবহার করুন যদি সেখানে কোনও পাইভট টেবিল জড়িত থাকে (হ্যাজম্যানি / স্বদেশ সম্পর্কিত সম্পর্কগুলির ক্ষেত্রে), না হলে আপনি রেফারেন্সটি মুছে ফেলবেন, সম্পর্কটি নয় ।
জেমস বেইলি

এটি আমার জন্য লারাভেল works এর জন্য কাজ করে @
আরমান এইচ

19

এটি সমাধানের জন্য 3 টি পদ্ধতি রয়েছে:

১. মডেল বুটে সুস্পষ্ট ইভেন্টগুলি ব্যবহার করা (রেফার্স: https://laravel.com/docs/5.7/eloquent#events )

class User extends Eloquent
{
    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
             $user->photos()->delete();
        });
    }
}

২. স্পষ্টত ইভেন্ট পর্যবেক্ষক ব্যবহার করে (রেফার্স: https://laravel.com/docs/5.7/eloquent#observers )

আপনার অ্যাপসোসভারপ্রভাইডারে, পর্যবেক্ষককে এর মতো নিবন্ধ করুন:

public function boot()
{
    User::observe(UserObserver::class);
}

এরপরে, এর মতো একটি পর্যবেক্ষক শ্রেণি যুক্ত করুন:

class UserObserver
{
    public function deleting(User $user)
    {
         $user->photos()->delete();
    }
}

3. ব্যবহার ফরেন কী বাধ্যবাধকতা (সুত্র: https://laravel.com/docs/5.7/migrations#foreign-key-constraints )

$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

1
আমি মনে করি যে 3 টি অপশনটি সবচেয়ে মার্জিত তাই যেহেতু ডাটাবেসে নিজেই সীমাবদ্ধতা তৈরি করছে। আমি এটি পরীক্ষা করি এবং ঠিকঠাক কাজ করি।
গিলবার্ট

14

লারাভেল ৫.২ হিসাবে, ডকুমেন্টেশনে উল্লেখ করা হয়েছে যে এই ধরণের ইভেন্ট হ্যান্ডলারগুলি অ্যাপসোসাইসপ্রোভাইডারটিতে নিবন্ধিত হওয়া উচিত:

<?php
class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::deleting(function ($user) {
            $user->photos()->delete();
        });
    }

আমি এমনকি তাদের আরও ভাল অ্যাপ্লিকেশন কাঠামোর জন্য ক্লোজার পরিবর্তে পৃথক ক্লাসে স্থানান্তরিত মনে করি।


1
লারাভেল ৫.৩ এগুলিকে পর্যবেক্ষক হিসাবে পৃথক শ্রেণিতে স্থাপনের পরামর্শ দিচ্ছে - এটি কেবল 5.3 তে নথিভুক্ত করা হলেও, Eloquent::observe()পদ্ধতিটি 5.2 তেও পাওয়া যায় এবং অ্যাপসোসভারপ্রাইভাইডার থেকে ব্যবহার করা যেতে পারে।
লেইথ

3
আপনার কাছ থেকে যদি আপনার 'অনেক' সম্পর্ক থাকে তবে আপনারও photos()যত্নবান হওয়া দরকার - এই প্রক্রিয়াটি নাতি-নাতনিদের মুছবে না কারণ আপনি মডেলগুলি লোড করছেন না। মুছে ফেলা-সম্পর্কিত ইভেন্টগুলিকে আগুন জ্বালানোর জন্য আপনাকে মডেল হিসাবে লুপ ওভার করতে হবে photos(নোট, নয় photos()) এবং delete()পদ্ধতিটি ফায়ার করতে হবে।
লেথ

1
@ লিথ অবলোকন পদ্ধতিটিও 5.1 তে পাওয়া যায়।
টাইলার রিড

2

আপনি যদি এর deleteজন্য পদ্ধতিটি ওভাররাইড করেন তবে ভাল । এই পদ্ধতিতে, আপনি deleteনিজেই পদ্ধতিতে ডিবি লেনদেনকে অন্তর্ভুক্ত করতে পারেন । আপনি যদি ইভেন্টের উপায়টি ব্যবহার করেন, deleteপ্রতিবার আপনি যখন কল করবেন তখন আপনাকে নিজের পদ্ধতিটির কলটি ডিবি লেনদেনের সাথে আবরণ করতে হবে।

আপনার Userমডেল।

public function delete()
{
    \DB::beginTransaction();

     $this
        ->photo()
        ->delete()
    ;

    $result = parent::delete();

    \DB::commit();

    return $result;
}

1

আমার ক্ষেত্রে এটি বেশ সহজ ছিল কারণ আমার ডাটাবেস টেবিলগুলি হ'ল মুছে ফেলা ক্যাসকেড সহ বিদেশী কী সহ InnoDB।

সুতরাং এই ক্ষেত্রে যদি আপনার ফটোগুলির টেবিলটিতে ব্যবহারকারীর জন্য কোনও বিদেশী কী রেফারেন্স থাকে তবে আপনাকে যা করতে হবে তা হল হোটেলটি মুছতে হবে এবং ডেটা বেস দ্বারা ক্লিনআপ করা হবে, ডেটা বেস থেকে সমস্ত ফটো রেকর্ড মুছে ফেলা হবে বেস।


অন্যান্য উত্তরে যেমন উল্লেখ করা হয়েছে, ডাটাবেস স্তরটিতে ক্যাসকেডিং মুছে ফেলা সফট ডিলিটগুলি ব্যবহার করার সময় কাজ করবে না। ক্রেতা হুঁশিয়ার. :)
বেন জনসন 14

1

আমি বস্তুটি নিজেই মোছার আগে সবকিছুকে বিচ্ছিন্ন করে সংগ্রহের মাধ্যমে পুনরাবৃত্তি করব।

এখানে একটি উদাহরণ:

try {
        $user = user::findOrFail($id);
        if ($user->has('photos')) {
            foreach ($user->photos as $photo) {

                $user->photos()->detach($photo);
            }
        }
        $user->delete();
        return 'User deleted';
    } catch (Exception $e) {
        dd($e);
    }

আমি জানি এটি স্বয়ংক্রিয় নয় তবে এটি খুব সাধারণ।

আরেকটি সহজ পদ্ধতির মডেলটি একটি পদ্ধতি সহ সরবরাহ করা হবে। এটার মত:

public function detach(){
       try {

            if ($this->has('photos')) {
                foreach ($this->photos as $photo) {

                    $this->photos()->detach($photo);
                }
            }

        } catch (Exception $e) {
            dd($e);
        }
}

তারপরে আপনি যেখানে প্রয়োজন সেখানে কেবল এটিকে কল করতে পারেন:

$user->detach();
$user->delete();

0

অথবা আপনি যদি এটি চান তবে এটি করতে পারেন, কেবল অন্য একটি বিকল্প:

try {
    DB::connection()->pdo->beginTransaction();

    $photos = Photo::where('user_id', '=', $user_id)->delete(); // Delete all photos for user
    $user = Geofence::where('id', '=', $user_id)->delete(); // Delete users

    DB::connection()->pdo->commit();

}catch(\Laravel\Database\Exception $e) {
    DB::connection()->pdo->rollBack();
    Log::exception($e);
}

দ্রষ্টব্য আপনি যদি ডিফল্ট লারাভেল ডিবি সংযোগটি ব্যবহার না করেন তবে আপনাকে নিম্নলিখিতগুলি করতে হবে:

DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();

0

নির্বাচিত উত্তরের বিশদটি জানাতে, যদি আপনার সম্পর্কের মধ্যেও এমন সম্পর্ক থাকে যা অবশ্যই মুছে ফেলা উচিত, আপনাকে প্রথমে সমস্ত শিশুর সম্পর্কের রেকর্ডগুলি পুনরুদ্ধার করতে হবে, তারপরে delete()পদ্ধতিটি কল করুন যাতে তাদের মোছার ইভেন্টগুলিও যথাযথভাবে বরখাস্ত করা হয়।

আপনি উচ্চতর অর্ডার বার্তাগুলি দিয়ে সহজেই এটি করতে পারেন ।

class User extends Eloquent
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
             $user->photos()->get()->each->delete();
        });
    }
}

আপনি কেবল সম্পর্কের আইডি কলামটি জিজ্ঞাসা করে কর্মক্ষমতা উন্নত করতে পারেন:

class User extends Eloquent
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
             $user->photos()->get(['id'])->each->delete();
        });
    }
}

-1

হ্যাঁ, তবে @ সুপারসন একটি মন্তব্যে উপরের মত বলেছেন, আপনি যদি কোয়েরি বিল্ডারে () মুছে ফেলেন তবে মডেল ইভেন্টটি বরখাস্ত করা হবে না, কারণ আমরা নিজেই মডেলটি লোড করছি না, তারপরে সেই মডেলটিতে মুছে ফেলা কল করুন।

ইভেন্টগুলি কেবল তখনই বরখাস্ত করা হয় যদি আমরা কোনও মডেল দৃষ্টান্তে মুছুন ফাংশনটি ব্যবহার করি।

সুতরাং, এই মৌমাছিটি বলেছেন:

if user->hasMany(post)
and if post->hasMany(tags)

ব্যবহারকারী মুছে ফেলার সময় পোস্ট ট্যাগ মুছতে, আমাদের পুনরাবৃত্তি করতে হবে $user->postsএবং কল করতে হবে$post->delete()

foreach($user->posts as $post) { $post->delete(); } -> এটি পোস্টে মোছার ইভেন্টটিকে আগুন ধরিয়ে দেবে

বনাম

$user->posts()->delete()-> এটি পোস্টে মোছার ঘটনাকে আগুন ধরিয়ে দেবে না কারণ আমরা প্রকৃতপক্ষে পোস্ট মডেলটি লোড করি না (আমরা কেবলমাত্র এসকিউএল চালাচ্ছি: DELETE * from posts where user_id = $user->idএবং এভাবে পোস্ট পোস্টটি এমনকি লোড হয় না)


-2

আপনি বিকল্প হিসাবে এই পদ্ধতিটি ব্যবহার করতে পারেন।

কি হবে তা হ'ল আমরা ব্যবহারকারীর টেবিলের সাথে সম্পর্কিত সমস্ত টেবিল নিয়েছি এবং লুপিং ব্যবহার করে সম্পর্কিত ডেটা মুছে ফেলছি

$tables = DB::select("
    SELECT
        TABLE_NAME,
        COLUMN_NAME,
        CONSTRAINT_NAME,
        REFERENCED_TABLE_NAME,
        REFERENCED_COLUMN_NAME
    FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
    WHERE REFERENCED_TABLE_NAME = 'users'
");

foreach($tables as $table){
    $table_name =  $table->TABLE_NAME;
    $column_name = $table->COLUMN_NAME;

    DB::delete("delete from $table_name where $column_name = ?", [$id]);
}

আমি মনে করি না যে এই সমস্ত কোয়েরিগুলি প্রয়োজনীয় কারণ আপনি যদি স্পষ্টভাবে নির্দিষ্ট করে থাকেন তবে সুস্পষ্ট orm এটিকে পরিচালনা করতে পারে।
ইস্ট 9:59
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.