formpanel-laravel maintained by formpanel
FormPanel Laravel
Laravel integration for the FormPanel PHP client library. This package provides a seamless way to submit forms to FormPanel API from your Laravel applications with configuration, facades, and artisan commands.
Requirements
- PHP 8.2 or higher
- Laravel 11.x or 12.x
- Composer
Installation
Install the package via Composer:
composer require formpanel/formpanel-laravel
The service provider will be automatically registered via Laravel's package auto-discovery.
Configuration
Publish Configuration
Publish the configuration file:
php artisan vendor:publish --tag=formpanel-config
This will create a config/formpanel.php file in your application.
Environment Variables
Add the following to your .env file:
FORMPANEL_API_KEY=your-api-key-here
FORMPANEL_FORM_SLUG=contact-form
Required:
FORMPANEL_API_KEY- Your FormPanel API key
Optional:
FORMPANEL_FORM_SLUG- Default form slugFORMPANEL_BASE_URL- Base URL (default: https://api.formpanel.io/api/v1)FORMPANEL_TIMEOUT- Request timeout in seconds (default: 30)FORMPANEL_MAX_RETRIES- Maximum retry attempts (default: 3)FORMPANEL_LOGGING- Enable debug logging (default: false)
Usage
Using the Facade
The package provides a convenient facade for accessing the FormPanel client:
use Formpanel\Laravel\Facades\Formpanel;
// Submit form data using the default form slug from config
$result = Formpanel::submit([
'name' => 'John Doe',
'email' => 'john@example.com',
'message' => 'Hello from Laravel!',
]);
echo "Submission ID: " . $result->id;
Using a Specific Form
Create a client for a specific form using the form() method:
use Formpanel\Laravel\Facades\Formpanel;
// Get a client for a specific form
$client = Formpanel::form('contact-form');
// Submit data
$result = $client->submit([
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'message' => 'Hello!',
]);
// Get form information
$form = $client->get();
echo "Form: {$form->name}";
echo "Fields: " . count($form->fields);
Using Dependency Injection
Inject the client into your controllers, jobs, or services:
use Formpanel\FormClient;
class ContactController extends Controller
{
public function __construct(protected FormClient $formpanel)
{
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string',
'email' => 'required|email',
'message' => 'required|string',
]);
try {
$result = $this->formpanel->submit($validated);
return redirect()->back()->with('success', 'Form submitted successfully!');
} catch (\Formpanel\Exceptions\ValidationException $e) {
return redirect()->back()->withErrors($e->getErrors());
}
}
}
Using the Factory for Multiple Forms
Use the factory to create clients for different forms:
use Formpanel\Laravel\FormpanelFactory;
class FormController extends Controller
{
public function __construct(protected FormpanelFactory $formpanel)
{
}
public function submitContact(Request $request)
{
$client = $this->formpanel->make('contact-form');
$result = $client->submit($request->validated());
return redirect()->back()->with('success', 'Contact form submitted!');
}
public function submitNewsletter(Request $request)
{
$client = $this->formpanel->make('newsletter-signup');
$result = $client->submit(['email' => $request->email]);
return redirect()->back()->with('success', 'Subscribed to newsletter!');
}
}
Getting Form Information
Retrieve form details including field definitions:
use Formpanel\Laravel\Facades\Formpanel;
$form = Formpanel::get();
echo "Form: {$form->name}\n";
echo "Status: {$form->status}\n";
echo "Description: {$form->description}\n";
foreach ($form->fields as $field) {
echo "- {$field->name} ({$field->type})";
echo $field->required ? " [required]" : "";
echo "\n";
}
Controller Example
Complete controller example with error handling:
<?php
namespace App\Http\Controllers;
use Formpanel\FormClient;
use Formpanel\Exceptions\ValidationException;
use Formpanel\Exceptions\FormpanelException;
use Illuminate\Http\Request;
class ContactController extends Controller
{
public function __construct(protected FormClient $formpanel)
{
}
public function create()
{
return view('contact');
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email',
'subject' => 'required|string|max:255',
'message' => 'required|string',
]);
try {
$result = $this->formpanel->submit($validated);
return redirect()
->route('contact.thank-you')
->with('submission_id', $result->id);
} catch (ValidationException $e) {
// FormPanel validation failed
return redirect()
->back()
->withInput()
->withErrors($e->getErrors());
} catch (FormpanelException $e) {
// Other FormPanel errors
return redirect()
->back()
->withInput()
->with('error', 'Failed to submit form: ' . $e->getMessage());
}
}
}
Queue Integration
Submit forms asynchronously using Laravel queues:
<?php
namespace App\Jobs;
use Formpanel\Laravel\FormpanelFactory;
use Formpanel\Exceptions\FormpanelException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class SubmitFormToFormpanel implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
protected string $formSlug,
protected array $data
) {
}
public function handle(FormpanelFactory $factory): void
{
try {
$client = $factory->make($this->formSlug);
$result = $client->submit($this->data);
Log::info('Form submitted to FormPanel', [
'submission_id' => $result->id,
'form_slug' => $this->formSlug,
]);
} catch (FormpanelException $e) {
Log::error('Failed to submit form to FormPanel', [
'error' => $e->getMessage(),
'form_slug' => $this->formSlug,
]);
throw $e;
}
}
}
Dispatch the job:
use App\Jobs\SubmitFormToFormpanel;
SubmitFormToFormpanel::dispatch('contact-form', [
'name' => 'John Doe',
'email' => 'john@example.com',
]);
Artisan Commands
Test API Connection
Test your FormPanel API integration:
php artisan formpanel:test contact-form \
--field=name="John Doe" \
--field=email="john@example.com" \
--field=message="Test message"
Output:
Testing FormPanel API integration...
Form: contact-form
Data:
name: John Doe
email: john@example.com
message: Test message
Form submitted successfully!
Response:
Submission ID: 550e8400-e29b-41d4-a716-446655440000
Message: Form submitted successfully!
Error Handling
The package uses the same exception classes as the base PHP library:
use Formpanel\Laravel\Facades\Formpanel;
use Formpanel\Exceptions\AuthenticationException;
use Formpanel\Exceptions\ValidationException;
use Formpanel\Exceptions\NotFoundException;
use Formpanel\Exceptions\RateLimitException;
use Formpanel\Exceptions\FormpanelException;
try {
$result = Formpanel::submit($data);
} catch (AuthenticationException $e) {
// Invalid API key (401)
Log::error('FormPanel authentication failed', ['error' => $e->getMessage()]);
} catch (NotFoundException $e) {
// Form not found (404)
Log::warning('FormPanel form not found', ['error' => $e->getMessage()]);
} catch (ValidationException $e) {
// Validation errors (422)
$errors = $e->getErrors();
return back()->withErrors($errors);
} catch (RateLimitException $e) {
// Rate limit exceeded (429)
$retryAfter = $e->getRetryAfter();
Log::warning('FormPanel rate limit exceeded', ['retry_after' => $retryAfter]);
} catch (FormpanelException $e) {
// Other errors (network issues, parsing errors, etc.)
Log::error('FormPanel error', ['error' => $e->getMessage()]);
}
Response Objects
SubmissionResult
Returned by submit():
$result = Formpanel::submit($data);
$result->id; // string - Submission UUID
$result->success; // bool - Whether submission was successful
$result->message; // string - Success/error message
Form
Returned by get():
$form = Formpanel::get();
$form->id; // string - Form UUID
$form->name; // string - Form name
$form->slug; // string - Form slug
$form->description; // ?string - Form description
$form->status; // string - Form status (e.g., 'active')
$form->fields; // FormField[] - Array of field definitions
$form->createdAt; // DateTimeImmutable
$form->updatedAt; // DateTimeImmutable
FormField
foreach ($form->fields as $field) {
$field->name; // string - Field name/identifier
$field->label; // string - Display label
$field->type; // string - Field type
$field->required; // bool - Whether field is required
$field->placeholder; // ?string - Placeholder text
$field->helpText; // ?string - Help text
$field->config; // array - Additional configuration
}
Advanced Configuration
Custom Retry Configuration
Configure retry behavior in your .env:
FORMPANEL_MAX_RETRIES=5
FORMPANEL_RETRY_BASE_DELAY=500
FORMPANEL_RETRY_MAX_DELAY=60000
FORMPANEL_RETRY_MULTIPLIER=2.0
Or in config/formpanel.php:
'retry' => [
'max_retries' => 5,
'base_delay_ms' => 500,
'max_delay_ms' => 60000,
'multiplier' => 2.0,
],
Logging
Enable request/response logging for debugging:
FORMPANEL_LOGGING=true
FORMPANEL_LOG_CHANNEL=stack
When enabled, the package will use Laravel's logging system with PSR-3 compatibility.
Testing
Mock in Tests
Use Laravel's service container to mock the client in tests:
use Formpanel\FormClient;
use Formpanel\SubmissionResult;
use Mockery;
public function test_contact_form_submission()
{
$mockClient = Mockery::mock(FormClient::class);
$mockClient->shouldReceive('submit')
->once()
->with(Mockery::any())
->andReturn(new SubmissionResult(
id: '550e8400-e29b-41d4-a716-446655440000',
success: true,
message: 'Form submitted successfully!',
));
$this->app->instance(FormClient::class, $mockClient);
$response = $this->post('/contact', [
'name' => 'John Doe',
'email' => 'john@example.com',
'message' => 'Test message',
]);
$response->assertRedirect();
}
Using Custom HTTP Client
For integration testing, you can inject a custom HTTP client:
use Formpanel\FormClient;
use Formpanel\Http\HttpClientInterface;
$mockHttpClient = new class implements HttpClientInterface {
public function get(string $uri): array {
return ['id' => 'test', 'name' => 'Test Form', /* ... */];
}
public function post(string $uri, array $data): array {
return ['submission_id' => 'test-123', 'success' => true, 'message' => 'OK'];
}
};
$client = new FormClient(
apiKey: 'test-key',
formSlug: 'test-form',
httpClient: $mockHttpClient,
);
Run Package Tests
composer install
./vendor/bin/phpunit
Security
- Store API keys in
.envfile, never in code - Use Laravel's validation before submitting to FormPanel
- Enable HTTPS in production
- The client includes automatic retry with exponential backoff for transient errors
Support
- GitHub: github.com/formpanel/formpanel-laravel
- Base Library: github.com/formpanel/formpanel-php
- Documentation: formpanel.com/docs
- Email: support@formpanel.com
License
MIT License. See LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.