Skip to content

Commit

Permalink
Implement PATCH support for relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
nohponex committed Feb 17, 2016
1 parent 3e1bf12 commit 550c235
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 99 deletions.
92 changes: 83 additions & 9 deletions src/Controller/PATCH.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
*/
namespace Phramework\JSONAPI\Controller;

use Phramework\Exceptions\ServerException;
use Phramework\JSONAPI\Relationship;
use \Phramework\Models\Request;
use \Phramework\Exceptions\RequestException;
use Phramework\Phramework;
use Phramework\Validate\ObjectValidator;

/**
Expand All @@ -29,14 +32,16 @@
abstract class PATCH extends \Phramework\JSONAPI\Controller\POST
{
/**
* @param object $parameters Request parameters
* @param object $parameters Request parameters
* @param string $method Request method
* @param array $headers Request headers
* @param integer|string $id Requested resource's id
* @param string $modelClass Resource's primary model
* to be used
* @param array $primaryDataParameters [Optional] Array with any
* additional arguments that the primary data is requiring
* @param callable[] $validationCallbacks
* @param callable|null $viewCallback
* @throws \Phramework\Exceptions\NotFound If resource not found
* @throws \Phramework\Exceptions\RequestException If no fields are changed
* @uses model's `getById` method to fetch resource
Expand All @@ -47,14 +52,17 @@ abstract class PATCH extends \Phramework\JSONAPI\Controller\POST
* @throws \Exception When Validation model for an attribute is not set
* @return boolean
* @todo rethink output
*/
protected static function handlePATCH(
$parameters,
$method,
$headers,
$id,
$modelClass,
$primaryDataParameters = []
$primaryDataParameters = [],
$validationCallbacks = [],
$viewCallback = null
) {
//Construct a validator
$validator = new ObjectValidator(
Expand Down Expand Up @@ -86,32 +94,98 @@ protected static function handlePATCH(

$requestAttributes = static::getRequestAttributes($parameters);

if (count((array) $requestAttributes) === 0) {
//@todo throw exception only both attributes and relationships are 0
if (($requestData = self::getRequestData($parameters)) !== null
&& property_exists($requestData, 'relationships')) {
$requestRelationships = $requestData->relationships;
} else {
$requestRelationships = new \stdClass();
}

if (count((array) $requestAttributes) === 0 && count((array) $requestRelationships) === 0) {
throw new RequestException('No fields updated');
}

$attributes = $validator->parse($requestAttributes);

//Parse relationship attributes, NOTE type TO_ONE relationships are writing their data back to $attributes object
$parsedRelationshipAttributes = self::getParsedRelationshipAttributes(
$modelClass,
$attributes,
$requestRelationships
);

//Check if callbacks for TO_MANY relationships are set
foreach ($parsedRelationshipAttributes as $relationshipKey => $relationshipValues) {
$relationship = $modelClass::getRelationship($relationshipKey);

if ($relationship->type == Relationship::TYPE_TO_MANY) {
if (!isset($relationship->callbacks->{Phramework::METHOD_PATCH})) {
throw new ServerException(sprintf(
'PATCH callback is not implemented for relationship "%s"',
$relationshipKey
));
}
}
}

//TODO allow nulls
foreach ($attributes as $key => $attribute) {
if ($attribute === null) {
unset($attributes->{$key});
}
}

//Call validation callbacks if set
foreach ($validationCallbacks as $callback) {
call_user_func(
$callback,
$requestAttributes,
$requestRelationships,
$attributes,
$parsedRelationshipAttributes
);
}

//Fetch data, to check if resource exists
$data = $modelClass::getById(
$currentResource = $modelClass::getById(
$id,
null, //fields
null, //fields todo maybe add []
...$primaryDataParameters
);

//Check if resource exists (MUST exist!)
static::exists($data);
static::exists($currentResource);

//Update the resource's attributes directly if any of then is requested to PATCH
if (count((array) $attributes) > 0) {
$patch = $modelClass::patch($id, (array)$attributes);
self::testUnknownError($patch, 'PATCH operation was not successful');
}

$patch = $modelClass::patch($id, (array) $attributes);
//Call TO_MANY callbacks to PATCH relationships
foreach ($parsedRelationshipAttributes as $relationshipKey => $relationshipValues) {
$relationship = $modelClass::getRelationship($relationshipKey);

if ($relationship->type == Relationship::TYPE_TO_MANY) {
call_user_func(
$relationship->callbacks->{Phramework::METHOD_PATCH},
$relationshipValues, //complete replacement
$id,
null //$additionalAttributes
);
}
}

self::testUnknownError($patch, 'PATCH operation was not successful');
if ($viewCallback !== null) {
if (!is_callable($viewCallback)) {
throw new ServerException('View callback is not callable!');
}

return call_user_func(
$viewCallback,
$id
);
}

static::viewData(
$modelClass::resource(['id' => $id]),
Expand Down
125 changes: 73 additions & 52 deletions src/Controller/POST.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ abstract class POST extends \Phramework\JSONAPI\Controller\GET
* @param array $relationshipParameters *[Optional]* Array with any
* additional argument primary data's relationships are requiring
* @param callable[] $validationCallbacks
* @param callable|null $viewCallback
* @todo handle as transaction queue, Since models usually are not producing exceptions.
* Prepare data until last possible moment,
* so that any exceptions can be thrown, and finally invoke the execution of the queue.
Expand Down Expand Up @@ -128,7 +129,7 @@ protected static function handlePOST(
}

/**
* @var int[]
* @var string[]
*/
$ids = [];

Expand All @@ -146,7 +147,6 @@ protected static function handlePOST(
$relationships = $queueItem->getRelationships();

foreach ($relationships as $key => $relationship) {
//@TODO
//Call post relationship method to post each of relationships pairs
foreach ($relationship->resources as $resourceId) {
call_user_func(
Expand Down Expand Up @@ -221,7 +221,7 @@ private static function handlePOSTResource(
$validator = $modelClass::getValidationModel();

$attributesValidator = (
isset($validator->attributes) && $validator->attributes
isset($validator->attributes) && $validator->attributes
? $validator->attributes
: new ObjectValidator()
);
Expand All @@ -230,7 +230,71 @@ private static function handlePOSTResource(
//Parse request attributes using $validationModel to validate the data
$attributes = $attributesValidator->parse($requestAttributes);

$relationships = $modelClass::getRelationships();
$parsedRelationshipAttributes = self::getParsedRelationshipAttributes(
$modelClass,
$attributes,
$requestRelationships,
$relationshipParameters
);

//Call Validation callbacks
foreach ($validationCallbacks as $callback) {
call_user_func(
$callback,
$requestAttributes,
$requestRelationships,
$attributes,
$parsedRelationshipAttributes
);
}

$queueRelationships = new \stdClass();

/**
* Call POST_RELATIONSHIP_BY_PREFIX handler for TO_MANY relationships
* This handler should post into database these relationships
*/
foreach ($requestRelationships as $relationshipKey => $relationshipValue) {
$relationship = $modelClass::getRelationship($relationshipKey);

if ($relationship->type == Relationship::TYPE_TO_MANY) {
$parsedRelationshipValue = $parsedRelationshipAttributes->{$relationshipKey};

$relationship = $modelClass::getRelationship($relationshipKey);

if (!isset($relationship->callbacks->{Phramework::METHOD_POST})) {
throw new ServerException(sprintf(
'POST callback is not implemented for relationship "%s"',
$relationshipKey
));
}

//Push to queueRelationships
$queueRelationships->{$relationshipKey} = (object) [
'callback' => $relationship->callbacks->{Phramework::METHOD_POST}, //callable
'resources' => $parsedRelationshipValue //array
];
}
}

return new \Phramework\JSONAPI\Controller\POST\QueueItem(
$attributes,
$queueRelationships
);
}

/**
* @param string $modelClass
* @param object $attributes
* @param object $requestRelationships
* @throws RequestException
* @throws \Exception
* @throws \Phramework\Exceptions\NotFoundException
* @return object
*/
protected static function getParsedRelationshipAttributes($modelClass, &$attributes, $requestRelationships, $relationshipParameters = [])
{
$validator = $modelClass::getValidationModel();

/**
* Format, object with
Expand Down Expand Up @@ -354,14 +418,13 @@ private static function handlePOSTResource(
}

//Parse attributes using relationship's validation model
$parsedRelationshipAttributes =
(
$parsedRelationshipAttributes = (
isset($validator->relationships)
? $validator->relationships->parse(
$relationshipAttributes
)
: new \stdClass()
);
: new \stdClass()
);

/**
* Foreach request relationship
Expand All @@ -378,7 +441,7 @@ private static function handlePOSTResource(
$parsedRelationshipValue = $parsedRelationshipAttributes->{$relationshipKey};

$tempIds = (
is_array($parsedRelationshipValue)
is_array($parsedRelationshipValue)
? $parsedRelationshipValue
: [$parsedRelationshipValue]
);
Expand Down Expand Up @@ -437,48 +500,6 @@ private static function handlePOSTResource(
}
}

//Call Validation callbacks
foreach ($validationCallbacks as $callback) {
call_user_func(
$callback,
$requestAttributes,
$requestRelationships,
$attributes
);
}

$queueRelationships = new \stdClass();

/**
* Call POST_RELATIONSHIP_BY_PREFIX handler for TO_MANY relationships
* This handler should post into database these relationships
*/
foreach ($requestRelationships as $relationshipKey => $relationshipValue) {
$relationship = $modelClass::getRelationship($relationshipKey);

if ($relationship->type == Relationship::TYPE_TO_MANY) {
$parsedRelationshipValue = $parsedRelationshipAttributes->{$relationshipKey};

$relationship = $modelClass::getRelationship($relationshipKey);

if (!isset($relationship->callbacks->{Phramework::METHOD_POST})) {
throw new ServerException(sprintf(
'POST callback is not implemented for relationship "%s"',
$relationshipKey
));
}

//Push to queueRelationships
$queueRelationships->{$relationshipKey} = (object) [
'callback' => $relationship->callbacks->{Phramework::METHOD_POST}, //callable
'resources' => $parsedRelationshipValue //array
];
}
}

return new \Phramework\JSONAPI\Controller\POST\QueueItem(
$attributes,
$queueRelationships
);
return $parsedRelationshipAttributes;
}
}
12 changes: 11 additions & 1 deletion tests/APP/Controllers/ArticleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,17 @@ public static function PATCH($params, $method, $headers, $id)
$method,
$headers,
$id,
Article::class
Article::class,
[],
[
function (
$requestAttributes,
$requestRelationships,
$attributes,
$parsedRelationshipAttributes
) {
}
]
);
}

Expand Down
5 changes: 3 additions & 2 deletions tests/APP/Models/Article.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ public static function getRelationships()
Relationship::TYPE_TO_MANY,
null,
(object) [
Phramework::METHOD_GET => [Tag::class, 'getRelationshipByArticle'],
Phramework::METHOD_POST => [Tag::class, 'postRelationshipByArticle']
Phramework::METHOD_GET => [Tag::class, 'getRelationshipArticle'],
Phramework::METHOD_POST => [Tag::class, 'postRelationshipArticle'],
Phramework::METHOD_PATCH => [Tag::class, 'patchRelationshipArticle']
],
Relationship::FLAG_DEFAULT | Relationship::FLAG_DATA
)
Expand Down
12 changes: 10 additions & 2 deletions tests/APP/Models/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public static function get(
* @param integer $return [description]
* @return integer [description]
*/
public static function postRelationshipByArticle(
public static function postRelationshipArticle(
$tagId,
$articleId,
$additionalAttributes = null,
Expand All @@ -127,11 +127,19 @@ public static function postRelationshipByArticle(
return 1;
}

public static function patchRelationshipArticle(
$tagId,
$articleId,
$additionalAttributes = null
) {
return true;
}

/**
* @param $articleId
* @return RelationshipResource[]
*/
public static function getRelationshipByArticle(
public static function getRelationshipArticle(
$articleId,
Fields $fields = null,
$flags = Resource::PARSE_DEFAULT
Expand Down

0 comments on commit 550c235

Please sign in to comment.