Debug School

rakesh kumar
rakesh kumar

Posted on

How to Create a Database Trigger for Automatic Status Updates in laravel

Different way to trigger in database
Database Trigger Method Example
Corn Job Example

Different way to trigger in database

To automatically change the status of a vendor from 1 to 0 if they do not reply, you can implement this functionality using various approaches depending on your application architecture and database technology. Here are five methods you might consider:

  1. Database Trigger You can create a database trigger that automatically updates the status when a certain condition is met, like a time threshold for replies.
CREATE TRIGGER update_vendor_status
AFTER INSERT ON vendor_replies
FOR EACH ROW
BEGIN
    DECLARE reply_time TIMESTAMP;
    SET reply_time = NEW.reply_time; -- Assuming `reply_time` is the timestamp when the reply was received

    IF TIMESTAMPDIFF(MINUTE, reply_time, NOW()) > 30 THEN -- Change 30 to your desired threshold in minutes
        UPDATE vendors SET status = 0 WHERE id = NEW.vendor_id;
    END IF;
END;
Enter fullscreen mode Exit fullscreen mode

2.Scheduled Job (Cron Job)
You can set up a cron job that runs a script at regular intervals to check for vendors who have not replied and update their status.

# In a crontab file
*/30 * * * * /path/to/php /path/to/script/update_vendor_status.php
Enter fullscreen mode Exit fullscreen mode

In update_vendor_status.php:

<?php
// Database connection
$conn = new mysqli("host", "user", "password", "database");

// Update status for vendors who haven't replied in the last 30 minutes
$sql = "UPDATE vendors SET status = 0 WHERE status = 1 AND id NOT IN (SELECT vendor_id FROM vendor_replies WHERE reply_time > NOW() - INTERVAL 30 MINUTE)";
$conn->query($sql);
$conn->close();
?>
Enter fullscreen mode Exit fullscreen mode
  1. Event-Based System Implement an event-based system where the absence of a reply triggers an event to change the status.
// Pseudocode for event listener
function onVendorReply($vendorId) {
    // Record the reply
    recordReply($vendorId);

    // Reset the status to 1
    updateVendorStatus($vendorId, 1);
}

