File "SendBulkStaffEmail.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Jobs/SendBulkStaffEmail.php
File size: 7.49 KB
MIME-type: text/x-php
Charset: utf-8
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\School;
use App\Models\Staff;
use App\Models\User;
use App\Services\CachingService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Throwable;
final class SendBulkStaffEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 3;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public int $timeout = 300; // 5 minutes timeout
/**
* The number of seconds to wait before retrying the job.
*
* @var int
*/
public int $backoff = 120; // 2 minutes between retries
/**
* Create a new job instance.
*
* @param int $schoolId The school ID from the main database (stored in job payload)
* @param int $staffUserId The staff user ID to send email for
*/
public function __construct(
private readonly int $schoolId,
private readonly int $staffUserId
) {}
/**
* Execute the job.
*
* @param CachingService $cache
* @return void
*/
public function handle(
CachingService $cache
): void {
// Ensure we start with main database connection
DB::setDefaultConnection('mysql');
try {
// Step 1: Get school information from main database using school_id
$school = School::on('mysql')->find($this->schoolId);
if (!$school) {
Log::error("School not found for ID: {$this->schoolId}");
throw new \Exception("School not found for ID: {$this->schoolId}");
}
// Step 2: Dynamically switch to school-specific database
DB::setDefaultConnection('school');
Config::set('database.connections.school.database', $school->database_name);
DB::purge('school');
DB::connection('school')->reconnect();
DB::setDefaultConnection('school');
// Step 3: Fetch uploaded staff data from school database
$staff = Staff::with('user')
->where('user_id', $this->staffUserId)
->first();
if (!$staff) {
Log::warning("No staff found for user ID: {$this->staffUserId} in school database: {$school->database_name}");
$this->switchBackToMainDatabase();
return;
}
$staffUser = $staff->user;
if (!$staffUser) {
Log::warning("User not found for staff user ID: {$this->staffUserId}");
$this->switchBackToMainDatabase();
return;
}
// Get settings and convert Collection to array for type compatibility
$schoolSettings = $cache->getSchoolSettings('*', $school->id);
$systemSettings = $cache->getSystemSettings();
// Convert Collections to arrays if needed
if ($schoolSettings instanceof \Illuminate\Support\Collection) {
$schoolSettings = $schoolSettings->toArray();
}
if ($systemSettings instanceof \Illuminate\Support\Collection) {
$systemSettings = $systemSettings->toArray();
}
// Generate password from mobile number
$password = $this->makeStaffPassword($staffUser->mobile);
// Prepare email content with placeholders
$emailBody = $this->replacePlaceholders(
$staffUser,
$password,
$school,
$schoolSettings,
$systemSettings
);
$data = [
'subject' => 'Welcome to ' . ($schoolSettings['school_name'] ?? $school->name),
'email' => $staffUser->email,
'email_body' => $emailBody
];
// Send email notification to staff
Mail::send('teacher.email', $data, static function ($message) use ($data) {
$message->to($data['email'])->subject($data['subject']);
});
Log::info("Email sent successfully to staff {$staffUser->email} for user {$staffUser->full_name}");
} catch (Throwable $e) {
Log::error("Bulk email job failed for school ID: {$this->schoolId}", [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
} finally {
// Always switch back to main database connection
$this->switchBackToMainDatabase();
}
}
/**
* Handle a job failure.
*
* @param Throwable $exception
* @return void
*/
public function failed(Throwable $exception): void
{
Log::error("Bulk email job permanently failed for school ID: {$this->schoolId}", [
'error' => $exception->getMessage(),
'trace' => $exception->getTraceAsString(),
'staff_user_id' => $this->staffUserId
]);
// Ensure we're back on main database
$this->switchBackToMainDatabase();
}
/**
* Switch back to main database connection.
*
* @return void
*/
private function switchBackToMainDatabase(): void
{
try {
DB::setDefaultConnection('mysql');
} catch (Throwable $e) {
Log::error("Failed to switch back to main database: " . $e->getMessage());
}
}
/**
* Generate staff password from mobile number.
*
* @param string|null $mobile
* @return string
*/
private function makeStaffPassword(?string $mobile): string
{
if (!$mobile) {
return 'password123';
}
return $mobile;
}
/**
* Replace placeholders in email template.
*
* @param User $staffUser
* @param string $password
* @param School $school
* @param array $schoolSettings
* @param array $systemSettings
* @return string
*/
private function replacePlaceholders(
User $staffUser,
string $password,
School $school,
array $schoolSettings,
array $systemSettings
): string {
$templateContent = $schoolSettings['email-template-staff'] ?? '';
$placeholders = [
'{full_name}' => $staffUser->full_name ?? ($staffUser->first_name . ' ' . $staffUser->last_name),
'{code}' => $school->code ?? '',
'{email}' => $staffUser->email ?? '',
'{password}' => $password,
'{school_name}' => $schoolSettings['school_name'] ?? $school->name,
'{support_email}' => $schoolSettings['school_email'] ?? $school->support_email ?? '',
'{support_contact}' => $schoolSettings['school_phone'] ?? $school->support_phone ?? '',
'{url}' => url('/'),
'{android_app}' => $systemSettings['app_link'] ?? '',
'{ios_app}' => $systemSettings['ios_app_link'] ?? '',
];
foreach ($placeholders as $placeholder => $replacement) {
$templateContent = str_replace($placeholder, $replacement, $templateContent);
}
return $templateContent;
}
}