Skip to content

Commit bf68c71

Browse files
authored
Merge pull request #1 from wazzac/alpha/develop
alpha/develop: Initial Alpha development for this package.
2 parents a391c6a + c337d9e commit bf68c71

File tree

14 files changed

+11960
-2
lines changed

14 files changed

+11960
-2
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor
2+
/.vscode

README.md

Lines changed: 289 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,289 @@
1-
# sync-model-to-hubspot
2-
Use event queues (...or immediate) to push any model data changes to HubSpot (CRM). i.e. `user.email` change to `hubspot.contact.email` property update.
1+
<p align="center">
2+
<a href="https://github.com/wazzac/sync-model-to-crm/issues"><img alt="GitHub issues" src="https://img.shields.io/github/issues/wazzac/sync-model-to-crm"></a>
3+
<a href="https://github.com/wazzac/sync-model-to-crm/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/wazzac/sync-model-to-crm"></a>
4+
<a href="https://github.com/wazzac/sync-model-to-crm/blob/main/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/wazzac/sync-model-to-crm"></a>
5+
</p>
6+
7+
# Synchronize a Model to a Remote Crm Object
8+
9+
A library that will syncronise any defined database table properties (inside the Model) to an external Crm provider, like [HubSpot](https://www.hubspot.com/), [Pipedrive](https://www.pipedrive.com/en) and more.
10+
11+
## Overview
12+
13+
The idea around this library is to make it very easy for a developer to define inside each applicable Model (like `User`, `Entity`, `Order`, etc.) which properties should syncronize to which CRM Provider and Environment.
14+
15+
After each first time successful sync, the CRM Object primary key will be stored in a mapping table against the local table primary key. This allows for quicker loading times for future changes.
16+
17+
Update your Model with 4 properties that define the rules for 3rd-party CRM synchronization:
18+
- @var string|array|null `$syncModelCrmEnvironment`; *Required
19+
- @var array `$syncModelCrmPropertyMapping`; *Required
20+
- @var array `$syncModelCrmUniqueSearch`;
21+
- @var string `$syncModelCrmRelatedObject`;
22+
- @var array `$syncModelCrmDeleteRules`;
23+
- @var array `$syncModelCrmActiveRules`;
24+
- @var array `$syncModelCrmAssociateRules`;
25+
26+
Looking at the below example:
27+
1. the `User` Model will syncronize to both the `Sandbox` and `Production` **HubSpot** environments _($syncModelCrmEnvironment)_.
28+
2. It will only syncronize the `name` and `email` properties to the HubSpot corresponding `firstname` and `email` fields _($syncModelCrmPropertyMapping)_.
29+
3. When there is no internal mapping yet stored, the CRM record will be uniquely loaded using the `email` property _($syncModelCrmUniqueSearch)_.
30+
4. In order for the script to know which remote CRM object relates to the User model, `contact` _($syncModelCrmRelatedObject)_ have to be defined as the remote item.
31+
5. The _($syncModelCrmDeleteRules)_ property is used to instruct the Crm what action to take when a local record is deleted/removed. For example, when _SoftDeletes_ are enabled locally, the crm will use the `soft_delete` rules to update the crm records or alternatively Archive the record in the crm.
32+
6. The reverse to the above, _($syncModelCrmActiveRules)_ will be used to define the action that will be taken when deleted records are activated again.
33+
7. Finally, the non-required _($syncModelCrmAssociateRules)_ property is used to define the relationship (associations) between objects. e.g. `user` to `entity`.
34+
35+
```PHP
36+
class User extends Authenticatable
37+
{
38+
// .
39+
// ..
40+
// ... original Model content above.
41+
42+
// --------------------------------------------------------------
43+
// Sync Model to CRM
44+
// --------------------------------------------------------------
45+
46+
/**
47+
* The CRM provider environment/s to use (e.g. production, sandbox, etc.)
48+
* Use an array to sync to multiple environments.
49+
* `null` will take the default defined value from the config file.
50+
*
51+
* @var string|array|null
52+
*/
53+
public $syncModelCrmEnvironment = ['sandbox', 'production'];
54+
55+
/**
56+
* Mapping array for local and CRM properties
57+
* This will be the primary property used to cycle through the crm providers
58+
*
59+
* @var array
60+
*/
61+
public $syncModelCrmPropertyMapping = [
62+
'hubspot' => [
63+
'name' => 'firstname',
64+
'email' => 'email',
65+
],
66+
];
67+
68+
/**
69+
* Unique filters for the CRM to locate the record if there is no internal mapping available.
70+
*
71+
* @var array
72+
*/
73+
public $syncModelCrmUniqueSearch = [
74+
'hubspot' => [
75+
'email' => 'email',
76+
],
77+
];
78+
79+
/**
80+
* The CRM object to sync this model to.
81+
* This is the CRM object type (e.g. contact, company, deal, etc.)
82+
* If this is null or not provided, the `object_table_mapping` key will be used from the config file.
83+
*
84+
* @var string
85+
*/
86+
public $syncModelCrmRelatedObject = 'contact';
87+
88+
/**
89+
* The Crm Delete rules to follow.
90+
* i.e. if Soft-delete is applicable, what should the CRM record be updated to?
91+
* if Hard-delete is used, the record will be deleted/archived in the CRM.
92+
*
93+
* @var array
94+
*/
95+
public $syncModelCrmDeleteRules = [
96+
'hard_delete' => [
97+
'hubspot' => false,
98+
],
99+
'soft_delete' => [
100+
'hubspot' => [
101+
'lifecyclestage' => 'other',
102+
'hs_lead_status' => 'DELETED',
103+
],
104+
]
105+
];
106+
107+
/**
108+
* The Crm Active/Restore rules to follow.
109+
* These will be the rules to follow for any new entries that are not soft-deleted.
110+
*/
111+
public $syncModelCrmActiveRules = [
112+
'hubspot' => [
113+
'lifecyclestage' => 'customer',
114+
'hs_lead_status' => 'OPEN',
115+
],
116+
];
117+
118+
/**
119+
* The Crm Associations to sync.
120+
* This is used to associate the model with other CRM objects.
121+
*
122+
* @var array
123+
*/
124+
public $syncModelCrmAssociateRules = [
125+
[
126+
'assocMethod' => 'entity', // App\Models\Entity::class
127+
'provider' => [
128+
'hubspot' => [
129+
[
130+
'association_category' => HubSpotController::ASSOCIATION_CATEGORY__HUBSPOT_DEFINED,
131+
'association_type_id' => HubSpotController::ASSOCIATION_TYPE_ID__CONTACT_TO_COMPANY_PRIMARY,
132+
],
133+
[
134+
'association_category' => HubSpotController::ASSOCIATION_CATEGORY__HUBSPOT_DEFINED,
135+
'association_type_id' => HubSpotController::ASSOCIATION_TYPE_ID__CONTACT_TO_COMPANY,
136+
],
137+
],
138+
],
139+
],
140+
];
141+
}
142+
```
143+
144+
## Usage
145+
146+
The are primarily 2 methods that you can use to initiate a Model sync.
147+
148+
Executing `(new CrmController())->setModel($user)->execute();`:
149+
1. Directly in a controller action.
150+
2. Via a Observer. e.g. inside a UserObserver to trigger after a save() event. (see below)
151+
```PHP
152+
class UserObserver implements ShouldHandleEventsAfterCommit
153+
{
154+
/**
155+
* Handle the User "created" event.
156+
*/
157+
public function created(User $user): void
158+
{
159+
echo ('create...');
160+
(new CrmController())->setModel($user)->execute(CrmController::EXEC_ACTION_CREATE);
161+
echo ('created...');
162+
}
163+
164+
/**
165+
* Handle the User "updated" event.
166+
*/
167+
public function updated(User $user): void
168+
{
169+
echo ('update...');
170+
(new CrmController())
171+
->setModel($user)
172+
->execute(CrmController::EXEC_ACTION_UPDATE, true);
173+
echo ('updated...');
174+
}
175+
176+
/**
177+
* Handle the User "deleted" event.
178+
* Run when a user is soft-deleted.
179+
*/
180+
public function deleted(User $user)
181+
{
182+
echo ('delete...');
183+
(new CrmController())->setModel($user)->execute(CrmController::EXEC_ACTION_DELETE);
184+
echo ('deleted...');
185+
}
186+
187+
/**
188+
* Handle the User "restored" event.
189+
* Soft-delete has been reversed.
190+
*/
191+
public function restored(User $user): void
192+
{
193+
echo ('restore...');
194+
(new CrmController())->setModel($user)->execute(CrmController::EXEC_ACTION_RESTORE);
195+
echo ('restored...');
196+
}
197+
198+
/**
199+
* Handle the User "force deleted" event.
200+
*/
201+
public function forceDeleted(User $user): void
202+
{
203+
echo ('forceDeleted...');
204+
}
205+
206+
/**
207+
* Handle the User "saved" event.
208+
*
209+
*/
210+
public function saved(User $user): void
211+
{
212+
echo ('saving...');
213+
}
214+
}
215+
```
216+
3. Inside an event job. This is a good method to separate the logic from the save event and put the sync in a job queue to be processed shortly after the record has been saved.
217+
218+
## Installation
219+
220+
> PHP 8.1 is a minimum requirement for this project.
221+
222+
1. Open terminal and require the package:
223+
224+
```bash
225+
composer require wazza/sync-model-to-crm
226+
```
227+
228+
2. Navigate to the `config` directory within your Laravel project and open the `app.php` file.
229+
230+
- Look for the `providers` array within the `app.php` file.
231+
- Inside the `providers` array, add the following line and save: `Wazza\SyncModelToCrm\Providers\SyncModelToCrmServiceProvider::class,` see example below:
232+
233+
```php
234+
'providers' => ServiceProvider::defaultProviders()->merge([
235+
/*
236+
* Package Service Providers...
237+
*/
238+
Wazza\SyncModelToCrm\Providers\SyncModelToCrmServiceProvider::class, // <-- here
239+
Wazza\DomTranslate\Providers\DomTranslateServiceProvider::class,
240+
241+
/*
242+
* Application Service Providers...
243+
*/
244+
App\Providers\AppServiceProvider::class,
245+
App\Providers\AuthServiceProvider::class,
246+
// App\Providers\BroadcastServiceProvider::class,
247+
App\Providers\EventServiceProvider::class,
248+
App\Providers\RouteServiceProvider::class,
249+
])->toArray(),
250+
251+
```
252+
253+
3. In your terminal again, complete the setup by running the below commands:
254+
255+
```bash
256+
php artisan vendor:publish --tag="sync-modeltocrm-config"
257+
php artisan vendor:publish --tag="sync-modeltocrm-migrations"
258+
php artisan migrate
259+
```
260+
261+
4. Below are some of the environment keys that can be added to your _.env_ configuration. If you need more information what each item does, refer to the `config/sync_modeltocrm.php` config file.
262+
```
263+
SYNC_MODEL_TO_CRM_HASH_SALT=Ey4cw2BHvi0HmGYjyqYr
264+
SYNC_MODEL_TO_CRM_HASH_ALGO=sha256
265+
SYNC_MODEL_TO_CRM_LOG_INDICATOR=sync-modeltocrm
266+
SYNC_MODEL_TO_CRM_LOG_LEVEL=3
267+
SYNC_MODEL_TO_CRM_PROVIDER=hubspot
268+
SYNC_MODEL_TO_CRM_ENVIRONMENT=sandbox
269+
SYNC_MODEL_TO_CRM_PROVIDER_HUBSPOT_SANDBOX_URI=https://api.hubapi.com/crm/v4/
270+
SYNC_MODEL_TO_CRM_PROVIDER_HUBSPOT_SANDBOX_TOKEN=xxx-xxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
271+
```
272+
273+
5. Run `config:cache` after you have made your env changes.
274+
275+
```bash
276+
php artisan config:cache
277+
```
278+
279+
6. **Done**. Review any configuration file changes that you might want to change. The config file was published to the main config folder.
280+
281+
## Looking at the logs
282+
283+
Depending on your log level (refer to your config file settings: 1:high to 3:low) less or more information will be written to your Laravel log file.
284+
285+
You can track the transactions by running `tail -f {log path}` or even including `grep` with the unique transaction 8 digit code.
286+
287+
## Testing
288+
289+
... more details to follow.

composer.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "wazza/sync-model-to-crm",
3+
"description": "Helper package to simplify Laravel Model (e.g. `User`, `Entity`, etc.) data synchronization to external CRM object (e.g. HubSpot `Contact`, `Company`, etc.)",
4+
"type": "library",
5+
"require": {
6+
"php": "^8.1",
7+
"ext-json": "*",
8+
"illuminate/support": "~10",
9+
"guzzlehttp/guzzle": "^7.4",
10+
"hubspot/api-client": "^10.1"
11+
},
12+
"require-dev": {
13+
"fakerphp/faker": "^1.20.0",
14+
"phpunit/phpunit": "~10",
15+
"orchestra/testbench": "~8",
16+
"mockery/mockery": "^1.2",
17+
"laravel/legacy-factories": "~1"
18+
},
19+
"license": "MIT",
20+
"autoload": {
21+
"psr-4": {
22+
"Wazza\\SyncModelToCrm\\": "src/"
23+
},
24+
"files": []
25+
},
26+
"autoload-dev": {
27+
"psr-4": {
28+
"Wazza\\SyncModelToCrm\\": "src/",
29+
"Wazza\\SyncModelToCrm\\Tests\\": "tests/"
30+
}
31+
},
32+
"exstra": {
33+
"laravel": {
34+
"providers": [
35+
"Wazza\\SyncModelToCrm\\Providers\\SyncModelToCrmServiceProvider"
36+
],
37+
"aliases": {
38+
"SyncModelToCrm": "Wazza\\SyncModelToCrm\\Facades\\SyncModelToCrm"
39+
}
40+
}
41+
},
42+
"authors": [{
43+
"name": "Warren Coetzee",
44+
"email": "[email protected]",
45+
"homepage": "https://www.wazzac.dev"
46+
}],
47+
"scripts": {
48+
"post-autoload-dump": [
49+
"@php ./vendor/bin/testbench package:discover --ansi"
50+
]
51+
},
52+
"minimum-stability": "stable"
53+
}

0 commit comments

Comments
 (0)