push notification api

This commit is contained in:
mohanmd 2026-06-14 17:27:54 +05:30
parent 8b8335eb32
commit 045b8d1f4c
11 changed files with 218 additions and 6 deletions

View File

@ -296,6 +296,33 @@ public function broadcastNotification(Request $request)
'updated_at' => now(),
]);
// Send FCM push notifications for broadcast
if ($request->channel === 'push' || $request->channel === 'both') {
$recipientsLower = strtolower($request->recipients);
if (str_contains($recipientsLower, 'worker') || $recipientsLower === 'all') {
$workers = \App\Models\Worker::whereNotNull('fcm_token')->get();
foreach ($workers as $worker) {
\App\Services\FCMService::sendPushNotification(
$worker->fcm_token,
$request->title,
$request->message,
['type' => 'broadcast']
);
}
}
if (str_contains($recipientsLower, 'employer') || $recipientsLower === 'all') {
$employers = \App\Models\User::where('role', 'employer')->whereNotNull('fcm_token')->get();
foreach ($employers as $employer) {
\App\Services\FCMService::sendPushNotification(
$employer->fcm_token,
$request->title,
$request->message,
['type' => 'broadcast']
);
}
}
}
return back()->with('success', "Notification campaign broadcasted successfully to all {$request->recipients} users!");
}
@ -523,13 +550,15 @@ public function storeAnnouncement(Request $request)
'content' => $request->content,
]);
\App\Models\Announcement::create([
$ann = \App\Models\Announcement::create([
'title' => $request->title,
'body' => $body,
'type' => 'Charity',
'status' => 'approved',
]);
$this->notifyUsersOfEvent($ann);
return back()->with('success', 'Charity Event created successfully.');
}
@ -541,6 +570,8 @@ public function approveAnnouncement(Request $request, $id)
$ann = \App\Models\Announcement::findOrFail($id);
$ann->update(['status' => 'approved']);
$this->notifyUsersOfEvent($ann);
return back()->with('success', 'Charity Event approved successfully.');
}
@ -572,4 +603,48 @@ public function deleteAnnouncement(Request $request, $id)
return back()->with('success', 'Charity Event deleted successfully.');
}
/**
* Broadcast FCM Push Notification for new Event/Announcement.
*/
protected function notifyUsersOfEvent($ann)
{
$title = "New Event: " . $ann->title;
$body = $ann->body;
if (str_starts_with($ann->body, '{"type":"Charity"')) {
$decoded = json_decode($ann->body, true);
if ($decoded && isset($decoded['content'])) {
$body = $decoded['content'];
}
}
// Notify all workers
$workers = \App\Models\Worker::whereNotNull('fcm_token')->get();
foreach ($workers as $worker) {
\App\Services\FCMService::sendPushNotification(
$worker->fcm_token,
$title,
$body,
[
'type' => 'announcement',
'announcement_id' => $ann->id,
]
);
}
// Notify all employers
$employers = \App\Models\User::where('role', 'employer')->whereNotNull('fcm_token')->get();
foreach ($employers as $employer) {
\App\Services\FCMService::sendPushNotification(
$employer->fcm_token,
$title,
$body,
[
'type' => 'announcement',
'announcement_id' => $ann->id,
]
);
}
}
}

View File

