›
byrcsc/laravel-assignment · 1.x
The engine notifies the assignee at four moments, and each notification is swappable or removable.
The package notifies one person: the assignee whose work changed. Managers, watchers, and dashboards are not its business, and hang off events instead.
| Moment | Notification | Sent to |
|---|---|---|
| An offer is made | OfferReceived | The assignee the offer names |
| An offer expires | OfferExpired | The assignee who let it lapse |
| An assignment becomes active | AssignmentReceived | The new holder |
| An assignment is taken away | AssignmentRemoved | The holder who lost it |
AssignmentReceived covers both routes to active work: a direct assign() and
an accepted offer. A listener does not need to tell them apart.
Two cases are deliberately quiet:
Reassignment sends exactly two notifications: AssignmentRemoved to the
outgoing assignee and AssignmentReceived to the incoming one.
// config/assignment.php
'notification_channels' => ['mail'],Mail is the only channel on by default, because it needs nothing installed. The list applies to all four notifications.
Adding 'database' requires Laravel's notifications table:
php artisan make:notifications-table
php artisan migrateThe database payload is small and stable:
[
'assignment_id' => 41,
'assignable_type' => 'App\Models\Callout',
'assignable_id' => 7,
'role' => 'crew',
'status' => 'offered',
]Any channel Laravel supports works, as long as your notifiable can route to it.
Set ['mail', 'database', 'broadcast'] and all three are used.
An assignee that does not use Laravel's Notifiable trait is skipped without
an error. A Team model with no inbox is a normal assignee, and fanning out to
its members is your application's decision:
public function handle(Assigned $event): void
{
$team = $event->assignment->assignee;
if ($team instanceof Team) {
Notification::send($team->members, new WorkArrived($event->assignment));
}
}Point the config key at your own class:
// config/assignment.php
'notifications' => [
'offered' => App\Notifications\CrewOfferReceived::class,
'offer_expired' => ByRcsc\LaravelAssignment\Notifications\OfferExpired::class,
'assigned' => ByRcsc\LaravelAssignment\Notifications\AssignmentReceived::class,
'unassigned' => ByRcsc\LaravelAssignment\Notifications\AssignmentRemoved::class,
],The class is built through the container with the assignment passed as
assignment, so the simplest replacement extends the shipped base:
use ByRcsc\LaravelAssignment\Notifications\AssignmentNotification;
final class CrewOfferReceived extends AssignmentNotification
{
protected function name(): string
{
return 'offer_received';
}
public function via(object $notifiable): array
{
return ['mail', 'sms'];
}
}name() chooses which translation keys and which mail view the notification
uses. Overriding via(), toMail(), or toArray() on your subclass changes
one notification without touching the other three.
A class that does not extend AssignmentNotification works too, as long as it
is a notification and its constructor accepts assignment.
Set the key to null:
'notifications' => [
'offered' => ByRcsc\LaravelAssignment\Notifications\OfferReceived::class,
'offer_expired' => null,
'assigned' => null,
'unassigned' => ByRcsc\LaravelAssignment\Notifications\AssignmentRemoved::class,
],Nothing is sent for a null moment, and no listener runs.
php artisan vendor:publish --tag="assignment-translations"That writes lang/vendor/assignment/en/notifications.php, keyed by moment:
return [
'offer_received' => [
'subject' => 'You have received an assignment offer',
'body' => 'A new assignment offer is waiting for your response.',
],
'offer_expired' => [...],
'assignment_received' => [...],
'assignment_removed' => [...],
];Add a directory per locale to translate them.
php artisan vendor:publish --tag="assignment-views"That writes four Blade files to resources/views/vendor/assignment/mail/, one
per moment. Each receives $assignment and $body, and builds on Laravel's
own mail::message component:
@component('mail::message')
# {{ __('assignment::notifications.assignment_received.subject') }}
{{ $body }}
@component('mail::button', ['url' => route('callouts.show', $assignment->assignable)])
View the callout
@endcomponent
@endcomponentThe messages are rendered as markdown mail, so the mail:: components are
available.
The shipped notifications use Laravel's Queueable trait but do not implement
ShouldQueue, so they are sent inline. To move them off the request, extend
one and add the interface:
final class QueuedOfferReceived extends OfferReceived implements ShouldQueue
{
}Point the config key at it. The events themselves cannot be queued; see events.