Skip to content

Commit

Permalink
Close #214
Browse files Browse the repository at this point in the history
  • Loading branch information
developer committed Aug 15, 2018
1 parent 5229bb5 commit 69deae9
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 6 deletions.
5 changes: 2 additions & 3 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="true">
stopOnFailure="false">
<testsuites>
<testsuite name="All">
<directory>./tests/</directory>
Expand All @@ -27,4 +26,4 @@
<log type="coverage-text" target="build/coverage.txt"/>
<log type="coverage-clover" target="build/clover.xml"/>
</logging-->
</phpunit>
</phpunit>
9 changes: 9 additions & 0 deletions src/Contracts/Schema/ContainerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ interface ContainerInterface
*/
public function getSchema($resourceObject): ?SchemaInterface;

/**
* If container has a Schema for a given input.
*
* @param mixed $resourceObject
*
* @return bool
*/
public function hasSchema($resourceObject): bool;

/**
* Get schema provider by resource type.
*
Expand Down
7 changes: 6 additions & 1 deletion src/Encoder/Parser/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ protected function getCurrentData()
*
* @SuppressWarnings(PHPMD.StaticAccess)
* @SuppressWarnings(PHPMD.ElseExpression)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function analyzeData($data): array
{
Expand All @@ -285,11 +286,15 @@ protected function analyzeData($data): array
$isOk = (is_array($data) === true || is_object($data) === true || $data === null);
$isOk ?: Exceptions::throwInvalidArgument('data', $data);

if (is_array($data) === true) {
if ($this->container->hasSchema($data) === true) {
$isCollection = false;
$traversableData = [$data];
} elseif (is_array($data) === true) {
$traversableData = $data;
} elseif ($data instanceof Traversable) {
$traversableData = $data instanceof IteratorAggregate ? $data->getIterator() : $data;
} elseif (is_object($data) === true) {
// normally resources should be handled above but if Schema was not registered for $data we get here
$isCollection = false;
$traversableData = [$data];
} elseif ($data === null) {
Expand Down
17 changes: 15 additions & 2 deletions src/Schema/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ public function __construct(SchemaFactoryInterface $factory, iterable $schemas =
*/
public function register(string $type, $schema): void
{
// Type must be non-empty string
if (empty($type) === true) {
if (empty($type) === true || class_exists($type) === false) {
throw new InvalidArgumentException(_($this->messages[self::MSG_INVALID_TYPE]));
}

Expand Down Expand Up @@ -175,6 +174,15 @@ public function getSchema($resource): ?SchemaInterface
return $this->getSchemaByType($resourceType);
}

/**
* @inheritdoc
*/
public function hasSchema($resourceObject): bool
{
return is_object($resourceObject) === true &&
$this->hasProviderMapping($this->getResourceType($resourceObject)) === true;
}

/**
* @inheritdoc
*
Expand Down Expand Up @@ -358,6 +366,11 @@ protected function setResourceToJsonTypeMapping(string $resourceType, string $js
*/
protected function getResourceType($resource): string
{
assert(
is_object($resource) === true,
'Unable to get a type of the resource as it is not an object.'
);

return get_class($resource);
}

Expand Down
8 changes: 8 additions & 0 deletions src/Schema/ResourceIdentifierContainerAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ public function getSchema($resourceObject): SchemaInterface
return $this->getSchemaAdapter($this->container->getSchema($resourceObject));
}

/**
* @inheritdoc
*/
public function hasSchema($resourceObject): bool
{
return $this->container->hasSchema($resourceObject);
}

/**
* @inheritdoc
*/
Expand Down
98 changes: 98 additions & 0 deletions tests/Data/AuthorCModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php namespace Neomerx\Tests\JsonApi\Data;

/**
* Copyright 2015-2018 info@neomerx.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use ArrayAccess;
use ArrayIterator;
use IteratorAggregate;

/**
* This model emulates a resource which looks like an array (Traversable) with its properties.
* The library should successfully distinguish arrays from resources that may act as arrays.
*
* A real word example of such a resource is Yii CModel https://www.yiiframework.com/doc/api/1.1/CModel
*
* @package Neomerx\Tests\JsonApi
*/
class AuthorCModel implements ArrayAccess, IteratorAggregate
{
const ATTRIBUTE_ID = 'author_id';
const ATTRIBUTE_FIRST_NAME = 'first_name';
const ATTRIBUTE_LAST_NAME = 'last_name';
const LINK_COMMENTS = 'comments';

/**
* Resource properties.
*
* @var array
*/
private $properties = [];

/**
* @param string $identity
* @param string $firstName
* @param string $lastName
* @param array|null $comments
*/
public function __construct(string $identity, string $firstName, string $lastName, array $comments = null)
{
$this[self::ATTRIBUTE_ID] = $identity;
$this[self::ATTRIBUTE_FIRST_NAME] = $firstName;
$this[self::ATTRIBUTE_LAST_NAME] = $lastName;
$this[self::LINK_COMMENTS] = $comments;
}

/**
* @inheritdoc
*/
public function getIterator()
{
return new ArrayIterator($this->properties);
}

/**
* @inheritdoc
*/
public function offsetExists($offset)
{
return array_key_exists($offset, $this->properties);
}

/**
* @inheritdoc
*/
public function offsetGet($offset)
{
return $this->properties[$offset];
}

/**
* @inheritdoc
*/
public function offsetSet($offset, $value)
{
$this->properties[$offset] = $value;
}

/**
* @inheritdoc
*/
public function offsetUnset($offset)
{
unset($this->properties[$offset]);
}
}
82 changes: 82 additions & 0 deletions tests/Data/AuthorCModelSchema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php namespace Neomerx\Tests\JsonApi\Data;