@ -25,6 +25,7 @@ public function login(Request $request)
'email' => 'nullable|string',
'mobile' => 'nullable|string',
'password' => 'required|string',
'fcm_token' => 'nullable|string|max:255',
]);
$validator->after(function ($validator) use ($request) {
@ -103,7 +104,11 @@ public function login(Request $request)
], 403);
}
$user->update(['api_token' => $apiToken]);
$userUpdateData = ['api_token' => $apiToken];
if ($request->has('fcm_token')) {
$userUpdateData['fcm_token'] = $request->fcm_token;
}
$user->update($userUpdateData);
if ($sponsor) {
$sponsor->update([
'api_token' => $apiToken,

View File

@ -93,6 +93,7 @@ public function updateProfile(Request $request)
'notifications' => 'required|boolean',
'current_password' => 'nullable|required_with:new_password|string',
'new_password' => 'nullable|string|min:8|confirmed',
'fcm_token' => 'nullable|string|max:255',
]);
if ($validator->fails()) {
@ -121,10 +122,14 @@ public function updateProfile(Request $request)
$oldEmail = $employer->getOriginal('email') ?? $employer->email;
// Update user table
$employer->update([
$userData = [
'name' => $request->name,
'email' => $request->email,
]);
];
if ($request->has('fcm_token')) {
$userData['fcm_token'] = $request->fcm_token;
}
$employer->update($userData);
// Sync with corresponding sponsor record if found
$matchingSponsor = \App\Models\Sponsor::where('email', $oldEmail)->first();

View File

@ -798,6 +798,19 @@ public function hireCandidate(Request $request, $id = null)
}
}
// Dispatch push notification to worker
if ($worker && $worker->fcm_token) {
\App\Services\FCMService::sendPushNotification(
$worker->fcm_token,
"Congratulations! You've been Hired",
"Employer " . ($employer->name ?? "Employer") . " has hired you.",
[
'type' => 'hired',
'employer_id' => $employer->id,
]
);
}
return response()->json([
'success' => true,
'message' => 'Candidate successfully marked as Hired!',

View File

@ -441,6 +441,7 @@ public function login(Request $request)
'phone' => 'required_without:email|nullable|string',
'email' => 'required_without:phone|nullable|email',
'password' => 'required|string',
'fcm_token' => 'nullable|string|max:255',
]);
if ($validator->fails()) {
@ -464,7 +465,11 @@ public function login(Request $request)
}
$apiToken = Str::random(80);
$worker->update(['api_token' => $apiToken]);
$updateData = ['api_token' => $apiToken];
if ($request->has('fcm_token')) {
$updateData['fcm_token'] = $request->fcm_token;
}
$worker->update($updateData);
return response()->json([
'success' => true,

View File

@ -271,6 +271,21 @@ public function sendMessage(Request $request, $id)
}
});
// Dispatch push notification to employer
$employer = $conv->employer;
if ($employer && $employer->fcm_token) {
\App\Services\FCMService::sendPushNotification(
$employer->fcm_token,
"New Message from " . ($worker->name ?? "Worker"),
$request->text ?: "Sent an attachment",
[
'conversation_id' => $conv->id,
'sender_type' => 'worker',
'sender_id' => $worker->id,
]
);
}
return response()->json([
'success' => true,
'message' => 'Message sent successfully.',

View File

@ -351,6 +351,22 @@ public function respondToOffer(Request $request, $id)
}
});
// Dispatch push notification to employer
$employer = $offer->employer;
if ($employer && $employer->fcm_token) {
$statusMessage = $responseStatus === 'accepted' ? 'accepted' : 'rejected';
\App\Services\FCMService::sendPushNotification(
$employer->fcm_token,
"Job Offer " . ucfirst($statusMessage),
"Worker " . ($worker->name ?? "Candidate") . " has " . $statusMessage . " your job offer.",
[
'type' => 'job_offer_response',
'offer_id' => $offer->id,
'status' => $responseStatus,
]
);
}
return response()->json([
'success' => true,
'message' => "Job offer successfully " . $responseStatus . ".",

View File

@ -10,7 +10,7 @@
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
#[Fillable(['name', 'email', 'password', 'role', 'status', 'subscription_status', 'subscription_expires_at', 'api_token'])]
#[Fillable(['name', 'email', 'password', 'role', 'status', 'subscription_status', 'subscription_expires_at', 'api_token', 'fcm_token'])]
#[Hidden(['password', 'remember_token', 'api_token'])]
class User extends Authenticatable
{

View File

@ -119,6 +119,20 @@ public static function sendPushNotification($token, $title, $body, array $data =
'title' => $title,
'body' => $body,
],
'android' => [
'notification' => [
'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
'sound' => 'default',
],
],
'apns' => [
'payload' => [
'aps' => [
'sound' => 'default',
'badge' => 1,
],
],
],
]
];

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('fcm_token')->nullable()->after('api_token');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('fcm_token');
});
}
};

View File

@ -202,4 +202,40 @@ public function test_unauthenticated_requests_are_blocked()
$response = $this->getJson('/api/workers/conversations');
$response->assertStatus(401);
}
/**
* Test worker message triggers push notification.
*/
public function test_worker_message_triggers_push_notification()
{
\Illuminate\Support\Facades\Http::fake([
'https://oauth2.googleapis.com/token' => \Illuminate\Support\Facades\Http::response(['access_token' => 'mocked-fcm-token'], 200),
'https://fcm.googleapis.com/*' => \Illuminate\Support\Facades\Http::response(['name' => 'projects/migrant-fe5a7/messages/mock-id'], 200),
]);
// Set fcm_token on employer
$this->employer->update(['fcm_token' => 'employer-device-fcm-token-123']);
// Create conversation
$conversation = Conversation::create([
'employer_id' => $this->employer->id,
'worker_id' => $this->worker->id,
]);
// Reply request
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->token,
])->postJson('/api/workers/conversations/' . $conversation->id . '/messages', [
'text' => 'Testing push notification to employer!'
]);
$response->assertStatus(201);
// Assert Http call was made to FCM endpoint
\Illuminate\Support\Facades\Http::assertSent(function ($request) {
return str_contains($request->url(), 'fcm.googleapis.com') &&
$request['message']['token'] === 'employer-device-fcm-token-123' &&
$request['message']['notification']['body'] === 'Testing push notification to employer!';
});
}
}