›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-approval
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Workflows and stages
  • Choosing the workflow
  • Approvers and resolvers
  • Requests and state
  • Conditional stages

Approval flow

  • Submitting for approval
  • Recording decisions
  • Attribute drafts
  • Returns and resubmission
  • Bulk decisions

Assignments and deadlines

  • Delegation and reassignment
  • SLAs and escalation

Reading and authorization

  • Eligibility and authorization
  • Queries and timelines
  • Events and listeners
  • Notifications
  • Notification content

Operations

  • Workflow definitions
  • Audit trail and evidence
  • Console commands
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Workflows and stages
  • Choosing the workflow
  • Approvers and resolvers
  • Requests and state
  • Conditional stages

Approval flow

  • Submitting for approval
  • Recording decisions
  • Attribute drafts
  • Returns and resubmission
  • Bulk decisions

Assignments and deadlines

  • Delegation and reassignment
  • SLAs and escalation

Reading and authorization

  • Eligibility and authorization
  • Queries and timelines
  • Events and listeners
  • Notifications
  • Notification content

Operations

  • Workflow definitions
  • Audit trail and evidence
  • Console commands
  • Testing
  • Troubleshooting

byrcsc/laravel-approval · 1.x

Notification content.

Customize notification text, localization, subjects, and email views.

The shipped notifications use generic wording, such as "You have been asked to approve Purchase Order #41." You can change the wording without replacing the notification classes. Pick the smallest option that fits:

You want toPublish
Reword or translate the messagesapproval-translations
Restructure the emails, add a link into your UIapproval-views
Change who receives them, or the whole messageNeither, see Notifications

Nothing here is required. The package ships working English messages and mail views, and an application that never publishes either still sends mail.

Rewording and translating

Every user-facing string lives in one file:

php artisan vendor:publish --tag="approval-translations"

That writes lang/vendor/approval/en/approval.php:

return [
    'requested' => [
        'subject' => 'Approval needed: :subject',
        'body' => 'You have been asked to approve :subject.',
    ],
    'approved' => [
        'subject' => 'Approved: :subject',
        'body' => ':subject has been approved.',
    ],
    // rejected, returned, assigned, overdue, conflicted

    'reason' => 'Reason: :reason',
    'reference' => 'Reference: :reference.',
    'deleted_record' => 'a deleted record',
];

Each event has a subject and a body. conflicted also has an unresolved line. The reason, reference, and deleted_record keys are shared across messages.

To reword a message, edit the file in place. To translate, copy the directory to es, fr, or another locale. The package resolves messages through trans() at send time, so they follow the application locale. A request routed to a French-speaking approver reads in French if lang/vendor/approval/fr/approval.php exists.

A placeholder can appear anywhere in the line. Concatenated strings fix the word order, so translations go through this file instead:

'requested' => [
    'subject' => 'Approbation requise : :subject',
    'body' => 'Il vous est demandé d\'approuver :subject.',
],

:subject is a readable name for the record. It is the record's name, title, reference, or label attribute, or ClassName #key if none is set. See Notifications for the full resolution order and what a deleted record reads as.

A key with no translation renders as the key itself instead of throwing an exception. An incomplete locale file produces visibly wrong mail, not a failed queue job.

Restructuring the emails

To add a button into your own UI, change the layout, or drop a line entirely:

php artisan vendor:publish --tag="approval-views"

That writes resources/views/vendor/approval/mail/, one Blade file per message:

approved.blade.php     rejected.blade.php     conflicted.blade.php
requested.blade.php    returned.blade.php     overdue.blade.php
assigned.blade.php

Each is an ordinary markdown mail view. The shipped files carry only the message and reference lines. A published copy can add interface elements such as a review button:

<x-mail::message>
{{ __('approval::approval.requested.body', ['subject' => $subject]) }}

<x-mail::button :url="route('approvals.show', $request)">
{{ __('Review it') }}
</x-mail::button>

{{ __('approval::approval.reference', ['reference' => $reference]) }}
</x-mail::message>

What each view receives

Variable
$subjectThe readable record name used in the subject line.
$referenceThe request's public ULID.
$requestThe ApprovalRequest.
$approvableThe record, or null if it has been deleted since.
$notificationThe notification instance, for anything else you need.

The rejected and returned views also receive a nullable $reason. Both shipped views guard it with @if ($reason !== null). Keep that guard, or a decision recorded without a comment renders an empty "Reason:" line.

Guard $approvable too. If a record is hard-deleted between the decision and the send, the notification has a request but no record. A published view that calls route('orders.show', $approvable) then fails for that mail.

Publishing one does not freeze the other

The shipped views read their wording through __('approval::approval.…') instead of hardcoding English. If you publish the views, the translations still apply. If you publish the translations, the views use them. You can also publish both, because the views and the translations share the same keys.

The translations apply only if your published views keep reading them. A view with inline text still works, but that message leaves the locale system. Mail to a French approver is then part French and part the text you typed.

Replacing a message with your own

To change the audience as well, point the event at your own notification class. See Notifications for the map and the recipient helpers.

A replacement class does not lose access to the published content. Extend ApprovalNotification and call the protected mailFor(), which builds the MailMessage from the same translation keys and views:

use ByRcsc\LaravelApproval\Events\ApprovalEvent;
use ByRcsc\LaravelApproval\Notifications\ApprovalNotification;
use Illuminate\Notifications\Messages\MailMessage;

final class PurchaseOrderNeedsApproval extends ApprovalNotification
{
    public static function recipients(ApprovalEvent $event): iterable
    {
        return self::activeApprovers($event);
    }

    public function toMail(object $notifiable): MailMessage
    {
        return $this->mailFor('requested');
    }
}

recipients() is abstract on the base class, so every replacement declares its audience. See why notifications declare their own recipients.

mailFor($key, $with = []) reads approval::approval.{$key}.subject for the subject, renders approval::mail.{$key}, and passes the five variables above. mailFor() merges anything in $with into the view variables. The shipped rejected and returned notifications use $with to supply $reason:

return $this->mailFor('rejected', ['reason' => $this->reason()]);

A class that needs only different recipients can keep the published wording. A class that needs a different message can pass extra variables to its own view.

Both of these are the mail channel only

Publishing translations and views changes what the mail channel sends. Neither reaches the others.

database and broadcast render toArray(), which carries structured data rather than prose:

[
    'request' => '01JG3…',   // the public ULID
    'state' => 'pending',
    'subject' => 'Purchase Order #41',
]

The payload has nothing to translate. subject is the record's readable name, not a translated line, so rewording approval.php does not change a database notification. An application that renders an in-app inbox from these rows writes its own wording based on state. People read the row long after the package writes it, so the text should use the reader's locale, not the locale in effect when the row was stored.

To change the payload itself, override toArray() on a replacement class.

What to read next

  • Notifications to replace a message, audience, or channel.
  • Events and listeners to build notifications for other lifecycle events.
  • Testing to assert notification behavior around approval actions.
PreviousNotificationsNextWorkflow definitions
View source

On this page

  1. Rewording and translating
  2. Restructuring the emails
  3. What each view receives
  4. Publishing one does not freeze the other
  5. Replacing a message with your own
  6. Both of these are the mail channel only
  7. What to read next