diff --git a/src/Filter.php b/src/Filter.php index d17099b..866b686 100644 --- a/src/Filter.php +++ b/src/Filter.php @@ -226,12 +226,16 @@ public function validate(InternalModel $model) $filterable = $model->getFilterableAttributes(); foreach ($this->attributes as $filterAttribute) { + $attribute = $filterAttribute->getAttribute(); + $operator = $filterAttribute->getOperator(); + $operand = $filterAttribute->getOperand(); + $isJSONFilter = ($filterAttribute instanceof FilterJSONAttribute); - if (!property_exists($filterable, $filterAttribute->attribute)) { + if (!property_exists($filterable, $attribute)) { throw new RequestException(sprintf( 'Filter attribute "%s" not allowed', - $filterAttribute->attribute + $attribute )); } @@ -240,64 +244,64 @@ public function validate(InternalModel $model) //Attempt to use filter validation model first if ($filterValidator //&& isset($filterValidator) - && isset($filterValidator->properties->{$filterAttribute->attribute}) + && isset($filterValidator->properties->{$attribute}) ) { $attributeValidator = - $filterValidator->properties->{$filterAttribute->attribute}; + $filterValidator->properties->{$attribute}; } elseif ($validationModel && isset( $validationModel->attributes, - $validationModel->attributes->properties->{$filterAttribute->attribute}) + $validationModel->attributes->properties->{$attribute}) ) { //Then attempt to use attribute validation model first $attributeValidator = - $validationModel->attributes->properties->{$filterAttribute->attribute}; + $validationModel->attributes->properties->{$attribute}; } else { throw new \Exception(sprintf( 'Filter attribute "%s" has not a filter validator', - $filterAttribute->attribute + $attribute )); } - $operatorClass = $filterable->{$filterAttribute->attribute}; + $operatorClass = $filterable->{$attribute}; if ($isJSONFilter && ($operatorClass & Operator::CLASS_JSONOBJECT) === 0) { throw new RequestException(sprintf( 'Filter attribute "%s" is not accepting JSON object filtering', - $filterAttribute->attribute + $attribute )); } //Check if operator is allowed if (!$isJSONFilter && !in_array( - $filterAttribute->operator, + $operator, Operator::getByClassFlags($operatorClass) )) { throw new RequestException(sprintf( 'Filter operator "%s" is not allowed for attribute "%s"', - $filterAttribute->operator, - $filterAttribute->attribute + $operator, + $attribute )); } //Validate filterAttribute operand against filter validator or validator if set - if (!in_array($filterAttribute->operator, Operator::getNullableOperators())) { + if (!in_array($operator, Operator::getNullableOperators())) { if ($isJSONFilter) { //If filter validator is set for dereference JSON object property if ($filterValidator - && isset($filterValidator->properties->{$filterAttribute->attribute}) - && isset($filterValidator->properties->{$filterAttribute->attribute} - ->properties->{$filterAttribute->key}) + && isset($filterValidator->properties->{$attribute}) + && isset($filterValidator->properties->{$attribute} + ->properties->{$filterAttribute->getKey()}) ) { $attributePropertyValidator = $filterValidator->properties - ->{$filterAttribute->attribute}->properties->{$filterAttribute->key}; + ->{$attribute}->properties->{$filterAttribute->getKey()}; - $attributePropertyValidator->parse($filterAttribute->operand); + $attributePropertyValidator->parse($operand); } //} else { // //**NOTE** Remain unparsed! //} } else { - $attributeValidator->parse($filterAttribute->operand); + $attributeValidator->parse($operand); } } } diff --git a/src/FilterAttribute.php b/src/FilterAttribute.php index 229044d..e7c70e3 100644 --- a/src/FilterAttribute.php +++ b/src/FilterAttribute.php @@ -27,9 +27,6 @@ * @since 1.0.0 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 * @author Xenofon Spafaridis - * @property-read string $attribute - * @property-read string $operator - * @property-read mixed|null $operand */ class FilterAttribute { @@ -145,28 +142,6 @@ public static function parse($filterKey, $filterValue) return $filterAttributes; } - /** - * @param string $name - * @return mixed - * @throws \Exception - */ - public function __get($name) - { - switch ($name) { - case 'operand': - return $this->operand; - case 'operator': - return $this->operator; - case 'attribute': - return $this->attribute; - } - - throw new \Exception(sprintf( - 'Undefined property via __get(): %s', - $name - )); - } - /** * @return string */ diff --git a/src/FilterJSONAttribute.php b/src/FilterJSONAttribute.php index 4183deb..8f69a31 100644 --- a/src/FilterJSONAttribute.php +++ b/src/FilterJSONAttribute.php @@ -21,11 +21,6 @@ * @since 1.0.0 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 * @author Xenofon Spafaridis - * @property-read string $attribute - * @property-read string $operator - * @property-read mixed|null $operand - * @property-read string $key - * todo remove */ class FilterJSONAttribute extends FilterAttribute { @@ -56,21 +51,6 @@ public function __construct( $this->key = $key; } - /** - * @param string $name - * @return mixed - * @throws \Exception - */ - public function __get($name) - { - switch ($name) { - case 'key': - return $this->key; - } - - return parent::__get($name); - } - /** * @return string */ diff --git a/src/InternalModel.php b/src/InternalModel.php index f6d6feb..9c0429c 100644 --- a/src/InternalModel.php +++ b/src/InternalModel.php @@ -17,6 +17,7 @@ namespace Phramework\JSONAPI; use Phramework\JSONAPI\DataSource\DatabaseDataSource; +use Phramework\JSONAPI\DataSource\IDataSource; use Phramework\JSONAPI\Model\DataSource; use Phramework\JSONAPI\Model\Directives; use Phramework\JSONAPI\Model\Relationships; @@ -67,9 +68,11 @@ class InternalModel * InternalModel constructor. * Will create a new internal model initialized with: * - defaultDirectives Page directive limit with value of getMaxPageLimit() + * - empty prepareRecords * @param string $resourceType + * @param IDataSource $dataSource null will interpreted as a new DatabaseDataSource */ - public function __construct($resourceType) + public function __construct(string $resourceType, IDataSource $dataSource = null) { $this->resourceType = $resourceType; @@ -80,46 +83,31 @@ public function __construct($resourceType) $this->filterableAttributes = new \stdClass(); $this->settings = new \stdClass(); - - $model = $this; //alias - $this->prepareRecords = function (array &$records) { - - }; - - $dataSource = $this->dataSource = new DatabaseDataSource( - $model - ); - $this->get = function () use ($dataSource) { - return $dataSource->get( - ...func_get_args() - ); + //Set default prepareRecords method as an empty method + $this->prepareRecords = function (array &$records) { }; - $this->post = function () use ($dataSource) { - return $dataSource->post( - ...func_get_args() - ); - }; + $this->dataSource = $dataSource; - $this->patch = function () use ($dataSource) { - return $dataSource->patch( - ...func_get_args() + if ($dataSource === null) { + //Set default data source + $this->dataSource = $dataSource = new DatabaseDataSource( + $this ); - }; - - $this->put = function () use ($dataSource) { - return $dataSource->put( - ...func_get_args() - ); - }; + } - //experiment + //Setup default operations to use data source + $this->get = [$dataSource, 'get']; + $this->post = [$dataSource, 'post']; + $this->patch = [$dataSource, 'patch']; + $this->put = [$dataSource, 'put']; $this->delete = [$dataSource, 'delete']; $this->validationModels = new \stdClass(); + //Set default empty filter validator $this->filterValidator = new ObjectValidator( (object) [], [], @@ -133,8 +121,9 @@ public function __construct($resourceType) * @return ValidationModel * @throws \DomainException If none validation model is set */ - public function getValidationModel(string $requestMethod = 'DEFAULT') - { + public function getValidationModel( + string $requestMethod = 'DEFAULT' + ) : ValidationModel { $key = 'DEFAULT'; if ($requestMethod !== null @@ -158,7 +147,7 @@ public function getValidationModel(string $requestMethod = 'DEFAULT') * @return $this */ public function setValidationModel( - $validationModel, + ValidationModel $validationModel, string $requestMethod = 'DEFAULT' ) { $key = 'DEFAULT'; @@ -194,17 +183,20 @@ public function setResourceType($resourceType) /** * @return string */ - public function getIdAttribute() + public function getIdAttribute() : string { return $this->idAttribute; } /** - * @return \stdClass + * @param string $idAttribute If non set, default is "id" + * @return $this */ - public function getRelationships() + public function setIdAttribute(string $idAttribute) { - return $this->relationships; + $this->idAttribute = $idAttribute; + + return $this; } public function collection( diff --git a/src/Model/Relationships.php b/src/Model/Relationships.php index 2bc3740..26b7590 100644 --- a/src/Model/Relationships.php +++ b/src/Model/Relationships.php @@ -19,6 +19,7 @@ use Phramework\Exceptions\RequestException; use Phramework\JSONAPI\Fields; +use Phramework\JSONAPI\InternalModel; use Phramework\JSONAPI\Relationship; use Phramework\JSONAPI\RelationshipResource; use Phramework\JSONAPI\Resource; @@ -56,12 +57,17 @@ public function setRelationships(\stdClass $relationships) return $this; } + /** + * @param string $relationshipKey + * @return Relationship + * @throws \DomainException If exception is not found + */ public function getRelationship(string $relationshipKey) : Relationship { $relationships = $this->getRelationships(); if (!isset($relationships->{$relationshipKey})) { - throw new \Exception(sprintf( + throw new \DomainException(sprintf( 'Not a valid relationship key "%s"', $relationshipKey )); @@ -88,26 +94,21 @@ public function issetRelationship(string $relationshipKey) : bool * @throws \Phramework\Exceptions\ServerException If relationship doesn't exist * @throws \Phramework\Exceptions\ServerException If relationship's class method is * not defined + * @todo */ public static function getRelationshipData( + InternalModel $model, $relationshipKey, $id, Fields $fields = null, $primaryDataParameters = [], $relationshipParameters = [] ) { - if (!static::issetRelationship($relationshipKey)) { - throw new \Phramework\Exceptions\ServerException(sprintf( - '"%s" is not a valid relationship key', - $relationshipKey - )); - } + $relationship = $model->getRelationship($relationshipKey); - $relationship = static::getRelationship($relationshipKey); - - switch ($relationship->type) { + switch ($relationship->getType()) { case \Phramework\JSONAPI\Relationship::TYPE_TO_ONE: - $resource = $callMethod = static::getById( + $resource = $callMethod = $model->getById( $id, $fields, $primaryDataParameters @@ -125,11 +126,11 @@ public static function getRelationshipData( ); case \Phramework\JSONAPI\Relationship::TYPE_TO_MANY: default: - if (!isset($relationship->callbacks->{Phramework::METHOD_GET})) { + if (!isset($relationship->getCallbacks()->{Phramework::METHOD_GET})) { return []; } - $callMethod = $relationship->callbacks->{Phramework::METHOD_GET}; + $callMethod = $relationship->getCallbacks()->{Phramework::METHOD_GET}; if (!is_callable($callMethod)) { throw new \Phramework\Exceptions\ServerException( @@ -140,9 +141,8 @@ public static function getRelationshipData( //also we could attempt to use getById like the above TO_ONE //to use relationships data - - return call_user_func( - $callMethod, + //todo annotate signature + return $callMethod( $id, $fields, ...$relationshipParameters @@ -171,6 +171,7 @@ public static function getRelationshipData( * ``` */ public static function getIncludedData( + InternalModel $model, $primaryData, $include = [], Fields $fields = null, @@ -190,7 +191,7 @@ public static function getIncludedData( //check if relationship exists foreach ($include as $relationshipKey) { - if (!static::issetRelationship($relationshipKey)) { + if (!$model->issetRelationship($relationshipKey)) { throw new RequestException(sprintf( 'Relationship "%s" not found', $relationshipKey @@ -253,9 +254,9 @@ public static function getIncludedData( $included = []; foreach ($include as $relationshipKey) { - $relationship = static::getRelationship($relationshipKey); + $relationship = $model->getRelationship($relationshipKey); - $relationshipModelClass = $relationship->model; + $relationshipModelClass = $relationship->getModel(); $ids = array_unique($tempRelationshipIds->{$relationshipKey}); @@ -265,7 +266,7 @@ public static function getIncludedData( : [] ); - $resources = $relationshipModelClass::getById( + $resources = $relationshipModelClass->getById( $ids, $fields, ...$additionalArgument diff --git a/tests/src/FilterAttributeTest.php b/tests/src/FilterAttributeTest.php index 48080f7..b8157cf 100644 --- a/tests/src/FilterAttributeTest.php +++ b/tests/src/FilterAttributeTest.php @@ -98,25 +98,35 @@ public function testValidateFailureAttributeJSONSecondLevel() } /** - * @covers ::__get - * @param string $property - * @param mixed $expected - * @dataProvider getAvailableProperties + * @covers ::getAttribute */ - public function testGet($property, $expected) + public function testGetAttribute() { $this->assertSame( - $this->filterAttribute->{$property}, - $expected + 'id', + $this->filterAttribute->getAttribute() ); } /** - * @covers ::__get - * @expectedException \Exception + * @covers ::getOperator */ - public function testGetFailure() + public function testGetOperator() { - $this->filterAttribute->{'not-found'}; + $this->assertSame( + Operator::OPERATOR_EQUAL, + $this->filterAttribute->getOperator() + ); + } + + /** + * @covers ::getOperand + */ + public function testGetOperand() + { + $this->assertSame( + '5', + $this->filterAttribute->getOperand() + ); } } diff --git a/tests/src/FilterJSONAttributeTest.php b/tests/src/FilterJSONAttributeTest.php index c0e3fcd..16af5e6 100644 --- a/tests/src/FilterJSONAttributeTest.php +++ b/tests/src/FilterJSONAttributeTest.php @@ -25,6 +25,9 @@ */ class FilterJSONAttributeTest extends \PHPUnit_Framework_TestCase { + /** + * @var FilterJSONAttribute + */ protected $filterAttribute; public function setUp() @@ -51,22 +54,13 @@ public function testConstruct() } /** - * @covers ::__get + * @covers ::getKey */ - public function testGet() + public function testGetKey() { $this->assertSame( 'keywords', - $this->filterAttribute->key + $this->filterAttribute->getKey() ); } - - /** - * @covers ::__get - * @expectedException \Exception - */ - public function testGetFailure() - { - $this->filterAttribute->{'not-found'}; - } }