registry = $registry; } /** * Execute all applicable triggers * * @param object $weldLogData Current weld log data * @param object|null $beforeData Previous weld log data (null for new records) * @param array $changedFields List of changed field names * @param bool $isNewRecord Whether this is a new record * @return array Execution results */ public function executeTriggers( $weldLogData, $beforeData = null, array $changedFields = [], bool $isNewRecord = false, ?string $action = null ): array { $this->overallStartTime = microtime(true); Log::info("=== WELD LOG SAVE TRIGGER STARTED ===", [ 'weld_log_id' => $weldLogData->id, 'timestamp' => now()->toDateTimeString(), 'is_new_record' => $isNewRecord, 'changed_fields_count' => count($changedFields), 'changed_fields' => $changedFields, 'action' => $action, 'memory_usage_start' => round(memory_get_usage(true) / 1024 / 1024, 2) . ' MB' ]); // Set database timeouts and memory limits TransactionHelper::setDatabaseTimeouts(300, 600, 600); ini_set('memory_limit', '2G'); ini_set('max_execution_time', 600); // 10 minutes $results = []; $triggers = $this->registry->getTriggersInOrder(); Log::info("Total triggers registered: " . count($triggers), [ 'trigger_names' => array_map(function($t) { return $t->getName(); }, $triggers) ]); foreach ($triggers as $trigger) { // Check if trigger should run if (!$trigger->shouldRun($changedFields, $isNewRecord)) { Log::info("Skipping trigger: {$trigger->getName()}", [ 'reason' => 'shouldRun returned false', 'order' => $trigger->getOrder(), 'dependent_fields' => $trigger->getDependentFields() ]); $results[$trigger->getName()] = [ 'skipped' => true, 'reason' => 'shouldRun returned false' ]; continue; } // Execute sync or async if ($trigger->isAsync()) { $this->executeAsync($trigger, $weldLogData, $beforeData, $changedFields); $results[$trigger->getName()] = [ 'queued' => true, 'execution' => 'async' ]; } else { try { $result = $trigger->execute($weldLogData, $beforeData, [ 'changed_fields' => $changedFields, 'is_new_record' => $isNewRecord, 'action' => $action ]); $results[$trigger->getName()] = array_merge($result, [ 'executed' => true, 'execution' => 'sync' ]); } catch (\Throwable $th) { Log::error("Trigger execution failed: {$trigger->getName()}", [ 'error' => $th->getMessage(), 'file' => $th->getFile(), 'line' => $th->getLine(), 'trace' => $th->getTraceAsString() ]); $results[$trigger->getName()] = [ 'executed' => false, 'error' => $th->getMessage(), 'execution' => 'sync' ]; // Re-throw critical errors, log others if ($this->isCriticalTrigger($trigger->getName())) { throw $th; } } } } $this->logOverallCompletion($weldLogData->id, $results); return $results; } /** * Execute trigger asynchronously (via queue) * * @param \App\Services\WeldLogTriggers\Contracts\WeldLogTriggerInterface $trigger * @param object $weldLogData * @param object|null $beforeData * @param array $changedFields */ protected function executeAsync($trigger, $weldLogData, $beforeData, $changedFields) { // Future implementation: Dispatch job to queue // \App\Jobs\WeldLogTriggerJob::dispatch( // get_class($trigger), // $weldLogData->id, // $beforeData, // $changedFields // ); Log::info("Trigger queued (async execution): {$trigger->getName()}", [ 'weld_log_id' => $weldLogData->id, 'note' => 'Async execution not yet implemented, falling back to sync' ]); // Fallback to sync execution for now try { $trigger->execute($weldLogData, $beforeData, [ 'changed_fields' => $changedFields, 'is_new_record' => false, // $action is not available here in the current signature, but async isn't used yet anyway ]); } catch (\Throwable $th) { Log::error("Async trigger execution failed: {$trigger->getName()}", [ 'error' => $th->getMessage() ]); } } /** * Check if a trigger is critical (should stop execution on failure) * * @param string $triggerName * @return bool */ protected function isCriticalTrigger(string $triggerName): bool { // Define which triggers are critical $criticalTriggers = [ 'Spool Status Changer', 'Line Lists Update', 'NDE Matrix Update', 'Construction Paint Logs Sync', 'Paint Follow Ups Sync', ]; return in_array($triggerName, $criticalTriggers); } /** * Log overall execution completion * * @param int $weldLogId * @param array $results */ protected function logOverallCompletion($weldLogId, array $results) { $overallDuration = round((microtime(true) - $this->overallStartTime) * 1000, 2); // Count execution statistics $executedCount = 0; $skippedCount = 0; $queuedCount = 0; $failedCount = 0; foreach ($results as $triggerName => $result) { if (isset($result['executed']) && $result['executed']) { $executedCount++; } elseif (isset($result['skipped']) && $result['skipped']) { $skippedCount++; } elseif (isset($result['queued']) && $result['queued']) { $queuedCount++; } elseif (isset($result['executed']) && !$result['executed']) { $failedCount++; } } Log::info("=== WELD LOG SAVE TRIGGER COMPLETED ===", [ 'weld_log_id' => $weldLogId, 'timestamp' => now()->toDateTimeString(), 'total_execution_time_ms' => $overallDuration, 'total_execution_time_sec' => round($overallDuration / 1000, 3), 'peak_memory_usage_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2), 'final_memory_usage_mb' => round(memory_get_usage(true) / 1024 / 1024, 2), 'statistics' => [ 'total_triggers' => count($results), 'executed' => $executedCount, 'skipped' => $skippedCount, 'queued' => $queuedCount, 'failed' => $failedCount ] ]); } /** * Get the registry instance * * @return WeldLogTriggerRegistry */ public function getRegistry(): WeldLogTriggerRegistry { return $this->registry; } }