Laravel 11 - How to Integrate Signature Pad

Touseef Afridi
30 Oct 24

Laravel 11 - How to Integrate Signature Pad

This tutorial covers integrating a signature pad in Laravel 11, enabling electronic signatures for faster workflows, reduced paperwork, and streamlined approvals.


If you're a video person, feel free to skip the post and check out the video instead!


Step # 1 : Create fresh Laravel project or use existing project.

Two commands to create fresh laravel project
Global Command : laravel new signature
Or use
Non Global Command : composer create-project laravel/laravel --prefer-dist signature

Step # 2 : Access the project.

Open a terminal (e.g., Git Bash) and navigate to your Laravel project's root folder.
Git Bash : cd c:xampp/htdocs/signature

Step # 3 : Install Signpad package.

Run the following command to install the package.
Command : composer require creagia/laravel-sign-pad

Step # 4 : Publish the config, migration & assets.

Use the following command to publish the package configuration & migration.
Command : php artisan sign-pad:install
It will prompt you to run the migrations; type "yes," and you'll see the migration details followed by a prompt to star the repo on GitHub—type "no" to skip.
Use the following command to publish the .js assets.
Command : php artisan vendor:publish --tag=sign-pad-assets

Step # 5 : Update the User Model.

Update the User model with the following changes.
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
// Include signature handling traits and contracts
use Creagia\LaravelSignPad\Concerns\RequiresSignature;
use Creagia\LaravelSignPad\Contracts\CanBeSigned;
// User model implements CanBeSigned for signature capabilities.
class User extends Authenticatable implements CanBeSigned
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    // Uses RequiresSignature for signature handling.
    use HasFactory, Notifiable, RequiresSignature;
    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

Step # 6 : Create and Update route.

Import the UserController class, which we will create in the next step, and include the following routes.
use App\Http\Controllers\UserController;

// Route to display the user creation form.
Route::get('/', [UserController::class, 'create']);
// Route to handle the form submission for creating a user and storing the signature.
Route::post('/confirm', [UserController::class, 'store']);
// Route to display the user's agreement details.
Route::get('/show/{user}', [UserController::class, 'show']);

Step # 7 : Create a controller.

Run the following command to generate the UserController.
Command : php artisan make:controller UserController
Replace the content of UserController.php with the provided code to handle user creation and signature storage.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Creagia\LaravelSignPad\Signature;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class UserController extends Controller
{
    public function create()
    {
        //Return the form view.
        return view('sign');
    }

    public function store(Request $request)
    {
        //Validate the data
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:8',
            'sign' => 'required', 
        ]);

        //Create user with validated Data.
        $user = User::create($validatedData);

        // Process the signature data and save it as a PNG file.
        $signatureData = str_replace('data:image/png;base64,', '', $validatedData['sign']);
        $signatureData = base64_decode($signatureData);
        $imageName = 'signatures/' . Str::uuid() . '.png';
        Storage::disk('public')->put($imageName, $signatureData);

        // Create and save the signature associated with the user.
        $signature = new Signature();
        $signature->model_type = User::class; 
        $signature->model_id = $user->id;
        $signature->uuid = Str::uuid();
        $signature->filename = $imageName;
        $signature->document_filename = null;
        $signature->certified = false;
        $signature->from_ips = json_encode([request()->ip()]);
        $signature->save();

        // Redirect back with success message.
        return redirect()->back()->with('success', 'Employment Agreement submited successfully!');
    }

    public function show(User $user)
    {
        // Load the user's signature relationship.
        $user->load('signature');

        // Return the view to display the user's details and signature.
        return view('show', compact('user'));
    }

}

Step # 8 : Create a storage link.

Run the command to create a symbolic link from the public directory to the storage directory.
Command: php artisan storage:link

Step # 9 : Create Views.

