Installation
composer require edouardbob/pipedrive-bundle
Add to config/bundles.php:
return [
// ...
EdouardBob\PipedriveBundle\PipedriveBundle::class => ['all' => true],
];
Configuration Publish the default config:
php artisan vendor:publish --tag=pipedrive-bundle-config
Update .env with your Pipedrive API key and domain:
PIPEDRIVE_API_KEY=your_api_key
PIPEDRIVE_DOMAIN=your_domain
First Sync
Define a syncable entity (e.g., Opportunity) with a PipedriveSyncable trait:
use EdouardBob\PipedriveBundle\Traits\PipedriveSyncable;
class Opportunity
{
use PipedriveSyncable;
// ...
}
Sync a single record:
$opportunity = Opportunity::find(1);
$opportunity->syncToPipedrive();
Key Files to Review
config/pipedrive.php: Field mappings, sync rules.src/Traits/PipedriveSyncable.php: Core sync logic.src/Services/PipedriveClient.php: API interaction layer.Define Mappings
In config/pipedrive.php, map entity fields to Pipedrive custom fields:
'fields' => [
'opportunity' => [
'custom_field_1' => 'entity.field_name',
'custom_field_2' => 'entity->calculatedMethod()',
],
],
Calculate Dynamic Values
Override getPipedriveSyncData() in your entity:
public function getPipedriveSyncData(): array
{
return [
'custom_field_3' => $this->calculateDynamicValue(),
// Inherits mapped fields from config.
];
}
Bulk Sync
Use the PipedriveSyncManager for batch operations:
$manager = app(\EdouardBob\PipedriveBundle\Services\PipedriveSyncManager::class);
$manager->syncCollection(Opportunity::all());
Event Listeners
Trigger syncs on model events (e.g., saved):
class OpportunityObserver
{
public function saved(Opportunity $opportunity)
{
if ($opportunity->isDirty(['field_name'])) {
$opportunity->syncToPipedrive();
}
}
}
Queue Syncs Offload syncs to a queue job:
use EdouardBob\PipedriveBundle\Jobs\SyncToPipedrive;
SyncToPipedrive::dispatch($opportunity);
Webhooks
Use Pipedrive webhooks to update local data when external changes occur. Extend the PipedriveWebhookHandler:
class CustomWebhookHandler extends PipedriveWebhookHandler
{
protected function handleOpportunityUpdate(array $data): void
{
// Custom logic for webhook payloads.
}
}
Field Mapping Conflicts
id, title) or duplicate custom field names.config/pipedrive.php to explicitly exclude reserved fields:
'excluded_fields' => ['id', 'title'],
Rate Limits
PipedriveClient or use chunked queries:
$manager->syncCollection(Opportunity::chunk(50));
Data Type Mismatches
number, date). Laravel’s Carbon or float may cause errors.getPipedriveSyncData():
'custom_field_4' => $this->price->format('Y-m-d'),
Webhook Verification
PipedriveWebhookHandler and validate signatures:
public function verifyWebhook(array $payload, string $signature): bool
{
return hash_equals(
$signature,
hash_hmac('sha256', $payload, config('pipedrive.webhook_secret'))
);
}
Enable Logging
Add to config/pipedrive.php:
'debug' => env('PIPEDRIVE_DEBUG', false),
Logs will appear in storage/logs/pipedrive.log.
Mock API Calls
Use the PipedriveClient interface to mock responses in tests:
$this->app->bind(
\EdouardBob\PipedriveBundle\Contracts\PipedriveClient::class,
function () {
return new class implements PipedriveClient {
public function updateItem($itemId, array $data) {
// Mock response.
}
};
}
);
Custom Sync Logic
Override shouldSyncToPipedrive() to control when syncs occur:
public function shouldSyncToPipedrive(): bool
{
return $this->isActive() && $this->hasChanges();
}
Field Transformers Create a custom transformer for complex fields:
use EdouardBob\PipedriveBundle\Contracts\FieldTransformer;
class AddressTransformer implements FieldTransformer
{
public function transform($value): array
{
return [
'street' => $value->street,
'city' => $value->city,
];
}
}
Register in config/pipedrive.php:
'transformers' => [
'address' => \App\Transformers\AddressTransformer::class,
],
Custom API Endpoints
Extend PipedriveClient to call non-standard endpoints:
class CustomPipedriveClient extends PipedriveClient
{
public function getCustomData(array $params)
{
return $this->request('GET', '/custom_endpoint', $params);
}
}
Bind the extended class in AppServiceProvider.
How can I help you explore Laravel packages today?