Skip to content

Commit

Permalink
NEW: Fixes #5
Browse files Browse the repository at this point in the history
- Added integration tests.
- Removed redundant JSON fixtures.
- Updated docs accordingly.
  • Loading branch information
phptek committed Jun 28, 2016
1 parent f2c532a commit fe140df
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 228 deletions.
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Contributing

Contributions in the form of suggestions, PR's and beer are *always* welcome.
This module is written to PSR-2, follows Semver and uses namespacing.

Contributions in the form of suggestions and PR's with regard to the above are *always welcome*
as is beer is most formats, apart from that stuff called "craft beer" which is so hoppy, I could be drinking
mushed Daffodils and not know the difference.


27 changes: 7 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,21 @@ JSON storage, querying and modification.

## Features

* Write JSON to a standard `DBField` subclass.
* Query JSON via simple accessors, Postgres-like operators or JSONPath expressions.
* Selectively return data as JSON, Arrays or cast to SilverStripe `Varchar`, `Int`, `Float` or `Boolean` objects.
* Selectively update specific portions of your source JSON, using JSONPath expressions.
* Query custom JSON data via simple accessors, Postgres-like operators or [JSONPath](http://goessner.net/articles/JsonPath/) expressions.
* Selectively return queries as JSON, Array or cast to SilverStripe `Varchar`, `Int`, `Float` or `Boolean` objects.
* Selectively update portions of your source JSON, using [JSONPath](http://goessner.net/articles/JsonPath/) expressions.

## Introduction

The module exposes a fully featured JSON query and update API, that allows developers to use XPath-like queries via [JSONPath](http://goessner.net/articles/JsonPath/)
The module exposes a fully featured JSON query and update API allowing developers to use XPath-like queries via [JSONPath](http://goessner.net/articles/JsonPath/)
or [Postgres' JSON operators](https://www.postgresql.org/docs/9.5/static/functions-json.html) (with some differences, see below) to query and update JSON data.

In Postgres both the `->` and `->>` operators act as string and integer key matchers on a JSON array or object respectively. The module
however treats both source types the same - they are after all *both JSON* so `->` is used as an **Integer Matcher** and `->>` as a **String Matcher**
*regardless* of the "type" of source JSON stored. The `#>` **Path Matcher** operator can act as an object or a text matcher, but the module wishes to simplify things and as such
the `#>` operator is *just a simple path matcher*.

Regardless of the type of query in-use you can set what form you'd like the data returned in via the `setReturnType()` method, on a query by query basis.
Regardless of the type of query in-use you can set what type you'd like the data returned in via the `setReturnType()` method, on a query by query basis.

Legitimate types are:

Expand All @@ -44,7 +43,7 @@ If using `SilverStripe`, the module will automatically cast the result(s) to one
* `Float`
* `Varchar`

If there are multiple results, the output will be an indexed array containing a single-value array for each result found.
If there are multiple results from a query, the output will be an indexed array containing a single-value array for each result found.

The module also allows developers to selectively *update* all, or just parts of the source JSON, via JSONPath expressions.

Expand All @@ -67,14 +66,6 @@ See: [Configuration Docs](docs/en/configuration.md).

See: [Usage Docs](docs/en/usage.md).

## Stability

This is currently *alpha software*. At time of writing (June 2016) there is
support for the `->` (Int matcher), `->>` (String matcher) and `#>` (Path matcher) operators and although well-tested,
they are far from complete.

This leads me to..

## Contributing

If you've been using Postgres or MySQL with its JSON functions for some time,
Expand All @@ -89,11 +80,7 @@ Please include all details, no matter how small. If it were *your module*, what
## Credits

* [Axel Anceau](https://github.com/Peekmo/) for his packaging-up of the pretty amazing JSONPath implementation by [Stefan Goessner](https://code.google.com/archive/p/jsonpath/)
* [Stefan Goessner](https://code.google.com/archive/p/jsonpath/) for the original work on JSONPath dating back to 2005!

## TODO

* Lose the fugly way that data is queried via `$this->dbObject()`
* [Stefan Goessner](https://code.google.com/archive/p/jsonpath/) for the original work on JSONPath dating back to 2005!

## Author

Expand Down
2 changes: 1 addition & 1 deletion _config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
* @package jsontext
*/

define('MODULE_NAME', 'JSON Text');
define('MODULE_NAME', 'JSONText');
define('MODULE_DIR', __DIR__);
2 changes: 1 addition & 1 deletion code/backends/PostgresJSONBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
namespace JSONText\Backends;

use JSONText\Exceptions\JSONTextException;
use JSONText\Fields\JSONText;

class PostgresJSONBackend extends JSONBackend
{
Expand Down Expand Up @@ -70,6 +69,7 @@ public function matchOnPath()
throw new JSONTextException($msg);
}

//$this->jsonText->getJSONStore()
$operandAsArray = $this->jsonText->toArray($this->operand);

// Empty is OK..could've been accidental...
Expand Down
49 changes: 23 additions & 26 deletions code/models/fieldtypes/JSONText.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*
* <code>
* static $db = [
* 'MyJSONStructure' => 'JSONText'
* 'MyJSONStructure' => '\JSONText\Fields\JSONText'
* ];
* </code>
*
Expand All @@ -23,7 +23,6 @@
* @package silverstripe-jsontext
* @subpackage fields
* @author Russell Michell <[email protected]>
* @todo Rename query() to getValue() that accepts optional param $expr (for JSONPath queries)
*/

namespace JSONText\Fields;
Expand Down Expand Up @@ -107,8 +106,8 @@ public function requireField()
'type' => 'text',
'parts' => $parts
];

DB::require_field($this->tableName, $this->name, $values);
\DB::require_field($this->tableName, $this->name, $values);
}

/**
Expand Down Expand Up @@ -148,14 +147,6 @@ public function setReturnType($type)
return $this;
}

/**
* @return string
*/
public function getReturnType()
{
return $this->returnType;
}

/**
* Returns the value of this field as an iterable.
*
Expand All @@ -164,16 +155,16 @@ public function getReturnType()
*/
public function getJSONStore()
{
if (!$json = $this->getValue()) {
return new \Peekmo\JsonPath\JsonStore('[]');
if (!$value = $this->getValue()) {
return new JsonStore('[]');
}

if (!$this->isJson($json)) {
if (!$this->isJson($value)) {
$msg = 'DB data is munged.';
throw new JSONTextException($msg);
}

$this->jsonStore = new \Peekmo\JsonPath\JsonStore($json);
$this->jsonStore = new JsonStore($value);

return $this->jsonStore;
}
Expand Down Expand Up @@ -366,7 +357,7 @@ public function query($operator, $operand = null)
}

$validType = ($isEx ? self::JSONTEXT_QUERY_JSONPATH : self::JSONTEXT_QUERY_OPERATOR);
if ($marshalled = $this->marshallQuery(func_get_args(), $validType)) {
if ($marshalled = $this->marshallQuery(func_get_args(), $validType, $this->getJSONStore())) {
return $this->returnAsType($marshalled);
}

Expand Down Expand Up @@ -423,9 +414,6 @@ private function marshallQuery($args, $type = 1)
*/
public function setValue($value, $record = null, $expr = '')
{
// Deal with standard SS behaviour
parent::setValue($value, $record);

if (empty($expr)) {
$this->value = $value;
} else {
Expand All @@ -434,13 +422,16 @@ public function setValue($value, $record = null, $expr = '')
throw new JSONTextException($msg);
}

if (!$this->jsonStore->set($expr, $value)) {
if (!$this->getJSONStore()->set($expr, $value)) {
$msg = 'Failed to properly set custom data to the JSONStore in ' . __FUNCTION__;
throw new JSONTextException($msg);
}

$this->value = $this->jsonStore->toString();
}

// Deal with standard SS behaviour
parent::setValue($this->value, $record);

return $this;
}
Expand All @@ -455,7 +446,7 @@ public function setValue($value, $record = null, $expr = '')
private function returnAsType($data)
{
$data = (array) $data;
$type = $this->getReturnType();
$type = $this->returnType;
if ($type === 'array') {
if (!count($data)) {
return [];
Expand Down Expand Up @@ -485,18 +476,24 @@ private function returnAsType($data)
}

/**
* Create an instance of {@link JSONBackend} according to the value of JSONText::backend defined by SS config.
* Create an instance of {@link JSONBackend} according to the value of JSONText::backend defined in SS config.
*
* @param string operand
* @return JSONBackend
* @throws JSONTextException
*/
protected function createBackendInst($operand)
{
$backend = $this->config()->backend;
$dbBackendClass = ucfirst($backend) . 'JSONBackend';
$dbBackendClass = '\JSONText\Backends\\' . ucfirst($backend) . 'JSONBackend';

if (!class_exists($dbBackendClass)) {
$msg = 'JSONText backend class ' . $dbBackendClass . ' not found.';
throw new JSONTextException($msg);
}

return \Injector::inst()->createWithArgs(
'\JSONText\Backends\\' . $dbBackendClass, [
$dbBackendClass, [
$operand,
$this
]);
Expand Down
5 changes: 4 additions & 1 deletion docs/en/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ do this via standard SS config in your project's `mysite/_config/config.yml` fil
backend: postgres


Note: The module default is to use `postgres` which is also the only backend that will work at the moment.
Notes:

* The module uses namespacing so take this into account when calling any part of the module's public API.
* The module default is to use `postgres` which is also the only backend that will work at the moment.
16 changes: 8 additions & 8 deletions docs/en/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ A small handful of simple query methods `first()`, `last()` and `nth()` exist fo
{
private static $db = [
'MyJSON' => 'JSONText'
'MyJSON' => '\JSONText\Fields\JSONText'
];
/*
Expand Down Expand Up @@ -86,7 +86,7 @@ You can also use Postgres-like JSON querying syntax, for querying more complex J
class MyOtherDataObject extends DataObject
{
private static $db = [
'MyJSON' => 'JSONText'
'MyJSON' => '\JSONText\Fields\JSONText'
];
/**
Expand Down Expand Up @@ -144,15 +144,15 @@ See: [Table of JSONPath expressions](jsonpath.md)
}';
private static $db = [
'MyJSON' => 'JSONText'
'MyJSON' => '\JSONText\Fields\JSONText'
];
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
if (!$this->MyJSON) {
$this->setValue($this->stubJSON);
$this->setField($this->MyJSON, $this->stubJSON);
}
}
Expand Down Expand Up @@ -193,8 +193,8 @@ See: [Table of JSONPath expressions](jsonpath.md)

## Updating and Modifying JSON

No self-respecting JSON query solution is complete without the ability to selectively modify
nested JSON data and sub-nodes. The module overloads `setValue()` to accept an optional 3rd parameter, a valid JSONPath
No self-respecting JSON query solution would be complete without the ability to selectively modify
nested JSON data. The module overloads `setValue()` to accept an optional 3rd parameter, a valid JSONPath
expression.

If the expression matches >1 JSON nodes, then that result is expressed as an indexed array, and each matching
Expand All @@ -218,15 +218,15 @@ node will be modified with the data passed to `setValue()` as the standard `$val
}';
private static $db = [
'MyJSON' => 'JSONText'
'MyJSON' => '\JSONText\Fields\JSONText'
];
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
if (!$this->MyJSON) {
$this->setValue($this->stubJSON);
$this->setField($this->MyJSON, $this->stubJSON);
}
}
Expand Down
Loading

0 comments on commit fe140df

Please sign in to comment.