Create a view named sign.blade.php that contains a form for creating a user with an employment agreement.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Code Shotcut - Employment Agreement</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    .sign-pad-button-submit {
      margin-top: 5px;
      background-color: #3b82f6;
      color: white;
      font-weight: bold;
      padding: 0.5rem 1rem;
      border-radius: 0.5rem;
      transition: background-color 0.3s ease;
    }
    .sign-pad-button-submit:hover {
      background-color: #2563eb;
    }
    .sign-pad-button-clear {
      margin-top: 5px;
      background-color: #ef4444;
      color: white;
      font-weight: bold;
      padding: 0.5rem 1rem;
      border-radius: 0.5rem;
      transition: background-color 0.3s ease;
    }
    .sign-pad-button-clear:hover {
      background-color: #dc2626;
    }
  </style>
</head>
<body class="bg-gray-50 min-h-screen flex items-center justify-center">
  <form action="/confirm" method="POST" class="bg-white p-10 rounded-lg shadow-lg max-w-xl w-full">
    @csrf
    <h1 class="text-2xl font-bold text-center mb-4">Code Shotcut - Employment Agreement</h1>
    <p class="mb-4 text-justify text-gray-600">
      Please fill in your details and sign below to confirm your acceptance of the employment terms.
    </p>
    <!-- Display Success Message -->
    @if (session('success'))
    <div class="bg-green-100 text-green-700 p-4 rounded mb-4">
      {{ session('success') }}
    </div>
    @endif
    <!-- Display Error Messages -->
    @if ($errors->any())
    <div class="bg-red-100 text-red-700 p-4 rounded mb-4">
      <ul>
        @foreach ($errors->all() as $error)
        <li>{{ $error }}</li>
        @endforeach
      </ul>
    </div>
    @endif
    <div class="mb-4">
      <label for="name" class="block text-lg text-gray-700 font-medium mb-1">Full Name</label>
      <input type="text" id="name" name="name"
      class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
      placeholder="Enter your full name" required />
    </div>
    <div class="mb-4">
      <label for="email" class="block text-lg text-gray-700 font-medium mb-1">Email Address</label>
      <input type="email" id="email" name="email"
      class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
      placeholder="Enter your email" required />
    </div>
    <div class="mb-4">
      <label for="password" class="block text-lg text-gray-700 font-medium mb-1">Password</label>
      <input type="password" id="password" name="password"
      class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
      placeholder="Create a password" required />
    </div>
    <!-- Signature Pad -->
    <div class="mb-4">
      <label class="block text-lg text-gray-700 font-medium mb-2">Signature</label>
      <x-creagia-signature-pad name='sign' />
    </div>
  </form>
  <!-- Sign-pad js -->
  <script src="{{ asset('vendor/sign-pad/sign-pad.min.js') }}"></script>
</body>
</html>
Create an additional view named show.blade.php to display the user's agreement details.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Code Shotcut - User Agreement</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-50 min-h-screen flex items-center justify-center">
    <div class="bg-white p-10 rounded-lg shadow-lg max-w-md w-full">
        <h1 class="text-2xl font-bold text-center mb-4">User Details</h1>
        <p><strong>Name:</strong> {{ $user->name }}</p>
        <p><strong>Email:</strong> {{ $user->email }}</p>
        <h2 class="mt-4 mb-2 text-lg font-medium">Signature</h2>
        @if ($user->signature)
            <img src="{{ asset('storage/' . $user->signature->filename) }}" alt="User Signature" class="w-full h-auto">
        @else
            <p>No signature available.</p>
        @endif
    </div>
</body>
</html>

Step # 10 : It's time to test.

Start the Laravel development server.
Command : php artisan serve.
Open your web browser and navigate to the following URL.
127.0.0.1:8000

Complete the form and submit it.

To view the agreement, use the following link, replacing $id with the appropriate user ID
127/0.0.1/show/$id

For more details please refer to the package documentation.

Share this with friends!


"Give this post some love and slap that 💖 button as if it owes you money! 💸😄"
0

0 Comments

To engage in commentary, kindly proceed by logging in or registering