// Periodically check and update status
function checkVendorReplies() {
    $vendors = getVendorsWithNoReply();
    foreach ($vendors as $vendor) {
        updateVendorStatus($vendor['id'], 0);
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Polling Method If your application can afford it, you can implement a polling method that checks for replies at regular intervals and updates the status accordingly.
// In your application loop
while (true) {
    sleep(1800); // Check every 30 minutes

    $sql = "UPDATE vendors SET status = 0 WHERE status = 1 AND id NOT IN (SELECT vendor_id FROM vendor_replies WHERE reply_time > NOW() - INTERVAL 30 MINUTE)";
    $conn->query($sql);
}
Enter fullscreen mode Exit fullscreen mode
  1. Using a WebSocket Connection If you're using WebSockets for real-time communication, you can update the status as soon as a reply is detected.
// WebSocket message handler
socket.on('vendorReply', (vendorId) => {
    // Update vendor status to 1
    updateVendorStatus(vendorId, 1);
});

// Function to check for replies
function checkForReplies() {
    const currentTime = new Date();
    // Check for vendors that have not replied in the desired timeframe
    vendors.forEach(vendor => {
        if (vendor.status === 1 && (currentTime - new Date(vendor.lastReplyTime)) > 1800000) { // 30 minutes
            updateVendorStatus(vendor.id, 0);
        }
    });
}
Enter fullscreen mode Exit fullscreen mode

Database Trigger Method Example

Create the Migration for Vendor Replies
First, create a migration for the vendor_replies table. Run:

php artisan make:migration create_vendor_replies_table
In the generated migration file (located in database/migrations), define the schema for the vendor_replies table:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateVendorRepliesTable extends Migration
{
    public function up()
    {
        Schema::create('vendor_replies', function (Blueprint $table) {
            $table->id();
            $table->foreignId('vendor_id')->constrained('vendors')->onDelete('cascade');
            $table->timestamp('reply_time')->nullable();
            $table->string('message', 255);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('vendor_replies');
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Create the Migration for the Trigger Next, create a migration for the trigger. Run:
php artisan make:migration create_vendor_status_trigger
Enter fullscreen mode Exit fullscreen mode

In this migration file, add the trigger definition:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class CreateVendorStatusTrigger extends Migration
{
    public function up()
    {
        DB::unprepared('
            CREATE TRIGGER update_vendor_status
            AFTER INSERT ON vendor_replies
            FOR EACH ROW
            BEGIN
                DECLARE reply_time TIMESTAMP;
                SET reply_time = NEW.reply_time;

                IF TIMESTAMPDIFF(MINUTE, reply_time, NOW()) > 30 THEN
                    UPDATE vendors SET status = 0 WHERE id = NEW.vendor_id;
                END IF;
            END
        ');
    }

    public function down()
    {
        DB::unprepared('DROP TRIGGER IF EXISTS update_vendor_status');
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Create the VendorReply Model Now, create the VendorReply model:

php artisan make:model VendorReply
In app/Models/VendorReply.php, add the following code:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class VendorReply extends Model
{
    use HasFactory;

    protected $table = 'vendor_replies';

    protected $fillable = [
        'vendor_id',
        'reply_time',
        'message'
    ];

    public function vendor()
    {
        return $this->belongsTo(Vendor::class);
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Create the VendorReply Controller Now, create a controller to handle incoming requests for vendor replies:
php artisan make:controller VendorReplyController
Enter fullscreen mode Exit fullscreen mode

In app/Http/Controllers/VendorReplyController.php, add the following code:

<?php

namespace App\Http\Controllers;

use App\Models\VendorReply;
use Illuminate\Http\Request;

class VendorReplyController extends Controller
{
    public function store(Request $request)
    {
        try {
            // Validate the incoming request data
            $validatedData = $request->validate([
                'vendor_id' => 'required|exists:vendors,id', // Ensure the vendor exists
                'message' => 'required|string|max:255', // Limit message length
            ]);

            // Create a new reply
            $reply = VendorReply::create([
                'vendor_id' => $validatedData['vendor_id'],
                'reply_time' => now(), // Current timestamp
                'message' => $validatedData['message'],
            ]);

            // Return a success response
            return response()->json(['success' => true, 'reply' => $reply]);

        } catch (\Illuminate\Validation\ValidationException $e) {
            // Handle validation errors
            return response()->json(['success' => false, 'errors' => $e->validator->errors()], 422);
        } catch (\Exception $e) {
            // Handle general exceptions
            return response()->json(['success' => false, 'message' => 'An error occurred: ' . $e->getMessage()], 500);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Define Routes You need to define a route for this controller method. Open routes/web.php or routes/api.php (depending on your application structure) and add:
use App\Http\Controllers\VendorReplyController;

Route::post('/vendor-replies', [VendorReplyController::class, 'store']);
Enter fullscreen mode Exit fullscreen mode
  1. Run Migrations Finally, run the migrations to create the tables and the trigger in your database:
php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Summary of the Implementation

Image description

following JSON body:

{
    "vendor_id": 1, // Make sure this ID exists in your vendors table
    "message": "This is a reply from the vendor."
}
Enter fullscreen mode Exit fullscreen mode

Note

  1. Make sure that the vendors table and the relevant IDs exist before testing.
  2. Adjust the time threshold in the trigger according to your business logic requirements

Corn Job Example

Step 1: Create the Database Trigger
First, we need to create a database trigger. This trigger will automatically update the vendor status based on the reply time.

Create Migration for the Trigger
Run the following Artisan command to create a migration for the trigger:

php artisan make:migration create_update_vendor_status_trigger
Enter fullscreen mode Exit fullscreen mode

In the migration file located in database/migrations, add the trigger definition:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class CreateUpdateVendorStatusTrigger extends Migration
{
    public function up()
    {
        DB::unprepared('
            CREATE TRIGGER update_vendor_status
            AFTER INSERT ON vendor_replies
            FOR EACH ROW
            BEGIN
                DECLARE reply_time TIMESTAMP;
                SET reply_time = NEW.reply_time;

                IF TIMESTAMPDIFF(MINUTE, reply_time, NOW()) > 30 THEN
                    UPDATE vendors SET status = 0 WHERE id = NEW.vendor_id;
                END IF;
            END
        ');
    }

    public function down()
    {
        DB::unprepared('DROP TRIGGER IF EXISTS update_vendor_status');
    }
}
Enter fullscreen mode Exit fullscreen mode

Run the migration to create the trigger:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Laravel Console Command
Now, create a console command to manage the vendor statuses, which you can run manually or schedule via cron.

Run the command:

php artisan make:command UpdateVendorStatus
Enter fullscreen mode Exit fullscreen mode

This will create a file in app/Console/Commands/UpdateVendorStatus.php. Open this file and implement the logic to update the vendor statuses:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class UpdateVendorStatus extends Command
{
    // The name and signature of the console command.
    protected $signature = 'vendors:update-status';

    // The console command description.
    protected $description = 'Update vendor status based on replies';

    // Execute the console command.
    public function handle()
    {
        // Define the time threshold (e.g., 30 minutes)
        $thresholdTime = Carbon::now()->subMinutes(30);

        // Update the vendor status
        $updated = DB::table('vendors')
            ->where('status', 1)
            ->whereNotIn('id', function ($query) use ($thresholdTime) {
                $query->select('vendor_id')
                      ->from('vendor_replies')
                      ->where('reply_time', '>', $thresholdTime);
            })
            ->update(['status' => 0]);

        $this->info("Updated $updated vendor statuses to inactive.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Schedule the Command
To run the command at regular intervals (e.g., every 30 minutes), you'll schedule it in the app/Console/Kernel.php file.


protected function schedule(Schedule $schedule)
{
    // Schedule the command to run every 30 minutes
    $schedule->command('vendors:update-status')->everyThirtyMinutes();
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Set Up the Cron Job on Your Server
To ensure the scheduled commands run automatically, you need to set up a cron job on your server.

Open the crontab editor:


crontab -e
Enter fullscreen mode Exit fullscreen mode

Add the following line to run the Laravel scheduler every minute:

* * * * * php /path/to/your/project/artisan schedule:run >> /dev/null 2>&1
Replace /path/to/your/project with the actual path to your Laravel project.
Enter fullscreen mode Exit fullscreen mode

Image description

Complete Code Overview

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class CreateUpdateVendorStatusTrigger extends Migration
{
    public function up()
    {
        DB::unprepared('
            CREATE TRIGGER update_vendor_status
            AFTER INSERT ON vendor_replies
            FOR EACH ROW
            BEGIN
                DECLARE reply_time TIMESTAMP;
                SET reply_time = NEW.reply_time;

                IF TIMESTAMPDIFF(MINUTE, reply_time, NOW()) > 30 THEN
                    UPDATE vendors SET status = 0 WHERE id = NEW.vendor_id;
                END IF;
            END
        ');
    }

    public function down()
    {
        DB::unprepared('DROP TRIGGER IF EXISTS update_vendor_status');
    }
}
Enter fullscreen mode Exit fullscreen mode

Console Command

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class UpdateVendorStatus extends Command
{
    protected $signature = 'vendors:update-status';
    protected $description = 'Update vendor status based on replies';

    public function handle()
    {
        $thresholdTime = Carbon::now()->subMinutes(30);
        $updated = DB::table('vendors')
            ->where('status', 1)
            ->whereNotIn('id', function ($query) use ($thresholdTime) {
                $query->select('vendor_id')
                      ->from('vendor_replies')
                      ->where('reply_time', '>', $thresholdTime);
            })
            ->update(['status' => 0]);

        $this->info("Updated $updated vendor statuses to inactive.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Scheduling the Command
In app/Console/Kernel.php:

protected function schedule(Schedule $schedule)
{
    $schedule->command('vendors:update-status')->everyThirtyMinutes();
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)