Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [3.5.0] - 2025-07-12
- Add Contact Fields API functionality

## [3.4.0] - 2025-07-04
- Add Batch sending functionality (transactional, bulk and sandbox)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Currently with this SDK you can:
- Inbox management
- Project management
- Contact management
- Fields CRUD
- Contacts CRUD
- Lists CRUD
- General
Expand Down
85 changes: 85 additions & 0 deletions examples/general/contacts.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,88 @@
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}


/**
* Get all Contact Fields existing in your account
*
* GET https://mailtrap.io/api/accounts/{account_id}/contacts/fields
*/
try {
$response = $contacts->getAllContactFields();

// print the response body (array)
var_dump(ResponseHelper::toArray($response));
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}


/**
* Get a specific Contact Field by ID.
*
* GET https://mailtrap.io/api/accounts/{account_id}/contacts/fields/{field_id}
*/
try {
$fieldId = 1; // Replace 1 with the actual field ID
$response = $contacts->getContactField($fieldId);

// print the response body (array)
var_dump(ResponseHelper::toArray($response));
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}


/**
* Create new Contact Fields. Please note, you can have up to 40 fields.
*
* POST https://mailtrap.io/api/accounts/{account_id}/contacts/fields
*/
try {
$response = $contacts->createContactField(
'New Field Name', // <= 80 characters
'text', // Allowed values: text, integer, float, boolean, date
'new_field_merge_tag' // Personalize your campaigns by adding a merge tag. This field will be replaced with unique contact details for each recipient.
);

// print the response body (array)
var_dump(ResponseHelper::toArray($response));
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}


/**
* Update existing Contact Field. Please note, you cannot change data_type of the field.
*
* PATCH https://mailtrap.io/api/accounts/{account_id}/contacts/fields/{field_id}
*/
try {
$fieldId = 1; // Replace 1 with the actual field ID
$response = $contacts->updateContactField(
$fieldId,
'Updated Field Name',
'updated_field_merge_tag'
);

// print the response body (array)
var_dump(ResponseHelper::toArray($response));
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}

/**
* Delete Contact Field by ID.
*
* DELETE https://mailtrap.io/api/accounts/{account_id}/contacts/fields/{field_id}
*/
try {
$fieldId = 1; // Replace 1 with the actual field ID
$response = $contacts->deleteContactField($fieldId);

// Print the response status code
var_dump($response->getStatusCode());
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}
74 changes: 74 additions & 0 deletions src/Api/General/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,80 @@ public function deleteContactByEmail(string $email): ResponseInterface
return $this->deleteContact($email);
}

/**
* Get all Contact Fields.
*
* @return ResponseInterface
*/
public function getAllContactFields(): ResponseInterface
{
return $this->handleResponse(
$this->httpGet($this->getBasePath() . '/fields')
);
}

/**
* Get a specific Contact Field by ID.
*
* @param int $fieldId
* @return ResponseInterface
*/
public function getContactField(int $fieldId): ResponseInterface
{
return $this->handleResponse(
$this->httpGet($this->getBasePath() . '/fields/' . $fieldId)
);
}

/**
* Create a new Contact Field.
*
* @param string $name
* @param string $dataType
* @param string $mergeTag
* @return ResponseInterface
*/
public function createContactField(string $name, string $dataType, string $mergeTag): ResponseInterface
{
return $this->handleResponse(
$this->httpPost(
path: $this->getBasePath() . '/fields',
body: ['name' => $name, 'data_type' => $dataType, 'merge_tag' => $mergeTag]
)
);
}

/**
* Update an existing Contact Field by ID.
*
* @param int $fieldId
* @param string $name
* @param string $mergeTag
* @return ResponseInterface
*/
public function updateContactField(int $fieldId, string $name, string $mergeTag): ResponseInterface
{
return $this->handleResponse(
$this->httpPatch(
path: $this->getBasePath() . '/fields/' . $fieldId,
body: ['name' => $name, 'merge_tag' => $mergeTag]
)
);
}

/**
* Delete a Contact Field by ID.
*
* @param int $fieldId
* @return ResponseInterface
*/
public function deleteContactField(int $fieldId): ResponseInterface
{
return $this->handleResponse(
$this->httpDelete($this->getBasePath() . '/fields/' . $fieldId)
);
}

