Streamlining Mentorship: Empowering Substitutes for Uninterrupted Sessions
Few things are as frustrating as a last-minute cancellation, especially in structured mentorship programs. It disrupts schedules, disappoints mentees, and adds administrative overhead. This was a challenge we faced within our devlog-ist/landing project, specifically concerning our mentorship cycle initiative.
The Mentorship Dilemma: Rigid Bookings
Previously, if a titular mentor was unable to deliver a scheduled session, the only recourse was to cancel it. This rigid approach often led to missed opportunities for mentees and placed an unnecessary burden on both mentors and program administrators. We recognized the need for a more flexible solution that could ensure continuity and leverage our network of available mentors.
Introducing Mentor Substitutes: A Flexible Solution
To address this, we've implemented a robust mentor substitute feature. Now, if a titular mentor cannot conduct a session, they can invite another qualified mentor to step in. This significantly reduces cancellations, maintaining the flow of mentorship and improving the overall experience for everyone involved.
The Workflow: Invitation to Acceptance
The process is designed to be secure and straightforward:
- Invitation: The titular mentor selects a substitute from a list of available users and initiates an invitation. This action triggers a signed invitation email to the substitute.
- Response: The substitute receives the email containing unique, signed URLs to either accept or reject the assignment. These links are time-sensitive, expiring after 7 days, and require the invited user to be authenticated and match the identity of the invited user.
- Notifications:
- Acceptance: If the substitute accepts, the
MentorBookingstatus updates toAccepted. The mentee is notified of the mentor change via email, and the titular mentor receives a confirmation. - Rejection: If the substitute rejects, the titular mentor is immediately notified and can then choose to invite another substitute.
- Acceptance: If the substitute accepts, the
Architectural Insights: Building a Secure and Resilient System
Implementing this feature required careful consideration of state management, security, and communication. Here's a look at the key architectural components:
SubstituteStatusEnum: A new PHP enum,SubstituteStatus, was introduced to manage the state of a substitute invitation (pending,accepted,rejected). This enum integrates seamlessly with Filament for intuitive color and label support in the admin interface.MentorSubstituteService: Centralizing complex business logic is crucial. This new service handles the core operations: inviting, accepting, and rejecting substitutes. It encapsulates vital validation rules, such as ensuring only the titular mentor can assign, preventing self-assignment, disallowing assignments to past bookings, and enforcing a one-shot response mechanism.MentorBookingModel Enhancements: TheMentorBookingmodel now includes asubstitute()relation to theUsermodel, along with helper methods likehasAcceptedSubstitute()andcanHaveSubstituteAssigned()to simplify conditional logic throughout the application.- Mailables: Four dedicated mailables (
invitation,accepted,rejected,mentee-notice) ensure clear and timely communication to all parties involved, leveraging Laravel's mailing capabilities. MentorSubstituteController& Signed Routes: A dedicated controller manages the assignment, acceptance, and rejection actions. All invitation response routes are secured using Laravel's signed URL feature, ensuring that only valid, non-tampered requests from the intended user are processed.- Database Migration: A migration (
add_substitute_fields_to_mentor_bookings_table) introduced necessary fields to thementor_bookingstable, includingsubstitute_user_id,substitute_status,substitute_invited_at, andsubstitute_responded_at, along with a composite index for efficient lookups.
Here’s a simplified example of how the MentorSubstituteService might handle an invitation:
namespace App\Services;
use App\Enums\SubstituteStatus;
use App\Models\MentorBooking;
use App\Models\User;
use App\Notifications\MentorSubstituteInvitation;
use Exception;
class MentorSubstituteService
{
public function inviteSubstitute(MentorBooking $booking, User $titular, User $substitute): void
{
// Basic validation for demonstration
if ($booking->mentor_id !== $titular->id) {
throw new Exception("Only titular mentor can assign a substitute.");
}
if ($titular->id === $substitute->id) {
throw new Exception("Mentor cannot substitute themselves.");
}
if ($booking->isPast()) {
throw new Exception("Cannot assign substitute to a past booking.");
}
$booking->substitute_user_id = $substitute->id;
$booking->substitute_status = SubstituteStatus::PENDING;
$booking->substitute_invited_at = now();
$booking->save();
// Notify the substitute with a signed URL
$substitute->notify(new MentorSubstituteInvitation($booking));
}
}
This robust solution ensures that mentorship sessions can continue even when the original mentor is unavailable, significantly enhancing the reliability and value of our program.
Actionable Takeaway
When building complex, multi-step workflows, especially those involving external user interactions, always centralize your business logic in dedicated service classes. This promotes clean code, simplifies validation, and makes your system more maintainable and robust. Additionally, leverage secure, time-bound mechanisms like signed URLs for sensitive actions to prevent unauthorized access and ensure data integrity.