Skip to content

Commit

Permalink
Merge pull request #3 from stefanak-michal/unnest_in_execute
Browse files Browse the repository at this point in the history
Unnest in execute
  • Loading branch information
stefanak-michal authored Jul 26, 2021
2 parents 0d3e2a1 + 4033380 commit a85db1f
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 126 deletions.
136 changes: 47 additions & 89 deletions src/Deepr.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
*/
final class Deepr
{
const OPTION_UNNEST_ONE_CHILD = 1;

/**
* Enable to see query traversal
* @var bool
Expand All @@ -32,9 +30,7 @@ final class Deepr
* Default options
* @var array
*/
private static $defaultOptions = [
self::OPTION_UNNEST_ONE_CHILD => true
];
private static $defaultOptions = [];

/**
* Current options
Expand All @@ -54,36 +50,34 @@ public function invokeQuery(Collection $root, array $query, array $options = [])
{
$this->options = array_replace(self::$defaultOptions, array_intersect_key($options, self::$defaultOptions));

foreach ($query as $key => $value) {
if ($key === '||')
throw new Exception('Parallel processing not implemented');
if (reset($query) === '||')
throw new Exception('Parallel processing not implemented');

$unnest = false;
if ($this->isUnnest($key)) {
$key = $this->getKey($key, false);
$unnest = true;
if (array_filter(array_keys($query), 'is_int') == array_keys($query)) {
$clone = clone $root;
foreach ($query as $key => $value) {
$clone->clear();
$this->invokeQuery($clone, $value, $options);
$root->add($clone);
}
return $root->execute($options);
}

foreach ($query as $key => $value) {
$action = $this->getKey($key, false);

if (property_exists($root, $key)) {
$collection = $root->$key;
if (property_exists($root, $action)) {
$collection = $root->$action;
if (is_string($collection) && class_exists($collection)) {
$collection = new $collection();
}
} elseif (method_exists($root, $key) && array_key_exists('()', $value)) {
$collection = $root->{$key}(...$value['()']);
} elseif (method_exists($root, $action) && array_key_exists('()', $value)) {
$collection = $root->{$action}(...$value['()']);
}

if (isset($collection) && $collection instanceof IComponent) {
$this->recursion($collection, $key, $value);

if ($unnest) {
if ($collection instanceof Collection) {
foreach ($collection->getChildren() as $child)
$root->add($child);
}
} else {
$root->add($collection, $key);
}
$this->recursion($collection, $action, $value);
$root->add($collection, $key);
} else {
throw new Exception('Requested value "' . $key . '" is not valid property or method call');
}
Expand All @@ -104,27 +98,31 @@ private function recursion(IComponent $root, string $action, array $values)
$key = $this->getKey($k, false);

if (is_int($k)) {
$this->recursion($root, $action, $v);
$clone = clone $root;
$this->recursion($clone, $action, $v);
$root->add($clone);
} elseif ($k === '[]' && !empty($action)) {
if (self::$debug)
var_dump($action . ' []');
if ($root instanceof ILoadable) {
$offset = 0;
$length = null;
$tmpValues = $values;
unset($tmpValues['[]']);

if (is_int($v)) {
$offset = $v;
$length = 1;
foreach ($root->load($v, 1)->getChildren() as $child) {
$this->recursion($child, $action, $tmpValues);
if ($child instanceof Collection)
foreach ($child->getChildren() as $name => $ch) {
$root->add($ch, $name);
}
}
} elseif (is_array($v)) {
$offset = $v[0] ?? 0;
$length = $v[1] ?? null;
foreach ($root->load($v[0] ?? 0, $v[1] ?? null)->getChildren() as $child) {
$this->recursion($child, $action, $tmpValues);
$root->add($child);
}
}

$tmpValues = $values;
unset($tmpValues['[]']);
foreach ($root->load($offset, $length)->getChildren() as $item) {
$this->recursion($item, $action, $tmpValues);
$root->add($item);
}
} else {
throw new Exception('To access collection of class it has to implement ILoadable interface');
}
Expand All @@ -137,71 +135,31 @@ private function recursion(IComponent $root, string $action, array $values)

$data = $root->{$key}(...$v['()']);
if ($data instanceof Collection) {
$nest = $this->isNest($k);
$this->recursion($data, $key, $v);
foreach ($data->getChildren() as $child) {
$this->recursion($child, $key, $v);
if (!$nest)
$root->add($child);
}

if ($nest)
$root->add($data, $this->getKey($k));
$root->add($data, $k);
} else {
throw new Exception('Method response has to be Collection');
}
} elseif ($v === true) {
if (self::$debug)
var_dump($action . ' ' . $k . ' true');
if (property_exists($root, $key))
$root->add(new Value($root->$key), $this->getKey($k));
} elseif (is_array($v)) {
if ($k === '=>') {
if (self::$debug)
var_dump($action . ' array return');
$this->recursion($root, $action, $v);
} elseif ($this->isNest($k)) {
if (self::$debug)
var_dump($action . ' array nest');
$clone = clone $root;
$this->recursion($clone, $action, $v);
$root->add($clone, $this->getKey($k));
} elseif ($this->isUnnest($k)) {
if (self::$debug)
var_dump($action . ' array unnest');
$this->recursion($root, $this->getKey($k, false), $v);
if (property_exists($root, $key)) {
$root->add(new Value($root->$key), $k);
}
} elseif (is_array($v)) {
if (self::$debug)
var_dump($action . ' array nest');
$clone = clone $root;
$this->recursion($clone, $action, $v);
$root->add($clone, $k);
}
}
}

/**
* Is "=>target", "source=>target" or "key"
* @param string $key
* @return bool
*/
private function isNest(string $key): bool
{
if (strpos($key, '=>') !== false) {
list($a, $b) = explode('=>', $key, 2);
return !empty($b);
}
return true;
}

/**
* Is "source=>"
* @param string $key
* @return bool
*/
private function isUnnest(string $key): bool
{
if (strpos($key, '=>') !== false) {
list($a, $b) = explode('=>', $key, 2);
return !empty($a) && empty($b);
}
return false;
}

/**
* Get final key
* @param string $key
Expand Down
29 changes: 25 additions & 4 deletions src/components/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ final public function getChildren(): array
return $this->children;
}

/**
* Clear children collection
*/
final public function clear()
{
$this->children = [];
}

/**
* @inheritDoc
*/
Expand All @@ -45,11 +53,24 @@ final public function execute(array $options = [])
$output = [];

foreach ($this->getChildren() as $key => $child) {
$output = array_merge($output, [$key => $child->execute($options)]);
}
$result = $child->execute($options);

if ($options[\Deepr\Deepr::OPTION_UNNEST_ONE_CHILD] && count($output) == 1 && is_int(key($output))) {
$output = reset($output);
if ($key !== '=>' && substr($key, -2) === '=>') { //unnest
$output = $result;
} elseif (is_array($result)) {
if (strpos($key, '=>') === false) { //just a key
$output[$key] = $result;
} else {
list ($k, $a) = explode('=>', $key);
if (empty($k) && empty($a)) { //to return
$output = array_merge($output, $result);
} elseif (!empty($a)) { //nest
$output = array_merge($output, [$a => $result]);
}
}
} else { //value
$output[$key] = $result;
}
}

return $output;
Expand Down
28 changes: 12 additions & 16 deletions tests/DeeprTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
* @package Deepr\tests
* @author Michal Stefanak
* @link https://github.com/stefanak-michal/deepr-php
*
* @covers \Deepr\Deepr
* @covers \Deepr\components\Collection
* @covers \Deepr\components\Value
*/
class DeeprTest extends TestCase
{
Expand All @@ -35,9 +39,13 @@ public function testDeepr(): ?Deepr
*/
public function testInvokeQueries(string $input, string $output, Deepr $deepr)
{
var_dump($input, $output);
try {
$root = new Root();
$result = $deepr->invokeQuery($root, json_decode($input, true));
$input = json_decode($input, true);
if (json_last_error() != JSON_ERROR_NONE)
throw new Exception(json_last_error_msg());
$result = $deepr->invokeQuery($root, $input);
$result = json_encode($result);
$this->assertJsonStringEqualsJsonString($output, $result);
} catch (Exception $e) {
Expand All @@ -49,14 +57,13 @@ public function testInvokeQueries(string $input, string $output, Deepr $deepr)
* Use json files in jsons directory as sample data for requests
* @return array
*/
public function jsonProvider()
public function jsonProvider(): array
{
$data = [];
$dir = __DIR__ . DIRECTORY_SEPARATOR . 'jsons' . DIRECTORY_SEPARATOR;
if (file_exists($dir)) {
foreach (glob($dir . '*.json') as $file) {
list($i, $type) = explode('-', pathinfo($file, PATHINFO_FILENAME), 2);
$i = intval($i);
$type = $type == 'input' ? 0 : 1;

$json = file_get_contents($file);
Expand All @@ -65,7 +72,7 @@ public function jsonProvider()
$json = json_decode($json, true);
if (json_last_error() != JSON_ERROR_NONE)
continue;
$data[$i][$type] = json_encode($json);
$data['json ' . $i][$type] = json_encode($json);
}
}

Expand Down Expand Up @@ -100,17 +107,6 @@ public function testMissingException(Deepr $deepr)
*/
public function testOptions(Deepr $deepr)
{
$input = json_decode('{"movies":{"[]":5,"=>":{"title": true}}}', true);
$output = '{"movies":[{"title":"Top Gun"}]}';
$options = [$deepr::OPTION_UNNEST_ONE_CHILD => false];

try {
$root = new Root();
$result = $deepr->invokeQuery($root, $input, $options);
$result = json_encode($result);
$this->assertJsonStringEqualsJsonString($output, $result);
} catch (Exception $e) {
$this->markTestIncomplete($e->getMessage());
}
$this->markTestSkipped('No options available');
}
}
5 changes: 3 additions & 2 deletions tests/classes/Movie.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Deepr\tests\classes;

use Deepr\components\Collection;
use Deepr\components\IComponent;

/**
* Class Movie
Expand Down Expand Up @@ -33,10 +34,10 @@ class Movie extends Collection
/**
* RPC method to get actors of movie
* {"movies":{"[]":[],"=>":{"getActors":{"()":[]}}}}
* @return Collection
* @return IComponent
* @see \Deepr\tests\classes\Person
*/
public function getActors(): Collection
public function getActors(): IComponent
{
if (is_null($this->actors)) {
$this->actors = new Collection();
Expand Down
10 changes: 3 additions & 7 deletions tests/classes/Movies.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,17 @@ public function load(int $offset, ?int $length): Collection
* RPC method to get movie by title
* {"movies":{"getByTitle":{"()":["The Matrix"]}}}
* @param string $title
* @return Collection
* @return IComponent
* @see \Deepr\tests\classes\Movie
*/
public function getByTitle(string $title): Collection
public function getByTitle(string $title): IComponent
{
$collection = new self();

$movie = new Movie();
$row = Database::getMovieByTitle($title);
$movie->_id = $row['_id'];
$movie->title = $row['title'];
$movie->released = $row['released'];
$movie->tagline = $row['tagline'];
$collection->add($movie);

return $collection;
return $movie;
}
}
13 changes: 5 additions & 8 deletions tests/classes/Root.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

namespace Deepr\tests\classes;

use Deepr\components\Collection;
use Deepr\components\Value;
use Deepr\components\{Collection, Value, IComponent};

/**
* Class Root
Expand All @@ -26,14 +25,12 @@ class Root extends Collection

/**
* Sample method
* RPC methods has to be public and returns Collection
* RPC methods has to be public and returns IComponent
* {"date": {"()": []}}
* @return Collection
* @return IComponent
*/
public function date(): Collection
public function date(): IComponent
{
$collection = new Collection();
$collection->add(new Value('2021-07-20'));
return $collection;
return new Value('2021-07-20');
}
}
Loading

0 comments on commit a85db1f

Please sign in to comment.