Skip to content

Commit 5d3b7d8

Browse files
authored
Merge pull request #1 from SynergiTech/add-additional-methods
Implement 'create', 'update', 'createOrUpdate', and 'delete' methods
2 parents 74f70b1 + a81cd32 commit 5d3b7d8

7 files changed

+286
-18
lines changed

README.md

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Allows you to directly pull an individual record as an array by Id.
2626
You can also specify another field name as the second parameter.
2727
If you specify a non-unique column and multiple records are returned then the first record is always returned.
2828

29+
#### Usage
30+
2931
```php
3032
Salesforce::table('MyTable')->find('YourIdHere');
3133
```
@@ -35,10 +37,94 @@ Salesforce::table('MyTable')->find('YourIdHere');
3537
Allows you to directly pull multiple records as a Laravel Collection by provide an array of their respective Id fields.
3638
You can also specify another field name as the second parameter.
3739

40+
#### Usage
41+
3842
```php
3943
Salesforce::table('MyTable')->findMany(['YourId1Here', 'YourId2Here']);
4044
```
4145

46+
### create
47+
48+
Allows you to create a new record on the specified table using an array of fields.
49+
50+
#### Usage
51+
52+
```php
53+
$response = Salesforce::table('MyTable')->create([
54+
'Name' => 'John Doe',
55+
]);
56+
```
57+
58+
#### Expected Response
59+
60+
```php
61+
[
62+
'id' => '', // Salesforce Id
63+
'success' => true,
64+
'errors' => [],
65+
'data' => [
66+
// Full record data
67+
],
68+
]
69+
```
70+
71+
### update
72+
73+
Allows you to update a record using it's Salesforce Id with an array of fields.
74+
75+
#### Usage
76+
77+
```php
78+
$response = Salesforce::table('MyTable')->update('Id', [
79+
'Name' => 'John Doe',
80+
]);
81+
```
82+
83+
#### Expected Response
84+
85+
See 'create' above
86+
87+
### createOrUpdate
88+
89+
Allows you to upsert a record using an Id field and the associated value.
90+
91+
#### Usage
92+
93+
```php
94+
$response = Salesforce::table('MyTable')->createOrUpdate('My_External_Id__c', 'ExternalId', [
95+
'Name' => 'John Doe',
96+
]);
97+
```
98+
99+
#### Expected Response
100+
101+
```php
102+
[
103+
'id' => '', // Salesforce Id
104+
'success' => true,
105+
'errors' => [],
106+
'created' => true, // True/False depending on whether the record was created or updated
107+
'data' => [
108+
// Full record data
109+
],
110+
]
111+
```
112+
113+
### delete
114+
115+
Allows you to delete a record by it's Id, returning true if successful.
116+
117+
#### Usage
118+
119+
```php
120+
Salesforce::table('MyTable')->delete('Id');
121+
```
122+
123+
## Query Builder
124+
125+
This package allows you to scope your get calls using query builder methods.
126+
Query builders **cannot** currently be used in conjunction with the `update` or `delete` methods (sorry 🙏).
127+
42128
### where
43129

44130
You can also scope your queries with where clauses.
@@ -105,4 +191,4 @@ Salesforce::table('MyTable')->where('Name', 'LIKE', 'John%')->limit(20)->get();
105191
## Exceptions
106192

107193
By default [omniphx/forrest](https://github.com/omniphx/forrest) typically throws a single exception with more detail contained within a JSON encoded string.
108-
We've wrapped a couple with our own exceptions to help with debugging.
194+
We've wrapped some with our own exceptions to help with debugging.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace SynergiTech\Salesforce\Exceptions;
4+
5+
use Exception;
6+
7+
class EntityIsDeletedException extends Exception
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace SynergiTech\Salesforce\Exceptions;
4+
5+
use Exception;
6+
7+
class InvalidCrossReferenceKeyException extends Exception
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace SynergiTech\Salesforce\Exceptions;
4+
5+
use Exception;
6+
7+
class JsonParseErrorException extends Exception
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace SynergiTech\Salesforce\Exceptions;
4+
5+
use Exception;
6+
7+
class MalformedIdException extends Exception
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace SynergiTech\Salesforce\Exceptions;
4+
5+
use Exception;
6+
7+
class RequiredFieldMissingException extends Exception
8+
{
9+
}

src/Services/TableService.php

Lines changed: 154 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
namespace SynergiTech\Salesforce\Services;
44

5+
use Exception;
56
use Illuminate\Support\Collection;
67
use Omniphx\Forrest\Exceptions\SalesforceException;
78
use Omniphx\Forrest\Providers\Laravel\Facades\Forrest;
9+
use stdClass;
10+
use SynergiTech\Salesforce\Exceptions\EntityIsDeletedException;
11+
use SynergiTech\Salesforce\Exceptions\InvalidCrossReferenceKeyException;
812
use SynergiTech\Salesforce\Exceptions\InvalidFieldException;
13+
use SynergiTech\Salesforce\Exceptions\JsonParseErrorException;
14+
use SynergiTech\Salesforce\Exceptions\MalformedIdException;
915
use SynergiTech\Salesforce\Exceptions\MalformedQueryException;
1016
use SynergiTech\Salesforce\Exceptions\NotFoundException;
17+
use SynergiTech\Salesforce\Exceptions\RequiredFieldMissingException;
1118
use SynergiTech\Salesforce\Models\Builder;
1219
use SynergiTech\Salesforce\Models\Response;
1320

@@ -47,39 +54,169 @@ public function findMany(int|string|array $id, string $fieldName = 'Id'): Collec
4754
$response = $this->get();
4855

4956
if ($response->records->count() === 0) {
50-
throw new NotFoundException("No records with the specified IDs could be found");
57+
throw new NotFoundException("No record(s) with the specified ID(s) could be found");
5158
}
5259

5360
return $response->records;
5461
}
5562

