|
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. |
0 commit comments