1: <?php
2: 3: 4: 5: 6: 7:
8:
9: namespace PipelinerSales\ApiClient\Repository\Rest;
10:
11: use PipelinerSales\ApiClient\Entity;
12: use PipelinerSales\ApiClient\EntityCollection;
13: use PipelinerSales\ApiClient\EntityCollectionIterator;
14: use PipelinerSales\ApiClient\PipelinerClientException;
15: use PipelinerSales\ApiClient\Repository\RepositoryInterface;
16: use PipelinerSales\ApiClient\Http\HttpInterface;
17: use PipelinerSales\ApiClient\Http\PipelinerHttpException;
18: use PipelinerSales\ApiClient\Http\Response;
19: use PipelinerSales\ApiClient\Query\Criteria;
20: use PipelinerSales\ApiClient\Query\Filter;
21: use PipelinerSales\ApiClient\Query\Sort;
22:
23: 24: 25:
26: class RestRepository implements RepositoryInterface
27: {
28:
29: private $httpClient;
30: private $urlPrefix;
31: private $entityType;
32: private $entityPlural;
33: private $dateTimeFormat;
34:
35: public function __construct($urlPrefix, $entity, $entityPlural, HttpInterface $connection, $dateTimeFormat)
36: {
37: $this->httpClient = $connection;
38: $this->urlPrefix = $urlPrefix;
39: $this->entityType = $entity;
40: $this->entityPlural = $entityPlural;
41: $this->dateTimeFormat = $dateTimeFormat;
42: }
43:
44: public function getById($id)
45: {
46: $result = $this->httpClient
47: ->request('GET', $this->urlPrefix . '/' . $this->entityPlural . '/' . $id)
48: ->decodeJson();
49: return $this->decodeEntity($result);
50: }
51:
52: public function get($criteria = null)
53: {
54: $criteriaUrl = '?';
55: if (is_null($criteria)) {
56: $criteriaUrl = '';
57: } elseif (is_array($criteria)) {
58: $criteriaUrl .= $this->arrayCriteriaToUrl($criteria);
59: } elseif (is_object($criteria)) {
60: $criteriaUrl .= $this->objectCriteriaToUrl($criteria);
61: } elseif (is_string($criteria)) {
62: $criteriaUrl .= $criteria;
63: } else {
64: throw new PipelinerClientException('Invalid criteria type');
65: }
66:
67: $response = $this->httpClient
68: ->request('GET', $this->urlPrefix . '/' . $this->entityPlural . $criteriaUrl);
69: $results = $response->decodeJson();
70: $decoded = array_map(array($this, 'decodeEntity'), $results);
71:
72: $range = $this->getContentRange($response);
73: if ($range === null) {
74: throw new PipelinerHttpException($response, 'Content-Range header not found');
75: }
76:
77: return new EntityCollection($decoded, $criteria, $range['start'], $range['end'], $range['total']);
78: }
79:
80: public function create()
81: {
82: return new Entity($this->entityType, $this->dateTimeFormat);
83: }
84:
85: public function delete($entity, $flags = self::FLAG_ROLLBACK_ON_ERROR)
86: {
87:
88:
89:
90:
91: if (is_array($entity)) {
92: $first = reset($entity);
93:
94:
95:
96: if (is_array($first) or $first instanceof Entity) {
97: return $this->deleteById(array_map(function ($item) {
98: return $item['ID'];
99: }, $entity), $flags);
100: }
101: }
102:
103: if (!isset($entity['ID'])) {
104: throw new PipelinerClientException('Cannot delete an entity which has no ID');
105: }
106: return $this->deleteById($entity['ID']);
107: }
108:
109: public function deleteById($id, $flags = self::FLAG_ROLLBACK_ON_ERROR)
110: {
111: if (is_array($id)) {
112:
113: $json = json_encode($id);
114: $url = $this->urlPrefix . '/deleteEntities?entityName=' . $this->entityType;
115: if ($flags) {
116: $url .= '&flag=' . $flags;
117: }
118: return $this->httpClient->request('POST', $url, $json);
119: } else {
120: return $this->httpClient->request('DELETE', $this->urlPrefix . '/' . $this->entityPlural . '/' . $id);
121: }
122: }
123:
124: public function bulkUpdate($data, $flags = self::FLAG_ROLLBACK_ON_ERROR, $sendFields = self::SEND_MODIFIED_FIELDS)
125: {
126: $payload = array();
127:
128: foreach ($data as $entity) {
129: if ($entity instanceof Entity) {
130: $values = (
131: $sendFields == self::SEND_MODIFIED_FIELDS ? $entity->getModifiedFields() : $entity->getFields()
132: );
133: $values['ID'] = $entity->getId();
134: $payload[] = $values;
135: } else {
136: $payload[] = $entity;
137: }
138: }
139:
140: $json = json_encode($payload);
141: $url = $this->urlPrefix . '/setEntities?entityName=' . $this->entityType;
142: if ($flags) {
143: $url .= '&flag=' . $flags;
144: }
145: return $this->httpClient->request('POST', $url, $json);
146: }
147:
148: public function save($entity, $sendFields = self::SEND_MODIFIED_FIELDS)
149: {
150: if ($entity instanceof Entity) {
151: $id = $entity->isFieldSet('ID') ? $entity->getId() : null;
152: $data = ($sendFields == self::SEND_MODIFIED_FIELDS ? $entity->modifiedToJson() : $entity->allToJson());
153: } elseif (is_array($entity)) {
154: $id = isset($entity['ID']) ? $entity['ID'] : null;
155: $data = json_encode($entity);
156: } else {
157: throw new \InvalidArgumentException('Invalid entity');
158: }
159:
160: if (!empty($id)) {
161:
162:
163:
164:
165:
166: $method = 'PUT';
167: $url = $this->urlPrefix . '/' . $this->entityPlural . '/' . $id;
168: } else {
169: $method = 'POST';
170: $url = $this->urlPrefix . '/' . $this->entityPlural;
171: }
172:
173: $response = $this->httpClient->request($method, $url, $data);
174:
175:
176: if ($response->getStatusCode() == 201) {
177: $response = new RestCreatedResponse($response);
178: $newId = $response->getCreatedId();
179: if ($entity instanceof Entity) {
180: $entity->setId($newId);
181: }
182: }
183:
184: if (($response->getStatusCode() == 200 or $response->getStatusCode() == 201)
185: and ( $entity instanceof Entity )) {
186: $entity->resetModified();
187: }
188:
189: return $response;
190: }
191:
192: public function getEntireRangeIterator(EntityCollection $collection)
193: {
194: return new EntityCollectionIterator($this, $collection);
195: }
196:
197: private function arrayCriteriaToUrl(array $criteria)
198: {
199: return http_build_query($criteria);
200: }
201:
202: private function objectCriteriaToUrl($criteria)
203: {
204: if ($criteria instanceof Criteria) {
205: return $criteria->toUrlQuery();
206: } elseif ($criteria instanceof Filter) {
207: $c = new Criteria();
208: $c->filter($criteria);
209: return $c->toUrlQuery();
210: } elseif ($criteria instanceof Sort) {
211: $c = new Criteria();
212: $c->sort($criteria);
213: return $c->toUrlQuery();
214: }
215: throw new PipelinerClientException('Invalid criteria object');
216: }
217:
218: private function decodeEntity(\stdClass $object)
219: {
220: $entity = new Entity($this->entityType, $this->dateTimeFormat);
221:
222: foreach ($object as $field => $value) {
223: $entity->setField($field, $value);
224: }
225: $entity->resetModified();
226:
227: return $entity;
228: }
229:
230: private function getContentRange(Response $response)
231: {
232: $result = preg_match('~Content-Range: items (\d+)-([\d-]+)/(\d+)~i', $response->getHeaders(), $matches);
233: if (!$result) {
234: return null;
235: }
236:
237: return array(
238: 'start' => intval($matches[1]),
239: 'end' => intval($matches[2]),
240: 'total' => intval($matches[3])
241: );
242: }
243: }
244: