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:

  1. 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.
  2. 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.
  3. Notifications:
    • Acceptance: If the substitute accepts, the MentorBooking status updates to Accepted. 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.

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:

  • SubstituteStatus Enum: 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.
  • MentorBooking Model Enhancements: The MentorBooking model now includes a substitute() relation to the User model, along with helper methods like hasAcceptedSubstitute() and canHaveSubstituteAssigned() 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 the mentor_bookings table, including substitute_user_id, substitute_status, substitute_invited_at, and substitute_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.

Streamlining Mentorship: Empowering Substitutes for Uninterrupted Sessions
GERARDO RUIZ

GERARDO RUIZ

Author

Share: