18 KiB
Save Trigger System - Complete Guide
What is Save Trigger System?
The Save Trigger System is like having a smart assistant that automatically keeps all your project data organized and synchronized. When you save or update information in one place, it automatically updates related information in other places without you having to do anything extra.
Simple Analogy
Think of it like a smart spreadsheet that automatically updates all related cells when you change one value. For example:
- You update a welding log → System automatically updates test packages, paint schedules, and material lists
- You change a line specification → System updates all related construction logs and quality control records
- You modify a test package → System updates status reports and progress tracking
Which Modules Have Save Triggers?
The Save Trigger System is active in the following key modules:
🏗️ Construction & Engineering Modules
-
Line Lists - When you update line specifications, it syncs with:
- Weld logs
- Paint matrices
- NDE matrices
- Construction paint logs
- Paint follow-ups
-
Weld Logs - When you update welding information, it updates:
- Test packages
- NDE matrices
- Paint follow-ups
- Construction paint logs
- Handover records
🎨 Paint & Coating Modules
-
Paint Systems - When you update paint specifications, it syncs with:
- Paint matrices
- Paint follow-ups
- Construction paint logs
-
Color Systems - When you update color codes, it updates:
- Paint matrices
- Paint follow-ups
- Construction paint logs
📋 Testing & Quality Modules
-
Test Packages - When you update test package information, it updates:
- Test package base statuses
- Welding status calculations
- Progress tracking
-
Test Pack Base Statuses - When you update status information, it syncs with:
- Test packages
- ISO drawings
- Priority calculations
🔧 Support & Material Modules
-
Supports - When you update support information, it syncs with:
- Material take-off sheets
- Construction logs
-
MTOs (Material Take-Off Sheets) - When you update material information, it updates:
- Support structures
- Material lists
📊 Other Modules
- NDE Matrices - Updates quality control records
- Repair Logs - Updates test package statuses
- Punch Lists - Updates construction progress
- Subcontractors - Updates related project records
How Does It Work? (Step by Step)
Step 1: User Saves Data
When you save or update information in any of these modules through the web interface:
User clicks "Save" → AdminController receives the data → System prepares background job
Step 2: Background Job Creation
Instead of processing everything immediately (which could slow down your application), the system creates a background job:
AdminController → Creates ExecuteSaveTriggerJob → Job goes to queue → User gets immediate response
Step 3: Queue Processing
The job waits in a queue (like a waiting line) and gets processed by a background worker:
Queue Worker picks up job → Executes Save Trigger file → Updates related tables → Job completed
Step 4: Data Synchronization
The Save Trigger file contains specific business logic that updates all related information:
Trigger File → Reads updated data → Finds related records → Updates them automatically
Real-World Example: Weld Log Update
Let's say you update a weld log entry with new information:
What You Do:
- Open weld log entry
- Change the spool number from "SPL-001" to "SPL-002"
- Click "Save"
What the System Does Automatically:
- Immediate Response: You get confirmation that your change was saved
- Background Processing: System creates a job to update related data
- Test Package Update: Updates test package status and progress
- Paint Schedule Update: Updates paint follow-up records
- Quality Control Update: Updates NDE matrix records
- Construction Log Update: Updates construction paint logs
- Handover Update: Updates handover records
Result:
All related information is automatically synchronized without you having to update 5 different places manually.
Technical Architecture
Queue System Flow
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Saves │ │ Job Created │ │ Job Queued │
│ Data in UI │───▶│ & Dispatched │───▶│ in Database │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Gets │ │ Queue Worker │ │ Job Executed │
│ Immediate │ │ Picks Up Job │ │ in Background │
│ Response │ │ from Queue │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ ▼ ▼
▼ ┌─────────────────┐ ┌─────────────────┐
┌─────────────────┐ │ Trigger File │ │ Data Updated │
│ Application │ │ Executed │ │ in Related │
│ Continues │ │ Asynchronously│ │ Tables │
│ Responsively │ └─────────────────┘ └─────────────────┘
└─────────────────┘
Core Components
- ExecuteSaveTriggerJob - The main job class that handles trigger execution
- SaveTrigger Directory - Contains individual trigger files for each table
- AdminController Integration - Dispatches jobs after CRUD operations
- Queue System - Handles asynchronous execution
Directory Structure
app/
├── Jobs/
│ └── ExecuteSaveTriggerJob.php
├── Http/Controllers/
│ ├── AdminController.php
│ └── SaveTrigger/
│ ├── weld_logs.php
│ ├── test_packages.php
│ ├── supports.php
│ ├── m_t_o_s.php
│ ├── i_t_p_s.php
│ └── [other_table_triggers].php
How It Works (Technical Details)
1. Trigger Dispatch
When a record is saved or updated through the AdminController, the system automatically dispatches a job:
// In AdminController::saveJson()
ExecuteSaveTriggerJob::dispatch(
$tableName, // Target table name
$request->all(), // Request data
$request['key'], // Record ID
$request['values'] ?? null, // Updated values
$beforeData // Original data before update
);
2. Job Execution
The ExecuteSaveTriggerJob handles the execution:
public function handle(): void
{
$path = app_path("Http/Controllers/SaveTrigger/{$this->tableName}.php");
if (file_exists($path)) {
// Make variables globally accessible in trigger file
$tableName = $this->tableName;
$request = $this->requestData;
$key = $this->key;
$values = $this->values;
$beforeData = $this->beforeData;
// Include and execute trigger file
include($path);
}
}
3. Trigger File Structure
Each trigger file follows a specific pattern:
<?php
// 1. Get the updated record
$data = db($tableName)->where("id", $request['key'])->first();
// 2. Define unique fields for upsert operations
$uniqueFields = [
'field1' => $data->value1,
'field2' => $data->value2,
];
// 3. Define data to update/insert
$dataToUpdate = [
'target_field1' => $data->source_field1,
'target_field2' => $data->source_field2,
];
// 4. Execute upsert operation
echo db('target_table')->updateOrInsert($uniqueFields, $dataToUpdate);
?>
Key Features
1. Asynchronous Execution
- Triggers run in the background using Laravel's queue system
- Main application flow is not blocked
- Failed jobs can be retried automatically
2. Telescope Integration
The job includes Telescope monitoring capabilities:
public function getDescription(): string
{
$action = $this->key ? 'Update' : 'Create';
return "Execute Save Trigger for {$this->tableName} table - {$action} operation";
}
public function tags(): array
{
$action = $this->key ? 'Update' : 'Create';
return [
"SaveTrigger:{$this->tableName}",
"Action:{$action}",
"Table:{$this->tableName}"
];
}
3. Change Detection
Advanced triggers can detect which fields have changed:
// Detect changed fields
$changedFields = [];
$dataArray = (array) $data;
foreach ($dataArray as $key => $value) {
if (isset($beforeData->$key) && $beforeData->$key != $value) {
$changedFields[] = $key;
}
}
// Run specific logic based on field changes
$shouldRunSpoolStatusChanger = !empty(array_intersect($changedFields, $spoolRelatedFields));
4. Complex Business Logic
Triggers can implement sophisticated business rules:
- Data Synchronization: Keep related tables in sync
- Status Updates: Update status fields based on conditions
- Calculations: Perform complex calculations and updates
- Validation: Ensure data integrity across tables
Performance Optimizations
Database Locking Prevention
The system uses chunking and transaction management to prevent database locking issues:
// Example: Processing large datasets in chunks
$recordsChunked = $records->chunk(5); // Process 5 records at a time
foreach($recordsChunked as $chunk) {
DB::transaction(function () use ($chunk) {
foreach($chunk as $record) {
// Update logic here
}
}, 3); // Retry up to 3 times
usleep(100000); // 0.1 second pause between chunks
}
Memory Management
- Small chunk sizes (3-10 records per chunk)
- Short transaction durations
- Automatic retry mechanisms
- Memory limit configurations
Example Trigger Implementations
Simple Upsert Trigger
<?php
// m_t_o_s.php - Material Take Off Sheet trigger
$data = db("m_t_o_s")->where("id", $request['key'])->first();
$uniqueFields = [
'line_number' => $data->line,
'support_code' => $data->component_code_id,
];
$dataToUpdate = [
'zone' => $data->project,
'line_number' => $data->line,
'rev' => $data->rev,
'support_code' => $data->component_code_id,
'erection_materials_name' => $data->description_ru,
'standart' => $data->manufacturing_standard,
'materials' => $data->material,
'quantity' => $data->quantity,
'welded_pad_measure' => $data->odmm_1,
'thickness' => $data->thicknessmm_1,
'unit_weight' => $data->weight,
'total_weight' => $data->total_weight,
'designer' => $data->designer,
];
echo db('supports')->updateOrInsert($uniqueFields, $dataToUpdate);
?>
Complex Multi-Table Trigger
<?php
// weld_logs.php - Complex welding log trigger
$id = $request['key'];
$data = db($tableName)->where("id", $id)->first();
// Detect field changes
$changedFields = [];
$dataArray = (array) $data;
foreach ($dataArray as $key => $value) {
if (isset($beforeData->$key) && $beforeData->$key != $value) {
$changedFields[] = $key;
}
}
// Define field groups for different operations
$spoolRelatedFields = ['spool_number', 'iso_number', 'type_of_joint'];
$ndeRelatedFields = ['type_of_welds', 'fluid_code', 'type_of_joint'];
// Determine which operations to run
$shouldRunSpoolStatusChanger = !empty(array_intersect($changedFields, $spoolRelatedFields));
$shouldRunNdeMatrixUpdate = !empty(array_intersect($changedFields, $ndeRelatedFields));
// Execute specific operations based on changes
if ($shouldRunSpoolStatusChanger) {
// Update spool status
$spoolStatusChangerParams = [
'spool_number' => $data->spool_number,
'iso_number' => $data->iso_number,
];
$spoolStatusChangerView = view('cron.spool-status-changer', $spoolStatusChangerParams)->render();
}
if ($shouldRunNdeMatrixUpdate) {
// Update NDE matrix
// Complex NDE logic here
}
?>
Supervisor System
What is Supervisor?
Supervisor is a process control system that ensures your queue workers keep running. Think of it as a manager that:
- Starts queue workers when the server boots
- Monitors them continuously
- Restarts them if they crash
- Logs their activities
Supervisor Configuration
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/your/project/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=8
redirect_stderr=true
stdout_logfile=/path/to/your/project/storage/logs/worker.log
stopwaitsecs=3600
Why Use Supervisor?
- Reliability: Workers automatically restart if they crash
- Scalability: Can run multiple workers simultaneously
- Monitoring: Easy to monitor worker status
- Logging: All worker activities are logged
Supervisor Commands
# Check supervisor status
sudo supervisorctl status
# Restart all workers
sudo supervisorctl restart laravel-worker:*
# Restart specific worker
sudo supervisorctl restart laravel-worker:laravel-worker_00
# Reload supervisor configuration
sudo supervisorctl reread
sudo supervisorctl update
Telescope Job Monitoring
What is Telescope?
Laravel Telescope is a debugging and monitoring tool that provides insight into your application's requests, exceptions, logs, database queries, cache operations, job executions, and more.
Job Tracking with Tags
The Save Trigger system uses custom tags to make job tracking easier in Telescope:
public function tags(): array
{
$action = $this->key ? 'Update' : 'Create';
return [
"SaveTrigger:{$this->tableName}",
"Action:{$action}",
"Table:{$this->tableName}"
];
}
How to Monitor Jobs in Telescope
- Access Telescope: Go to
/telescopein your application - Navigate to Jobs: Click on "Jobs" in the sidebar
- Filter by Tags: Use the search/filter to find specific jobs:
SaveTrigger:weld_logs- All weld log triggersAction:Update- All update operationsTable:test_packages- All test package triggers
Telescope Job Details
Each job in Telescope shows:
Job Details:
├── Description: "Execute Save Trigger for weld_logs table - Update operation"
├── Tags: ["SaveTrigger:weld_logs", "Action:Update", "Table:weld_logs"]
├── Queue: "default"
├── Status: "processed"
├── Execution Time: "2.5s"
├── Memory Usage: "45.2MB"
└── Exception: (if any)
Advanced Telescope Filtering
// In Telescope configuration
'watchers' => [
Watchers\JobWatcher::class => [
'enabled' => env('TELESCOPE_JOB_WATCHER', true),
'ignore' => [
// Ignore specific jobs if needed
],
],
],
Job Performance Monitoring
Monitor job performance patterns:
- Execution Time: Track how long jobs take to complete
- Memory Usage: Monitor memory consumption
- Failure Rate: Check job failure patterns
- Queue Depth: Monitor queue backlog
Telescope Dashboard
Create custom Telescope dashboards to monitor:
- Job Success Rate: Percentage of successful jobs
- Average Execution Time: Mean job duration
- Queue Depth: Number of pending jobs
- Error Patterns: Common job failures
Best Practices
1. Error Handling
Always include proper error handling in trigger files:
try {
// Trigger logic here
echo db('target_table')->updateOrInsert($uniqueFields, $dataToUpdate);
} catch (\Exception $e) {
Log::error("Save trigger error for {$tableName}: " . $e->getMessage());
// Don't throw - let the job fail gracefully
}
2. Performance Optimization
- Use database indexes on frequently queried fields
- Implement change detection to avoid unnecessary operations
- Use batch operations when possible
- Set appropriate database timeouts
3. Monitoring
- Use Telescope to monitor job execution
- Implement logging for debugging
- Set up job failure notifications
4. Testing
- Test triggers with various data scenarios
- Verify data consistency after trigger execution
- Monitor performance impact
Configuration
Queue Configuration
Ensure your queue is properly configured in config/queue.php:
'default' => env('QUEUE_CONNECTION', 'database'),
Job Timeout
Set appropriate timeout values for long-running triggers:
// In trigger files
DB::statement('SET SESSION innodb_lock_wait_timeout = 300');
DB::statement('SET SESSION lock_wait_timeout = 300');
ini_set('memory_limit', '512M');
ini_set('max_execution_time', 600);
Troubleshooting
Common Issues
- Job Not Executing: Check queue worker is running
- Data Not Syncing: Verify trigger file exists and has correct logic
- Performance Issues: Implement change detection and optimize queries
- Memory Issues: Increase memory limits and optimize data processing
Debugging
- Check Telescope for job details and execution logs
- Review Laravel logs for error messages
- Test trigger files independently
- Monitor database performance during trigger execution
Security Considerations
- Input Validation: Always validate data in trigger files
- SQL Injection: Use Laravel's query builder methods
- Access Control: Ensure proper authentication and authorization
- Data Integrity: Implement proper error handling and rollback mechanisms
This Save Trigger System provides a robust, scalable solution for maintaining data consistency and implementing complex business rules across the application.