From 477f9754256c692df09130fa8a56ae486bdc4b2f Mon Sep 17 00:00:00 2001 From: Julien LIBERT Date: Wed, 28 Aug 2024 11:39:12 +0200 Subject: [PATCH 1/2] Add detach association --- src/Api/Association.php | 13 ++++++++++++- src/Api/Builder.php | 13 ++++++++++++- src/Api/Model.php | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Api/Association.php b/src/Api/Association.php index 8c087ee..493d6ed 100644 --- a/src/Api/Association.php +++ b/src/Api/Association.php @@ -51,6 +51,17 @@ public function attach($targetId) ); } + public function detach($targetId) + { + if ($targetId instanceof Model) { + $targetId = $targetId->id; + } + + $this->sourceBuilder()->deleteAssociation( + $this->target, $targetId + ); + } + public function sourceBuilder(): Builder { return $this->source->builder(); @@ -65,4 +76,4 @@ public function __call($method, $parameters) { return $this->forwardCallTo($this->builder(), $method, $parameters); } -} \ No newline at end of file +} diff --git a/src/Api/Builder.php b/src/Api/Builder.php index c359d7c..de85fa1 100644 --- a/src/Api/Builder.php +++ b/src/Api/Builder.php @@ -340,6 +340,17 @@ public function associate(Model $target, $targetId) )->json(); } + public function deleteAssociation(Model $target, $targetId) + { + return $this->client()->delete( + $this->object->endpoint('associate', [ + 'association' => $target->type(), + 'associationId' => $targetId, + 'associationType' => Str::singular($this->object->type()) . "_to_" . Str::singular($target->type()) + ]) + )->json(); + } + public function client(): Client { return $this->client; @@ -408,4 +419,4 @@ public function __call($method, $parameters) 'Call to undefined method %s::%s()', static::class, $method )); } -} \ No newline at end of file +} diff --git a/src/Api/Model.php b/src/Api/Model.php index da4e50d..846a2fa 100644 --- a/src/Api/Model.php +++ b/src/Api/Model.php @@ -93,7 +93,7 @@ public function fill(array $properties): static private function isAllowedProperty(string $key): bool { - return $key === 'email' || + return $key === 'email' || !(HubSpot::isType($key) || HubSpot::isType(Str::plural($key))); } From a97e11a9501977502789bea95c84b87b5931790a Mon Sep 17 00:00:00 2001 From: Julien LIBERT Date: Wed, 28 Aug 2024 17:34:38 +0200 Subject: [PATCH 2/2] Update README with an example for detach method. --- README.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3535522..ed3759b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Interact with HubSpot's CRM with an enjoyable, Eloquent-like developer experienc - Cursors provide a seamless way to loop through all records: `foreach(Contact::cursor() AS $contact) { ... }` > **Note** -> Only the CRM API is currently implemented. +> Only the CRM API is currently implemented. ## Installation @@ -24,7 +24,7 @@ composer require stechstudio/laravel-hubspot ### 2) Configure HubSpot -[Create a private HubSpot app](https://developers.hubspot.com/docs/api/private-apps#create-a-private-app) and give it appropriate scopes for what you want to do with this SDK. +[Create a private HubSpot app](https://developers.hubspot.com/docs/api/private-apps#create-a-private-app) and give it appropriate scopes for what you want to do with this SDK. Copy the provided access token, and add to your Laravel `.env` file: @@ -102,8 +102,8 @@ This package provides three different ways of fetching these results. #### Paginating -Similar to a traditional database paginated result, you can paginate through a HubSpot collection of objects. -You will receive a `LengthAwarePaginator` just like with Eloquent, which means you can generate links in your UI just like you are used to. +Similar to a traditional database paginated result, you can paginate through a HubSpot collection of objects. +You will receive a `LengthAwarePaginator` just like with Eloquent, which means you can generate links in your UI just like you are used to. ```php $contacts = Contact::paginate(20); @@ -113,8 +113,8 @@ By default, this `paginate` method will look at the `page` query parameter. You #### Cursor iteration -You can use the `cursor` method to iterate over the entire collection of objects. -This uses [lazy collections](https://laravel.com/docs/9.x/collections#lazy-collections) and [generators](https://www.php.net/manual/en/language.generators.overview.php) +You can use the `cursor` method to iterate over the entire collection of objects. +This uses [lazy collections](https://laravel.com/docs/9.x/collections#lazy-collections) and [generators](https://www.php.net/manual/en/language.generators.overview.php) to seamlessly fetch chunks of records from the API as needed, hydrating objects when needed, and providing smooth iteration over a limitless number of objects. ```php @@ -129,7 +129,7 @@ foreach(Contact::cursor() AS $contact) { #### Manually fetching chunks -Of course, you can grab collections of records with your own manual pagination or chunking logic. +Of course, you can grab collections of records with your own manual pagination or chunking logic. Use the `take` and `after` methods to specify what you want to grab, and then `get`. ```php @@ -143,7 +143,7 @@ $contacts = Contact::get(); ### Searching and filtering When retrieving multiple objects, you will frequently want to filter, search, and order these results. -You can use a fluent interface to build up a query before retrieving the results. +You can use a fluent interface to build up a query before retrieving the results. #### Adding filters @@ -185,7 +185,7 @@ Contact::search('1234')->get(); #### Ordering -You can order the results with any property. +You can order the results with any property. ```php Contact::orderBy('lastname')->get(); @@ -199,7 +199,7 @@ Contact::orderBy('days_to_close', 'desc')->get(); ### Associations -HubSpot associations are handled similar to Eloquent relationships. +HubSpot associations are handled similar to Eloquent relationships. #### Dynamic properties @@ -230,7 +230,7 @@ Normally, there are three HubSpot API calls to achieve the above result: 2. Retrieve all the contact IDs that are associated to this company 3. Query for contacts that match the IDs -Now we can eliminate the second API call by eager loading the associated contact IDs. +Now we can eliminate the second API call by eager loading the associated contact IDs. This library always eager-loads the IDs for associated companies, contacts, deals, and tickets. It does not eager-load IDs for engagements like emails and notes, since those association will tend to be much longer lists. @@ -258,7 +258,13 @@ This will create a new contact, associate it to the company, and return the new You can also associate existing objects using `attach`. This method accepts and ID or an object instance. ```php -Company::find(555)->attach(Contact::find(123)); +Company::find(555)->contacts()->attach(Contact::find(123)); +``` + +You can also detach existing objects using `detach`. This method accepts and ID or an object instance. + +```php +Company::find(555)->contacts()->detach(Contact::find(123)); ```