/**
* Copyright 2015-2018 info@neomerx.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use Neomerx\JsonApi\Contracts\Document\LinkInterface;

/**
* @package Neomerx\Tests\JsonApi
*/
class AuthorCModelSchema extends DevSchema
{
/**
* @inheritdoc
*/
protected $resourceType = 'people';

/**
* @inheritdoc
*/
public function getId($author): ?string
{
return $author[AuthorCModel::ATTRIBUTE_ID];
}

/**
* @inheritdoc
*/
public function getAttributes($author, array $fieldKeysFilter = null): ?array
{
return [
AuthorCModel::ATTRIBUTE_FIRST_NAME => $author[AuthorCModel::ATTRIBUTE_FIRST_NAME],
AuthorCModel::ATTRIBUTE_LAST_NAME => $author[AuthorCModel::ATTRIBUTE_LAST_NAME],
];
}

/**
* @inheritdoc
*/
public function getRelationships($author, bool $isPrimary, array $includeRelationships): ?array
{
assert($author instanceof AuthorCModel);

if (($isPrimary && $this->isIsLinksInPrimary()) || (!$isPrimary && $this->isIsLinksInIncluded())) {
$selfLink = $this->getRelationshipSelfLink($author, AuthorCModel::LINK_COMMENTS);
$links = [
AuthorCModel::LINK_COMMENTS => [
self::LINKS => [LinkInterface::SELF => $selfLink],
self::SHOW_DATA => false,
],
];
} else {
$links = [
AuthorCModel::LINK_COMMENTS => [
// closures for data are supported as well
self::DATA => function () use ($author) {
return isset($author[AuthorCModel::LINK_COMMENTS]) ?
$author[AuthorCModel::LINK_COMMENTS] : null;
},
],
];
}

// NOTE: The line(s) below for testing purposes only. Not for production.
$this->fixLinks($author, $links);

return $links;
}
}
67 changes: 67 additions & 0 deletions tests/Encoder/EncodeSimpleObjectsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use Neomerx\JsonApi\Factories\Factory;
use Neomerx\Tests\JsonApi\BaseTestCase;
use Neomerx\Tests\JsonApi\Data\Author;
use Neomerx\Tests\JsonApi\Data\AuthorCModel;
use Neomerx\Tests\JsonApi\Data\AuthorCModelSchema;
use Neomerx\Tests\JsonApi\Data\AuthorSchema;
use Neomerx\Tests\JsonApi\Data\Collection;
use Neomerx\Tests\JsonApi\Data\Comment;
Expand Down Expand Up @@ -825,4 +827,69 @@ public function testEncodingSimilarRelationships(): void

$this->assertEquals($expected, $actual);
}

/**
* Test encode array-based objects.
*
* @see https://github.com/neomerx/json-api/pull/214
*/
public function testEncodeArrayBasedObject(): void
{
$author = new AuthorCModel(9, 'Dan', 'Gebhardt');
$encoder = Encoder::instance([
AuthorCModel::class => AuthorCModelSchema::class,
], $this->encoderOptions);

$actual = $encoder->encodeData($author);

$expected = <<<EOL
{
"data" : {
"type" : "people",
"id" : "9",
"attributes" : {
"first_name" : "Dan",
"last_name" : "Gebhardt"
},
"relationships" : {
"comments" : { "data" : null }
},
"links" : {
"self" : "http://example.com/people/9"
}
}
}
EOL;
// remove formatting from 'expected'
$expected = json_encode(json_decode($expected));

$this->assertEquals($expected, $actual);

// same but as array

$actual = $encoder->encodeData([$author]);

$expected = <<<EOL
{
"data" : [{
"type" : "people",
"id" : "9",
"attributes" : {
"first_name" : "Dan",
"last_name" : "Gebhardt"
},
"relationships" : {
"comments" : { "data" : null }
},
"links" : {
"self" : "http://example.com/people/9"
}
}]
}
EOL;
// remove formatting from 'expected'
$expected = json_encode(json_decode($expected));

$this->assertEquals($expected, $actual);
}
}

0 comments on commit 69deae9

Please sign in to comment.