diff --git a/src/ConvertKit_API.php b/src/ConvertKit_API.php index 1921d42..b3641f8 100644 --- a/src/ConvertKit_API.php +++ b/src/ConvertKit_API.php @@ -174,6 +174,34 @@ public function get_landing_pages() return $this->get_resources('landing_pages'); } + /** + * Adds a subscriber to a form. + * + * @param integer $form_id Form ID. + * @param array $options Array of user data (email, name). + * + * @throws \InvalidArgumentException If the provided arguments are not of the expected type. + * + * @return false|object + */ + public function form_subscribe(int $form_id, array $options) + { + if (!is_int($form_id)) { + throw new \InvalidArgumentException(); + } + if (!is_array($options)) { + throw new \InvalidArgumentException(); + } + + // Add API Key to array of options. + $options['api_key'] = $this->api_key; + + return $this->post( + sprintf('forms/%s/subscribe', $form_id), + $options + ); + } + /** * List subscriptions to a form * @@ -452,55 +480,6 @@ public function get_resources(string $resource) return $this->resources[$resource]; } - /** - * Adds a subscriber to a form. - * - * @param integer $form_id Form ID. - * @param array $options Array of user data (email, name). - * - * @throws \InvalidArgumentException If the provided arguments are not of the expected type. - * - * @return false|object - */ - public function form_subscribe(int $form_id, array $options) - { - if (!is_int($form_id)) { - throw new \InvalidArgumentException(); - } - if (!is_array($options)) { - throw new \InvalidArgumentException(); - } - - // Add API Key to array of options. - $options['api_key'] = $this->api_key; - - return $this->post( - sprintf('forms/%s/subscribe', $form_id), - $options - ); - } - - /** - * Remove subscription from a form - * - * @param array $options Array of user data (email). - * - * @throws \InvalidArgumentException If the provided arguments are not of the expected type. - * - * @return false|object - */ - public function form_unsubscribe(array $options) - { - if (!is_array($options)) { - throw new \InvalidArgumentException(); - } - - // Add API Secret to array of options. - $options['api_secret'] = $this->api_secret; - - return $this->put('unsubscribe', $options); - } - /** * Get the ConvertKit subscriber ID associated with email address if it exists. * Return false if subscriber not found. @@ -524,7 +503,6 @@ public function get_subscriber_id(string $email_address) 'subscribers', [ 'api_secret' => $this->api_secret, - 'status' => 'all', 'email_address' => $email_address, ] ); @@ -548,6 +526,8 @@ public function get_subscriber_id(string $email_address) * * @param integer $subscriber_id Subscriber ID. * + * @see https://developers.convertkit.com/#view-a-single-subscriber + * * @throws \InvalidArgumentException If the provided arguments are not of the expected type. * * @return false|integer @@ -566,11 +546,104 @@ public function get_subscriber(int $subscriber_id) ); } + /** + * Updates the information for a single subscriber. + * + * @param integer $subscriber_id Existing Subscriber ID. + * @param string $first_name New First Name. + * @param string $email_address New Email Address. + * @param array $fields Updated Custom Fields. + * + * @see https://developers.convertkit.com/#update-subscriber + * + * @return false|mixed + */ + public function update_subscriber( + int $subscriber_id, + string $first_name = '', + string $email_address = '', + array $fields = [] + ) { + // Build parameters. + $options = [ + 'api_secret' => $this->api_secret, + ]; + + if (!empty($first_name)) { + $options['first_name'] = $first_name; + } + if (!empty($email_address)) { + $options['email_address'] = $email_address; + } + if (!empty($fields)) { + $options['fields'] = $fields; + } + + // Send request. + return $this->put( + sprintf('subscribers/%s', $subscriber_id), + $options + ); + } + + /** + * Unsubscribe an email address from all forms and sequences. + * + * @param string $email Email Address. + * + * @see https://developers.convertkit.com/#unsubscribe-subscriber + * + * @return false|object + */ + public function unsubscribe(string $email) + { + return $this->put( + 'unsubscribe', + [ + 'api_secret' => $this->api_secret, + 'email' => $email, + ] + ); + } + + /** + * Remove subscription from a form + * + * @param array $options Array of user data (email). + * + * @see https://developers.convertkit.com/#unsubscribe-subscriber + * + * @throws \InvalidArgumentException If the provided arguments are not of the expected type. + * + * @return false|object + */ + public function form_unsubscribe(array $options) + { + // This function is deprecated in 1.0, as we prefer functions with structured arguments. + // This function name is also misleading, as it doesn't just unsubscribe the email + // address from forms. + trigger_error( + 'form_unsubscribe() is deprecated in 1.0. Use unsubscribe($email) instead.', + E_USER_NOTICE + ); + + if (!is_array($options)) { + throw new \InvalidArgumentException(); + } + + // Add API Secret to array of options. + $options['api_secret'] = $this->api_secret; + + return $this->put('unsubscribe', $options); + } + /** * Get a list of the tags for a subscriber. * * @param integer $subscriber_id Subscriber ID. * + * @see https://developers.convertkit.com/#list-tags-for-a-subscriber + * * @throws \InvalidArgumentException If the provided arguments are not of the expected type. * * @return false|array diff --git a/tests/ConvertKitAPITest.php b/tests/ConvertKitAPITest.php index b0dda34..c121cc0 100644 --- a/tests/ConvertKitAPITest.php +++ b/tests/ConvertKitAPITest.php @@ -546,7 +546,7 @@ public function testGetResourcesInvalidResourceType() } /** - * Test that form_subscribe() and form_unsubscribe() returns the expected data. + * Test that form_subscribe() returns the expected data. * * @since 1.0.0 * @@ -565,13 +565,7 @@ public function testFormSubscribe() $this->assertEquals(get_object_vars($result->subscription)['subscribable_id'], $_ENV['CONVERTKIT_API_FORM_ID']); // Unsubscribe. - $result = $this->api->form_unsubscribe([ - 'email' => $email, - ]); - $this->assertInstanceOf('stdClass', $result); - $this->assertArrayHasKey('subscriber', get_object_vars($result)); - $this->assertArrayHasKey('email_address', get_object_vars($result->subscriber)); - $this->assertEquals(get_object_vars($result->subscriber)['email_address'], $email); + $this->api->unsubscribe($email); } /** @@ -665,6 +659,204 @@ public function testGetSubscriberWithInvalidSubscriberID() $subscriber = $this->api->get_subscriber(12345); } + /** + * Test that update_subscriber() works when no changes are made. + * + * @since 1.0.0 + * + * @return void + */ + public function testUpdateSubscriberWithNoChanges() + { + $result = $this->api->update_subscriber($_ENV['CONVERTKIT_API_SUBSCRIBER_ID']); + $this->assertInstanceOf('stdClass', $result); + $this->assertArrayHasKey('subscriber', get_object_vars($result)); + $this->assertArrayHasKey('id', get_object_vars($result->subscriber)); + $this->assertEquals(get_object_vars($result->subscriber)['id'], $_ENV['CONVERTKIT_API_SUBSCRIBER_ID']); + } + + /** + * Test that update_subscriber() works when updating the subscriber's first name. + * + * @since 1.0.0 + * + * @return void + */ + public function testUpdateSubscriberFirstName() + { + // Add a subscriber. + $email = $this->generateEmailAddress(); + $result = $this->api->add_subscriber_to_sequence( + $_ENV['CONVERTKIT_API_SEQUENCE_ID'], + $email + ); + + // Get subscriber ID. + $subscriberID = $result->subscription->subscriber->id; + + // Update subscriber's first name. + $result = $this->api->update_subscriber( + $subscriberID, + 'First Name' + ); + + // Confirm the change is reflected in the subscriber. + $this->assertInstanceOf('stdClass', $result); + $this->assertArrayHasKey('subscriber', get_object_vars($result)); + $this->assertArrayHasKey('id', get_object_vars($result->subscriber)); + $this->assertEquals(get_object_vars($result->subscriber)['id'], $subscriberID); + $this->assertEquals(get_object_vars($result->subscriber)['first_name'], 'First Name'); + + // Unsubscribe. + $this->api->unsubscribe($email); + } + + /** + * Test that update_subscriber() works when updating the subscriber's email address. + * + * @since 1.0.0 + * + * @return void + */ + public function testUpdateSubscriberEmailAddress() + { + // Add a subscriber. + $email = $this->generateEmailAddress(); + $result = $this->api->add_subscriber_to_sequence( + $_ENV['CONVERTKIT_API_SEQUENCE_ID'], + $email + ); + + // Get subscriber ID. + $subscriberID = $result->subscription->subscriber->id; + + // Update subscriber's email address. + $newEmail = $this->generateEmailAddress(); + $result = $this->api->update_subscriber( + $subscriberID, + '', + $newEmail + ); + + // Confirm the change is reflected in the subscriber. + $this->assertInstanceOf('stdClass', $result); + $this->assertArrayHasKey('subscriber', get_object_vars($result)); + $this->assertArrayHasKey('id', get_object_vars($result->subscriber)); + $this->assertEquals(get_object_vars($result->subscriber)['id'], $subscriberID); + $this->assertEquals(get_object_vars($result->subscriber)['email_address'], $newEmail); + + // Unsubscribe. + $this->api->unsubscribe($newEmail); + } + + /** + * Test that update_subscriber() works when updating the subscriber's custom fields. + * + * @since 1.0.0 + * + * @return void + */ + public function testUpdateSubscriberCustomFields() + { + // Add a subscriber. + $email = $this->generateEmailAddress(); + $result = $this->api->add_subscriber_to_sequence( + $_ENV['CONVERTKIT_API_SEQUENCE_ID'], + $email + ); + + // Get subscriber ID. + $subscriberID = $result->subscription->subscriber->id; + + // Update subscriber's email address. + $result = $this->api->update_subscriber( + $subscriberID, + '', + '', + [ + 'last_name' => 'Last Name', + ] + ); + + // Confirm the change is reflected in the subscriber. + $this->assertInstanceOf('stdClass', $result); + $this->assertArrayHasKey('subscriber', get_object_vars($result)); + $this->assertArrayHasKey('id', get_object_vars($result->subscriber)); + $this->assertEquals(get_object_vars($result->subscriber)['id'], $subscriberID); + $this->assertEquals($result->subscriber->fields->last_name, 'Last Name'); + + // Unsubscribe. + $this->api->unsubscribe($email); + } + + /** + * Test that update_subscriber() throws a ClientException when an invalid + * subscriber ID is specified. + * + * @since 1.0.0 + * + * @return void + */ + public function testUpdateSubscriberWithInvalidSubscriberID() + { + $this->expectException(GuzzleHttp\Exception\ClientException::class); + $subscriber = $this->api->update_subscriber(12345); + } + + /** + * Test that unsubscribe() works with a valid subscriber email address. + * + * @since 1.0.0 + * + * @return void + */ + public function testUnsubscribe() + { + // Add a subscriber. + $email = $this->generateEmailAddress(); + $result = $this->api->add_subscriber_to_sequence( + $_ENV['CONVERTKIT_API_SEQUENCE_ID'], + $email + ); + + // Unsubscribe. + $result = $this->api->unsubscribe($email); + + // Confirm the change is reflected in the subscriber. + $this->assertInstanceOf('stdClass', $result); + $this->assertArrayHasKey('subscriber', get_object_vars($result)); + $this->assertEquals($result->subscriber->email_address, $email); + $this->assertEquals($result->subscriber->state, 'cancelled'); + } + + /** + * Test that unsubscribe() throws a ClientException when an email + * address is specified that is not subscribed. + * + * @since 1.0.0 + * + * @return void + */ + public function testUnsubscribeWithNotSubscribedEmailAddress() + { + $this->expectException(GuzzleHttp\Exception\ClientException::class); + $subscriber = $this->api->unsubscribe('not-subscribed@convertkit.com'); + } + + /** + * Test that unsubscribe() throws a ClientException when an invalid + * email address is specified. + * + * @since 1.0.0 + * + * @return void + */ + public function testUnsubscribeWithInvalidEmailAddress() + { + $this->expectException(GuzzleHttp\Exception\ClientException::class); + $subscriber = $this->api->unsubscribe('invalid-email'); + } + /** * Test that get_subscriber_tags() returns the expected data. *