63+
/**
64+
* Execute the query and retrieve available objects in a response
65+
*/
5666
public function get(): Response
5767
{
5868
try {
5969
$query = $this->getQuery();
6070
return new Response($query, Forrest::query($query));
6171
} catch (SalesforceException $ex) {
62-
$message = $ex->getMessage();
63-
/** @var array<mixed> $errors */
64-
$errors = json_decode($message);
65-
/** @var \stdClass{errorCode:string, message:string} $error */
66-
$error = $errors[0];
67-
68-
if (property_exists($error, 'errorCode')) {
69-
$message = $this->formatErrorMessage($error->message);
70-
71-
switch ($error->errorCode) {
72-
case 'MALFORMED_QUERY':
73-
throw new MalformedQueryException($message);
74-
case 'INVALID_QUERY_FILTER_OPERATOR':
75-
throw new InvalidFieldException($message);
76-
}
72+
throw $this->wrapException($ex);
73+
}
74+
}
75+
76+
/**
77+
* Create a record in the Salesforce table with the specified data
78+
*
79+
* @param array<string, mixed> $data
80+
* @return array<string, mixed>|false
81+
*/
82+
public function create(array $data = []): array|false
83+
{
84+
try {
85+
$response = Forrest::sobjects($this->table, [
86+
'method' => 'post',
87+
'body' => $data,
88+
]);
89+
90+
if ($response['success'] ?? false) {
91+
$response['data'] = $this->find($response['id']);
92+
return $response;
7793
}
94+
} catch (SalesforceException $ex) {
95+
throw $this->wrapException($ex);
96+
}
97+
98+
return false;
99+
}
78100

79-
throw $ex;
101+
/**
102+
* Update a record with the specified Id in Salesforce with the provided data
103+
*
104+
* @param array<string, mixed> $data
105+
* @return array<string, mixed>
106+
*/
107+
public function update(string $id, array $data): array
108+
{
109+
try {
110+
Forrest::sobjects(implode('/', [
111+
$this->table,
112+
$id,
113+
]), [
114+
'method' => 'patch',
115+
'body' => $data,
116+
]);
117+
118+
return $this->find($id);
119+
} catch (SalesforceException $ex) {
120+
throw $this->wrapException($ex);
80121
}
81122
}
82123

124+
/**
125+
* Upsert a record using the specified external Id field and Id with the provided data
126+
*
127+
* @param array<string, mixed> $data
128+
* @return array<string, mixed>|false
129+
*/
130+
public function createOrUpdate(string $field, string $id, array $data = []): array|false
131+
{
132+
try {
133+
$response = Forrest::sobjects(implode('/', [
134+
$this->table,
135+
$field,
136+
$id,
137+
]), [
138+
'method' => 'patch',
139+
'body' => $data,
140+
]);
141+
142+
if ($response['success'] ?? false) {
143+
$response['data'] = $this->find($response['id']);
144+
return $response;
145+
}
146+
} catch (SalesforceException $ex) {
147+
throw $this->wrapException($ex);
148+
}
149+
150+
return false;
151+
}
152+
153+
/**
154+
* Delete a record on the table using the specified Id
155+
*
156+
* @return bool
157+
*/
158+
public function delete(string $id): bool
159+
{
160+
try {
161+
Forrest::sobjects(implode('/', [
162+
$this->table,
163+
$id,
164+
]), [
165+
'method' => 'delete',
166+
]);
167+
return true;
168+
} catch (SalesforceException $ex) {
169+
throw $this->wrapException($ex);
170+
}
171+
}
172+
173+
protected function wrapException(SalesforceException $ex): Exception
174+
{
175+
$error = $this->decodeError($ex);
176+
177+
if (property_exists($error, 'errorCode')) {
178+
$message = $this->formatErrorMessage($error->message);
179+
180+
switch ($error->errorCode) {
181+
case 'ENTITY_IS_DELETED':
182+
return new EntityIsDeletedException($message);
183+
case 'INVALID_CROSS_REFERENCE_KEY':
184+
return new InvalidCrossReferenceKeyException($message);
185+
case 'INVALID_ID_FIELD':
186+
return new InvalidFieldException($message);
187+
case 'INVALID_QUERY_FILTER_OPERATOR':
188+
return new InvalidFieldException($message);
189+
case 'REQUIRED_FIELD_MISSING':
190+
return new RequiredFieldMissingException($message);
191+
case 'MALFORMED_QUERY':
192+
return new MalformedQueryException($message);
193+
case 'MALFORMED_ID':
194+
return new MalformedIdException($message);
195+
case 'NOT_FOUND':
196+
return new NotFoundException($message);
197+
}
198+
}
199+
200+
return $ex;
201+
}
202+
203+
/**
204+
* @return stdClass{errorCode:string, message:string}
205+
*/
206+
protected function decodeError(SalesforceException $ex): stdClass
207+
{
208+
$message = $ex->getMessage();
209+
/** @var array<mixed>|false $errors */
210+
$errors = json_decode($message);
211+
212+
if (json_last_error() !== JSON_ERROR_NONE) {
213+
throw new JsonParseErrorException('Error message received was not valid json. Message: ' . $message);
214+
}
215+
216+
/** @var stdClass{errorCode:string, message:string} $error */
217+
return $errors[0];
218+
}
219+
83220
protected function formatErrorMessage(string $message): string
84221
{
85222
return str_replace('\n', ' - ', $message);

0 commit comments

Comments
 (0)