public function getAccountId(): int
{
return $this->accountId;
Expand Down
125 changes: 125 additions & 0 deletions tests/Api/General/ContactTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,131 @@ public function testInvalidCreateContact(): void
$this->contact->createContact($invalidContactDTO);
}

public function testGetAllContactFields(): void
{
$this->contact->expects($this->once())
->method('httpGet')
->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/fields')
->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactFields())));

$response = $this->contact->getAllContactFields();
$responseData = ResponseHelper::toArray($response);

$this->assertInstanceOf(Response::class, $response);
$this->assertCount(2, $responseData);
$this->assertArrayHasKey('id', $responseData[0]);
$this->assertArrayHasKey('name', $responseData[0]);
$this->assertArrayHasKey('data_type', $responseData[0]);
$this->assertArrayHasKey('merge_tag', $responseData[0]);
}

public function testGetContactField(): void
{
$fieldId = 1;

$this->contact->expects($this->once())
->method('httpGet')
->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/fields/' . $fieldId)
->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactFields()[0])));

$response = $this->contact->getContactField($fieldId);
$responseData = ResponseHelper::toArray($response);

$this->assertArrayHasKey('id', $responseData);
$this->assertEquals($fieldId, $responseData['id']);
}

public function testCreateContactField(): void
{
$fieldData = ['name' => 'Custom Field', 'data_type' => 'text', 'merge_tag' => 'my_contact_field'];

$this->contact->expects($this->once())
->method('httpPost')
->with(
AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/fields',
[],
$fieldData
)
->willReturn(new Response(201, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactFields()[0])));

$response = $this->contact->createContactField($fieldData['name'], $fieldData['data_type'], $fieldData['merge_tag']);
$responseData = ResponseHelper::toArray($response);

$this->assertArrayHasKey('id', $responseData);
$this->assertEquals($fieldData['name'], $responseData['name']);
}

public function testCreateContactFieldMultipleValidationErrors(): void
{
$fieldData = ['name' => 'Duplicate Field', 'data_type' => 'text', 'merge_tag' => 'duplicate_merge_tag'];
$errors = [
'errors' => [
'name' => ['has already been taken'],
'merge_tag' => ['has already been taken'],
],
];

$this->contact->expects($this->once())
->method('httpPost')
->with(
AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/fields',
[],
$fieldData
)
->willReturn(
new Response(422, ['Content-Type' => 'application/json'], json_encode($errors))
);

$this->expectException(HttpClientException::class);
$this->expectExceptionMessage('Errors: name -> has already been taken. merge_tag -> has already been taken.');

$this->contact->createContactField($fieldData['name'], $fieldData['data_type'], $fieldData['merge_tag']);
}

public function testUpdateContactField(): void
{
$fieldId = 1;
$fieldData = ['name' => 'Updated Field', 'merge_tag' => 'my_contact_field_new'];

$this->contact->expects($this->once())
->method('httpPatch')
->with(
AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/fields/' . $fieldId,
[],
$fieldData
)
->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactFields()[1])));

$response = $this->contact->updateContactField($fieldId, $fieldData['name'], $fieldData['merge_tag']);
$responseData = ResponseHelper::toArray($response);

$this->assertArrayHasKey('id', $responseData);
$this->assertEquals($fieldData['name'], $responseData['name']);
$this->assertEquals($fieldData['merge_tag'], $responseData['merge_tag']);
}

public function testDeleteContactField(): void
{
$fieldId = 1;

$this->contact->expects($this->once())
->method('httpDelete')
->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/fields/' . $fieldId)
->willReturn(new Response(204));

$response = $this->contact->deleteContactField($fieldId);

$this->assertEquals(204, $response->getStatusCode());
}

private function getExpectedContactFields(): array
{
return [
['id' => 1, 'name' => 'Custom Field', 'data_type' => 'text', 'merge_tag' => 'my_contact_field'],
['id' => 2, 'name' => 'Updated Field', 'data_type' => 'text', 'merge_tag' => 'my_contact_field_new'],
];
}

private function getExpectedContactLists(): array
{
return [
Expand Down