From fd49f953019a98a40a1e16dcd0d46ed193eeb13c Mon Sep 17 00:00:00 2001 From: Dipak Sarkar Date: Tue, 12 Dec 2023 22:33:04 +0530 Subject: [PATCH 1/3] added update method to application --- src/XeroPHP/Application.php | 48 ++++++++++++++++++- .../Models/Accounting/PurchaseOrder.php | 2 +- src/XeroPHP/Models/Accounting/TaxRate.php | 2 +- .../Models/Accounting/TrackingCategory.php | 3 +- .../TrackingCategory/TrackingOption.php | 1 + src/XeroPHP/Remote/Model.php | 46 ++++++++++++++---- 6 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/XeroPHP/Application.php b/src/XeroPHP/Application.php index 675275f3..fae7a373 100644 --- a/src/XeroPHP/Application.php +++ b/src/XeroPHP/Application.php @@ -190,7 +190,7 @@ public function loadByGUID($model, $guid) /** @var Remote\Model $class */ $class = $this->validateModelClass($model); - if(!$guid){ + if (!$guid) { throw new Remote\Exception\NotFoundException; } @@ -231,7 +231,7 @@ public function loadByGUIDs($model, $guids) /** @var $class Remote\Model */ $class = $this->validateModelClass($model); - if(empty($guids)){ + if (empty($guids)) { return []; } @@ -319,6 +319,50 @@ public function save(Remote\Model $object, $replace_data = false) return $response; } + /** + * @param Remote\Model $object + * @param bool $replace_data + * + * @throws Exception + * + * @return Remote\Response|null + */ + public function update(Remote\Model $object, $replace_data = false) + { + //Saves any properties that don't want to be included in the normal loop + //(special saving endpoints) + $this->savePropertiesDirectly($object); + + if (!$object->isDirty()) { + return null; + } + + $object->validate(); + + $method = $object::supportsMethod(Request::METHOD_POST) ? Request::METHOD_POST : Request::METHOD_PUT; + $uri = sprintf('%s/%s', $object::getResourceURI(), $object->getGUID()); + + if (!$object::supportsMethod($method)) { + throw new Exception(sprintf('%s doesn\'t support [%s] via the API', get_class($object), $method)); + } + + //Put in an array with the first level containing only the 'root node'. + $data = [$object::getRootNodeName() => $object->toStringArray(true)]; + $url = new URL($this, $uri, $object::getAPIStem()); + $request = new Request($this, $url, $method); + + $request->setBody(Helpers::arrayToXML($data))->send(); + $response = $request->getResponse(); + + if (false !== $element = current($response->getElements())) { + $object->fromStringArray($element, $replace_data); + } + //Mark the object as clean since no exception was thrown + $object->setClean(); + + return $response; + } + /** * @param array|Collection $objects * @param mixed $checkGuid diff --git a/src/XeroPHP/Models/Accounting/PurchaseOrder.php b/src/XeroPHP/Models/Accounting/PurchaseOrder.php index 1e1488da..bd3efbbc 100644 --- a/src/XeroPHP/Models/Accounting/PurchaseOrder.php +++ b/src/XeroPHP/Models/Accounting/PurchaseOrder.php @@ -306,7 +306,7 @@ public function getLineItems() public function addLineItem(LineItem $value) { $this->propertyUpdated('LineItems', $value); - if (! isset($this->_data['LineItems'])) { + if (!isset($this->_data['LineItems'])) { $this->_data['LineItems'] = new Remote\Collection(); } $this->_data['LineItems'][] = $value; diff --git a/src/XeroPHP/Models/Accounting/TaxRate.php b/src/XeroPHP/Models/Accounting/TaxRate.php index 5da3f971..e4c65952 100644 --- a/src/XeroPHP/Models/Accounting/TaxRate.php +++ b/src/XeroPHP/Models/Accounting/TaxRate.php @@ -227,7 +227,7 @@ public function getTaxComponents() public function addTaxComponent(TaxComponent $value) { $this->propertyUpdated('TaxComponents', $value); - if (! isset($this->_data['TaxComponents'])) { + if (!isset($this->_data['TaxComponents'])) { $this->_data['TaxComponents'] = new Remote\Collection(); } $this->_data['TaxComponents'][] = $value; diff --git a/src/XeroPHP/Models/Accounting/TrackingCategory.php b/src/XeroPHP/Models/Accounting/TrackingCategory.php index c5a595cd..a2155706 100644 --- a/src/XeroPHP/Models/Accounting/TrackingCategory.php +++ b/src/XeroPHP/Models/Accounting/TrackingCategory.php @@ -1,4 +1,5 @@ _data['TrackingOptionID']; } - + /** * @param string $value * diff --git a/src/XeroPHP/Models/Accounting/TrackingCategory/TrackingOption.php b/src/XeroPHP/Models/Accounting/TrackingCategory/TrackingOption.php index 41d9a8e6..323cd6ee 100644 --- a/src/XeroPHP/Models/Accounting/TrackingCategory/TrackingOption.php +++ b/src/XeroPHP/Models/Accounting/TrackingCategory/TrackingOption.php @@ -1,4 +1,5 @@ _data = []; $this->_associated_objects = []; } - + public static function make(Application $application = null) { return new static($application); @@ -202,17 +202,17 @@ public function fromStringArray($input_array, $replace_data = false) $isArray = $meta[self::KEY_IS_ARRAY]; //If set and NOT replace data, continue - if (! $replace_data && isset($this->_data[$property])) { + if (!$replace_data && isset($this->_data[$property])) { continue; } - if (! isset($input_array[$property])) { + if (!isset($input_array[$property])) { $this->_data[$property] = null; continue; } - if ($isArray && ! is_array($input_array[$property])) { + if ($isArray && !is_array($input_array[$property])) { $this->_data[$property] = null; continue; @@ -255,12 +255,12 @@ public function toStringArray($dirty_only = false) { $out = []; foreach (static::getProperties() as $property => $meta) { - if (! isset($this->_data[$property])) { + if (!isset($this->_data[$property])) { continue; } //if we only want the dirty props, stop here - if ($dirty_only && ! isset($this->_dirty[$property]) && $property !== static::getGUIDProperty()) { + if ($dirty_only && !isset($this->_dirty[$property]) && $property !== static::getGUIDProperty()) { continue; } @@ -349,7 +349,7 @@ public static function castFromString($type, $value, $php_type) case self::PROPERTY_TYPE_BOOLEAN: return in_array(strtolower($value), ['true', '1', 'yes'], true); - /** @noinspection PhpMissingBreakStatementInspection */ + /** @noinspection PhpMissingBreakStatementInspection */ case self::PROPERTY_TYPE_TIMESTAMP: $timezone = new \DateTimeZone('UTC'); @@ -394,8 +394,8 @@ public function validate($check_children = true) $mandatory = $meta[self::KEY_MANDATORY]; //If it's got a GUID, it's already going to be valid almost all cases - if (! $this->hasGUID() && $mandatory) { - if (! isset($this->_data[$property]) || empty($this->_data[$property])) { + if (!$this->hasGUID() && $mandatory) { + if (!isset($this->_data[$property]) || empty($this->_data[$property])) { $class = get_class($this); throw new RequiredFieldException( $class, @@ -428,6 +428,24 @@ public function validate($check_children = true) return true; } + /** + * Shorthand update an object if it is instantiated with app context. + * + * @throws Exception + * + * @return Response|null + */ + public function update() + { + if ($this->_application === null) { + throw new Exception( + '->update() is only available on objects that have an injected application context.' + ); + } + + return $this->_application->update($this); + } + /** * Shorthand save an object if it is instantiated with app context. * @@ -494,6 +512,10 @@ public function __isset($property) */ public function __get($property) { + if (empty($property)) { + return false; + } + $getter = sprintf('get%s', $property); if (method_exists($this, $getter)) { @@ -513,6 +535,10 @@ public function __get($property) */ public function __set($property, $value) { + if (empty($property)) { + return false; + } + $setter = sprintf('set%s', $property); if (method_exists($this, $setter)) { @@ -524,7 +550,7 @@ public function __set($property, $value) protected function propertyUpdated($property, $value) { - if (! isset($this->_data[$property]) || $this->_data[$property] !== $value) { + if (!isset($this->_data[$property]) || $this->_data[$property] !== $value) { //If this object can update itself, set its own dirty flag, otherwise, set its parent's. if (count(array_intersect($this::getSupportedMethods(), [Request::METHOD_PUT, Request::METHOD_POST])) > 0) { //Object can update itself From c0f4e09e943879f7c9cc52d3316df6c9548c261e Mon Sep 17 00:00:00 2001 From: Dipak Sarkar Date: Tue, 19 Dec 2023 23:42:32 +0530 Subject: [PATCH 2/3] Resolved white space --- src/XeroPHP/Application.php | 12 ++++++------ src/XeroPHP/Remote/Model.php | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/XeroPHP/Application.php b/src/XeroPHP/Application.php index fae7a373..e6cd3980 100644 --- a/src/XeroPHP/Application.php +++ b/src/XeroPHP/Application.php @@ -190,7 +190,7 @@ public function loadByGUID($model, $guid) /** @var Remote\Model $class */ $class = $this->validateModelClass($model); - if (!$guid) { + if (! $guid) { throw new Remote\Exception\NotFoundException; } @@ -281,7 +281,7 @@ public function save(Remote\Model $object, $replace_data = false) //(special saving endpoints) $this->savePropertiesDirectly($object); - if (!$object->isDirty()) { + if (! $object->isDirty()) { return null; } @@ -298,7 +298,7 @@ public function save(Remote\Model $object, $replace_data = false) $object->setApplication($this); } - if (!$object::supportsMethod($method)) { + if (! $object::supportsMethod($method)) { throw new Exception(sprintf('%s doesn\'t support [%s] via the API', get_class($object), $method)); } @@ -333,7 +333,7 @@ public function update(Remote\Model $object, $replace_data = false) //(special saving endpoints) $this->savePropertiesDirectly($object); - if (!$object->isDirty()) { + if (! $object->isDirty()) { return null; } @@ -342,7 +342,7 @@ public function update(Remote\Model $object, $replace_data = false) $method = $object::supportsMethod(Request::METHOD_POST) ? Request::METHOD_POST : Request::METHOD_PUT; $uri = sprintf('%s/%s', $object::getResourceURI(), $object->getGUID()); - if (!$object::supportsMethod($method)) { + if (! $object::supportsMethod($method)) { throw new Exception(sprintf('%s doesn\'t support [%s] via the API', get_class($object), $method)); } @@ -478,7 +478,7 @@ private function savePropertiesDirectly(Remote\Model $object) */ public function delete(Remote\Model $object) { - if (!$object::supportsMethod(Request::METHOD_DELETE)) { + if (! $object::supportsMethod(Request::METHOD_DELETE)) { throw new Exception( sprintf( '%s doesn\'t support [DELETE] via the API', diff --git a/src/XeroPHP/Remote/Model.php b/src/XeroPHP/Remote/Model.php index 33480fc6..a53aab79 100644 --- a/src/XeroPHP/Remote/Model.php +++ b/src/XeroPHP/Remote/Model.php @@ -202,11 +202,11 @@ public function fromStringArray($input_array, $replace_data = false) $isArray = $meta[self::KEY_IS_ARRAY]; //If set and NOT replace data, continue - if (!$replace_data && isset($this->_data[$property])) { + if (! $replace_data && isset($this->_data[$property])) { continue; } - if (!isset($input_array[$property])) { + if (! isset($input_array[$property])) { $this->_data[$property] = null; continue; @@ -255,12 +255,12 @@ public function toStringArray($dirty_only = false) { $out = []; foreach (static::getProperties() as $property => $meta) { - if (!isset($this->_data[$property])) { + if (! isset($this->_data[$property])) { continue; } //if we only want the dirty props, stop here - if ($dirty_only && !isset($this->_dirty[$property]) && $property !== static::getGUIDProperty()) { + if ($dirty_only && ! isset($this->_dirty[$property]) && $property !== static::getGUIDProperty()) { continue; } @@ -394,8 +394,8 @@ public function validate($check_children = true) $mandatory = $meta[self::KEY_MANDATORY]; //If it's got a GUID, it's already going to be valid almost all cases - if (!$this->hasGUID() && $mandatory) { - if (!isset($this->_data[$property]) || empty($this->_data[$property])) { + if (! $this->hasGUID() && $mandatory) { + if (! isset($this->_data[$property]) || empty($this->_data[$property])) { $class = get_class($this); throw new RequiredFieldException( $class, @@ -550,7 +550,7 @@ public function __set($property, $value) protected function propertyUpdated($property, $value) { - if (!isset($this->_data[$property]) || $this->_data[$property] !== $value) { + if (! isset($this->_data[$property]) || $this->_data[$property] !== $value) { //If this object can update itself, set its own dirty flag, otherwise, set its parent's. if (count(array_intersect($this::getSupportedMethods(), [Request::METHOD_PUT, Request::METHOD_POST])) > 0) { //Object can update itself From 8f3b72095ef6cdcf23148be92ccbaedda51faebf Mon Sep 17 00:00:00 2001 From: Dipak Sarkar Date: Tue, 19 Dec 2023 23:44:44 +0530 Subject: [PATCH 3/3] resolve conflicts --- src/XeroPHP/Remote/Model.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/XeroPHP/Remote/Model.php b/src/XeroPHP/Remote/Model.php index a53aab79..35070533 100644 --- a/src/XeroPHP/Remote/Model.php +++ b/src/XeroPHP/Remote/Model.php @@ -547,10 +547,12 @@ public function __set($property, $value) trigger_error(sprintf("Undefined property %s::$%s.\n", __CLASS__, $property)); } - + protected function propertyUpdated($property, $value) { - if (! isset($this->_data[$property]) || $this->_data[$property] !== $value) { + $currentValue = isset($this->_data[$property]) ? $this->_data[$property] : null; + + if ($currentValue !== $value) { //If this object can update itself, set its own dirty flag, otherwise, set its parent's. if (count(array_intersect($this::getSupportedMethods(), [Request::METHOD_PUT, Request::METHOD_POST])) > 0) { //Object can update itself