Filament - How to generate Terms and Conditions PDF and send it to user after consent

Julien BOYER
Julien BOYER (aka. yebor974) Buy Me A Coffee
Published at 22 Aug 2024
This article describes how you can generate PDF of your terms and conditions when published and sent it to user on consent action.

This article uses the Filament Terms Guard Plugin and requires a minimum version of ^1.1.1.

  1. Plugin Installation and Configuration

To install the plugin, follow the official documentation.

For this article, we are using two panels identified by the IDs admin and member.

  • The admin panel is an administration panel where we enable the plugin with the TermResource to manage the terms and conditions:
use Yebor974\FilamentTermsGuard\TermsGuardPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            TermsGuardPlugin::make()
                ->termsResource()
        ])
}
  • The member panel is intended for the users of our site where we require the user to consent to our terms and conditions.
use Yebor974\FilamentTermsGuard\TermsGuardPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            TermsGuardPlugin::make()
        ])
}

We add the following key in the .env file to enable the terms and conditions check for the logged-in user:

TERMS_GUARD_PANELS=member
  1. Publishing Our Terms and Conditions and Generating the Associated PDF

In the admin panel, we can create, modify, and publish the terms and conditions:

Index of terms and conditions view

When the publish action is triggered, the TermPublished event is emitted. We can associate a listener to this event for additional processing (in this case, generating the PDF associated with our published terms and conditions).

To create the GenerateTermPDF listener linked to the TermPublished event:

php artisan make:listener GenerateTermPDF --event=\\Yebor974\\FilamentTermsGuard\\Events\\TermPublished

Link these two elements in the EventServiceProvider:

use App\Listeners\GenerateTermPDF;
use Yebor974\FilamentTermsGuard\Events\TermPublished;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        // ...
        TermPublished::class => [
            GenerateTermPDF::class
        ],
    ];
	
	  //...
}

You can then manage the PDF generation in the handle method of your listener:

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Yebor974\FilamentTermsGuard\Events\TermPublished;

class GenerateTermPDF
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(TermPublished $event): void
    {
        // $event->term;
        if(!Storage::directoryExists('cgu')) {
            Storage::makeDirectory('cgu');
        }

        Pdf::html("<div class='prose'>{$event->term->content}</div>")
            ->format(Format::A4)
            ->margins(10, 0, 15, 0)
            ->save(storage_path("app/cgu/{$event->term->id}.pdf"));
    }
}

I generally use Spatie's Laravel-PDF plugin to generate this PDF official documentation. The previous example is basic and does not include a document title or CSS theme. You can refer to the Laravel-PDF documentation for more customization.

  1. Member Consent and Sending the Accepted Terms by Email

As before, you can create a SendTermByMail listener to send the accepted terms by email to the user. The plugin emits the TermAccepted event for this purpose.

First, create an email notification named SendAcceptedTerm:

php artisan make:notification SendAcceptedTerm
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Yebor974\FilamentTermsGuard\Models\Term;

class SendAcceptedTerm extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     */
    public function __construct(protected Term $term)
    {
        $this->afterCommit();
    }

    /**
     * Get the notification's delivery channels.
     *
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     */
    public function toMail(object $notifiable): MailMessage
    {
        return (new MailMessage)
                    ->subject("New Terms and Conditions")
                    ->line('You have accepted the new terms and conditions of the platform.')
                    ->line('Please find them attached!')
                    ->attach(storage_path("app/cgu/{$this->term->id}.pdf"), [ // here we add attachment and rename sending file
                        'as' => "Terms and Conditions - {$this->term->name}.pdf",
                        'mime' => 'application/pdf',
                    ]);
    }

    // ...
}

Then, create the SendTermByMail listener linked to the TermAccepted event:

php artisan make:listener SendTermByMail --event=\\Yebor974\\FilamentTermsGuard\\Events\\TermAccepted

Link these two elements in the EventServiceProvider:

use App\Listeners\SendTermByMail;
use Yebor974\FilamentTermsGuard\Events\TermAccepted;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        // ...
        TermAccepted::class => [
            SendTermByMail::class
        ],
    ];
	
	  //...
}

You can then manage sending an email to the user who accepted the terms with the associated document in the handle method of your listener:

use App\Notifications\SendAcceptedTerm;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Yebor974\FilamentTermsGuard\Events\TermAccepted;

class SendTermByMail
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(TermAccepted $event): void
    {
        // $event->user
        // $event->term
        $event->user->notify(new SendAcceptedTerm($event->term));
    }
}

Other posts

Plugins

To contact me

If you have an idea to bring to life, a problem to solve, or a significant technical challenge to tackle, don't waste any more time. Contact me now and let me help you turn your ambitions into reality. My expertise is at your disposal to successfully materialize your projects.

Geographic location

Paris & Reunion Island (France - UTC+4)