diff --git a/.codeclimate.yml b/.codeclimate.yml index 7626a4d..9074df2 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,4 +1,4 @@ -version: 2 +version: "2" plugins: phpcodesniffer: enabled: true @@ -13,4 +13,5 @@ exclude_patterns: - "**/*.xml" - ".circleci/" - ".gitignore" - - "docs/" \ No newline at end of file + - "docs/" + - "tests/" diff --git a/composer.json b/composer.json index 0129825..8f5806d 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ ], "require": { "ext-json": "*", - "galbar/jsonpath": "^1.1", + "galbar/jsonpath": "^3.0", "opis/json-schema": "^1.0.8" }, "require-dev": { diff --git a/src/RootedJsonData.php b/src/RootedJsonData.php index 0608dd4..19628f8 100644 --- a/src/RootedJsonData.php +++ b/src/RootedJsonData.php @@ -183,6 +183,46 @@ public function __isset($path) return $notSmart->get($path) ? true : false; } + /** + * Magic __unset method, detects field from path. + * + * @param mixed $path + * Path to unset, including specific field. + */ + public function __unset($path) + { + $exploded = explode(".", $path); + $field = array_pop($exploded); + $imploded = implode(".", $exploded); + $this->remove($imploded, $field); + } + + /** + * Wrapper for JsonObject::remove() method, plus validation. + * + * @param mixed $path + * jsonPath. + * @param mixed $field + * Field to remove. + * + * @return \JsonPath\JsonObject + * Modified object (self). + */ + public function remove($path, $field) + { + $validationJsonObject = new JsonObject((string) $this->data); + $validationJsonObject->remove($path, $field); + + $result = self::validate($validationJsonObject, $this->schema); + if (!$result->isValid()) { + $keywordArgs = $result->getFirstError()->keywordArgs(); + $message = "{$path} expects a {$keywordArgs['expected']}"; + throw new ValidationException($message, $result); + } + + return $this->data->remove($path, $field); + } + /** * Get the JSON Schema as a string. * diff --git a/tests/RootedJsonDatatTest.php b/tests/RootedJsonDatatTest.php index 888973a..17a1c64 100644 --- a/tests/RootedJsonDatatTest.php +++ b/tests/RootedJsonDatatTest.php @@ -217,4 +217,58 @@ public function testAddWithSchema() $this->expectException(ValidationException::class); $data->add("$.numbers", ["name" => "three", "value" => 3]); } + + /** + * If a schema is provided, adding elements that match array should work, + * elements that violate schema will fail. + */ + public function testRemove() + { + $json = '{"field1":"foo","field2":"bar"}'; + $schema = ' + { + "type": "object", + "required":["field1"], + "properties": { + "field1": { + "type":"string" + }, + "field2": { + "type":"string" + } + } + }'; + $data = new RootedJsonData($json, $schema); + $data->remove("$", "field2"); + $this->assertEquals("foo", $data->{"$.field1"}); + $this->expectException(ValidationException::class); + $data->remove("$", "field1"); + } + + /** + * If a schema is provided, adding elements that match array should work, + * elements that violate schema will fail. + */ + public function testUnset() + { + $json = '{"field1":"foo","field2":"bar"}'; + $schema = ' + { + "type": "object", + "required":["field1"], + "properties": { + "field1": { + "type":"string" + }, + "field2": { + "type":"string" + } + } + }'; + $data = new RootedJsonData($json, $schema); + unset($data->{"$.field2"}); + $this->assertEquals("foo", $data->{"$.field1"}); + $this->expectException(ValidationException::class); + unset($data->{"$.field1"}); + } }