Skip to content

Commit cf7226a

Browse files
committed
Modifications to has-many and belongs-to validators. Fixes #1
1 parent fe2ead2 commit cf7226a

File tree

4 files changed

+149
-88
lines changed

4 files changed

+149
-88
lines changed

src/Validator/Relationships/BelongsToValidator.php

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class BelongsToValidator extends AbstractValidator implements ConfigurableInterf
4040
// Error constants
4141
const ERROR_INVALID_VALUE = 'invalid-value';
4242
const ERROR_INVALID_TYPE = 'invalid-resource-type';
43-
const ERROR_INVALID_ID = 'invalid-resouce-id';
43+
const ERROR_INVALID_ID = 'invalid-resource-id';
44+
const ERROR_INCOMPLETE_IDENTIFIER = 'incomplete-identifier';
4445
const ERROR_NULL_DISALLOWED = 'relationship-required';
4546
const ERROR_NOT_FOUND = 'not-found';
4647

@@ -81,6 +82,12 @@ class BelongsToValidator extends AbstractValidator implements ConfigurableInterf
8182
ErrorObject::TITLE => 'Invalid Relationship',
8283
ErrorObject::DETAIL => 'The supplied belongs-to relationship id is missing or invalid.',
8384
],
85+
self::ERROR_INCOMPLETE_IDENTIFIER => [
86+
ErrorObject::CODE => self::ERROR_INCOMPLETE_IDENTIFIER,
87+
ErrorObject::STATUS => 400,
88+
ErrorObject::TITLE => 'Incomplete Resource Identifier',
89+
ErrorObject::DETAIL => 'The supplied resource identifier object is not complete.',
90+
],
8491
self::ERROR_NULL_DISALLOWED => [
8592
ErrorObject::CODE => self::ERROR_NULL_DISALLOWED,
8693
ErrorObject::STATUS => 422,
@@ -215,47 +222,43 @@ protected function validate($value)
215222

216223
// must be a belongs to relationship
217224
if (!$object->isBelongsTo()) {
218-
$this->error(static::ERROR_INVALID_VALUE)
219-
->source()
220-
->setPointer('/' . Relationship::DATA);
225+
$this->error(static::ERROR_INVALID_VALUE, '/' . Relationship::DATA);
221226
return;
222227
}
223228

224229
$data = $object->getData();
225230

226231
// must not be empty if empty is not allowed.
227232
if (!$data && !$this->isEmptyAllowed()) {
228-
$this->error(static::ERROR_NULL_DISALLOWED)
229-
->source()
230-
->setPointer('/' . Relationship::DATA);
233+
$this->error(static::ERROR_NULL_DISALLOWED, '/' . Relationship::DATA);
231234
}
232235

233236
// if empty, is valid at this point so return.
234237
if (!$data) {
235238
return;
236239
}
237240

241+
// must have type and id.
242+
if (!$data->hasType() || !$data->hasId()) {
243+
$this->error(static::ERROR_INCOMPLETE_IDENTIFIER, '/' . Relationship::DATA);
244+
return;
245+
}
246+
238247
// type must be acceptable
239248
if (!$data->hasType() || !$this->isType($data->getType())) {
240-
$this->error(static::ERROR_INVALID_TYPE)
241-
->source()
242-
->setPointer('/' . Relationship::DATA . '/' . ResourceIdentifier::TYPE);
249+
$this->error(static::ERROR_INVALID_TYPE, '/' . Relationship::DATA . '/' . ResourceIdentifier::TYPE);
243250
}
244251

245252
$id = $data->hasId() ? $data->getId() : null;
246253

247254
// id must be set an be either a non-empty string or an integer.
248255
if ((!is_string($id) && !is_int($id)) || (is_string($id) && empty($id))) {
249-
$this->error(static::ERROR_INVALID_ID)
250-
->source()
251-
->setPointer('/' . Relationship::DATA . '/' . ResourceIdentifier::ID);
256+
$this->error(static::ERROR_INVALID_ID, '/' . Relationship::DATA . '/' . ResourceIdentifier::ID);
252257
}
253258

254259
// check the callback, if one exists.
255260
if ($this->hasCallback() && false == call_user_func($this->getCallback(), $data)) {
256-
$this->error(static::ERROR_NOT_FOUND)
257-
->source()
258-
->setPointer('/' . Relationship::DATA);
261+
$this->error(static::ERROR_NOT_FOUND, '/' . Relationship::DATA);
259262
}
260263
}
261264
}

src/Validator/Relationships/HasManyValidator.php

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class HasManyValidator extends AbstractValidator implements ConfigurableInterfac
4242
const ERROR_INVALID_VALUE = BelongsToValidator::ERROR_INVALID_VALUE;
4343
const ERROR_INVALID_TYPE = BelongsToValidator::ERROR_INVALID_TYPE;
4444
const ERROR_INVALID_ID = BelongsToValidator::ERROR_INVALID_ID;
45+
const ERROR_INCOMPLETE_IDENTIFIER = BelongsToValidator::ERROR_INCOMPLETE_IDENTIFIER;
4546
const ERROR_EMPTY_DISALLOWED = BelongsToValidator::ERROR_NULL_DISALLOWED;
4647
const ERROR_INVALID_COLLECTION = 'invalid-resources';
4748
const ERROR_NOT_FOUND = BelongsToValidator::ERROR_NOT_FOUND;
@@ -65,6 +66,12 @@ class HasManyValidator extends AbstractValidator implements ConfigurableInterfac
6566
ErrorObject::TITLE => 'Invalid Relationship',
6667
ErrorObject::DETAIL => 'The supplied relationship id is missing or invalid.',
6768
],
69+
self::ERROR_INCOMPLETE_IDENTIFIER => [
70+
ErrorObject::CODE => self::ERROR_INCOMPLETE_IDENTIFIER,
71+
ErrorObject::STATUS => 400,
72+
ErrorObject::TITLE => 'Incomplete Resource Identifier',
73+
ErrorObject::DETAIL => 'The supplied resource identifier object is not complete.',
74+
],
6875
self::ERROR_EMPTY_DISALLOWED => [
6976
ErrorObject::CODE => self::ERROR_EMPTY_DISALLOWED,
7077
ErrorObject::STATUS => 422,
@@ -220,9 +227,7 @@ protected function validate($value)
220227

221228
// must be a has many relationship
222229
if (!$object->isHasMany()) {
223-
$this->error(static::ERROR_INVALID_VALUE)
224-
->source()
225-
->setPointer('/' . Relationship::DATA);
230+
$this->error(static::ERROR_INVALID_VALUE, '/' . Relationship::DATA);
226231
return;
227232
}
228233

@@ -231,11 +236,11 @@ protected function validate($value)
231236

232237
// if empty, empty relationship must be allowed.
233238
if ($data->isEmpty() && !$this->isEmptyAllowed()) {
234-
$this->error(static::ERROR_EMPTY_DISALLOWED)
235-
->source()
236-
->setPointer('/' . Relationship::DATA);
237-
return;
238-
} elseif ($data->isEmpty()) {
239+
$this->error(static::ERROR_EMPTY_DISALLOWED, '/' . Relationship::DATA);
240+
}
241+
242+
// if empty, is valid at this point
243+
if ($data->isEmpty()) {
239244
return;
240245
}
241246

@@ -255,22 +260,23 @@ protected function validate($value)
255260
*/
256261
protected function validateIdentifier(ResourceIdentifier $identifier, $index)
257262
{
258-
$pointer = sprintf('/%s/%s/', Relationship::DATA, $index);
263+
$pointer = sprintf('/%s/%s', Relationship::DATA, $index);
264+
265+
// type and id must both be present
266+
if (!$identifier->hasType() || !$identifier->hasId()) {
267+
$this->error(static::ERROR_INCOMPLETE_IDENTIFIER, $pointer);
268+
}
259269

260270
// type must be acceptable
261-
if (!$identifier->hasType() || !$this->isType($identifier->getType())) {
262-
$this->error(static::ERROR_INVALID_TYPE)
263-
->source()
264-
->setPointer($pointer . ResourceIdentifier::TYPE);
271+
if ($identifier->hasType() && !$this->isType($identifier->getType())) {
272+
$this->error(static::ERROR_INVALID_TYPE, $pointer . '/' . ResourceIdentifier::TYPE);
265273
}
266274

267275
$id = $identifier->hasId() ? $identifier->getId() : null;
268276

269-
// id must be set an be either a non-empty string or an integer.
270-
if ((!is_string($id) && !is_int($id)) || (is_string($id) && empty($id))) {
271-
$this->error(static::ERROR_INVALID_ID)
272-
->source()
273-
->setPointer($pointer . ResourceIdentifier::ID);
277+
// id must be set and be either a non-empty string or an integer.
278+
if ($identifier->hasId() && ((!is_string($id) && !is_int($id)) || (is_string($id) && empty($id)))) {
279+
$this->error(static::ERROR_INVALID_ID, $pointer . '/' . ResourceIdentifier::ID);
274280
}
275281
}
276282

@@ -283,9 +289,7 @@ protected function validateCallback(ResourceIdentifierCollection $collection)
283289
$pointer = '/' . Relationship::DATA;
284290

285291
if (!is_array($check) && false == $check) {
286-
$this->error(static::ERROR_INVALID_COLLECTION)
287-
->source()
288-
->setPointer($pointer);
292+
$this->error(static::ERROR_INVALID_COLLECTION, $pointer);
289293
}
290294

291295
if (!is_array($check)) {
@@ -300,9 +304,7 @@ protected function validateCallback(ResourceIdentifierCollection $collection)
300304
throw new \RuntimeException('Invalid error index.');
301305
}
302306

303-
$this->error(static::ERROR_NOT_FOUND)
304-
->source()
305-
->setPointer($pointer . '/' . $index);
307+
$this->error(static::ERROR_NOT_FOUND, $pointer . '/' . $index);
306308
}
307309
}
308310
}

test/Validator/Relationships/BelongsToValidatorTest.php

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@ class BelongsToValidatorTest extends \PHPUnit_Framework_TestCase
3131
const ID = 123;
3232
const INVALID_ID = 456;
3333

34+
/**
35+
* @var \stdClass
36+
*/
3437
protected $valid;
38+
39+
/**
40+
* @var BelongsToValidator
41+
*/
3542
protected $validator;
3643

3744
protected function setUp()
@@ -45,6 +52,24 @@ protected function setUp()
4552
$this->validator->setTypes(static::TYPE);
4653
}
4754

55+
/**
56+
* @return ErrorObject
57+
*/
58+
protected function getError()
59+
{
60+
if (1 !== count($this->validator->getErrors())) {
61+
$this->fail('Did not find a single error.');
62+
}
63+
64+
$error = current($this->validator->getErrors()->getAll());
65+
66+
if (!$error instanceof ErrorObject) {
67+
$this->fail('Not an error object.');
68+
}
69+
70+
return $error;
71+
}
72+
4873
public function testValid()
4974
{
5075
$this->assertSame($this->validator, $this->validator->setTypes(static::TYPE));
@@ -71,38 +96,51 @@ public function testInvalidType()
7196

7297
$this->assertFalse($this->validator->isValid($this->valid));
7398

74-
/** @var ErrorObject $error */
75-
$error = current($this->validator->getErrors()->getAll());
76-
77-
$this->assertInstanceOf(ErrorObject::class, $error);
99+
$error = $this->getError();
78100
$this->assertEquals(BelongsToValidator::ERROR_INVALID_TYPE, $error->getCode());
79101
$this->assertEquals(400, $error->getStatus());
80102
$this->assertEquals('/data/type', $error->source()->getPointer());
81103
}
82104

105+
public function testMissingType()
106+
{
107+
unset($this->valid->{Relationship::DATA}->{ResourceIdentifier::TYPE});
108+
109+
$this->assertFalse($this->validator->isValid($this->valid));
110+
$error = $this->getError();
111+
$this->assertEquals(BelongsToValidator::ERROR_INCOMPLETE_IDENTIFIER, $error->getCode());
112+
$this->assertEquals(400, $error->getStatus());
113+
$this->assertEquals('/data', $error->source()->getPointer());
114+
}
115+
83116
public function testInvalidId()
84117
{
85118
$this->valid->{Relationship::DATA}->{ResourceIdentifier::ID} = null;
86119
$this->assertFalse($this->validator->isValid($this->valid));
87120

88-
/** @var ErrorObject $error */
89-
$error = current($this->validator->getErrors()->getAll());
90-
91-
$this->assertInstanceOf(ErrorObject::class, $error);
121+
$error = $this->getError();
92122
$this->assertEquals(BelongsToValidator::ERROR_INVALID_ID, $error->getCode());
93123
$this->assertEquals(400, $error->getStatus());
94124
$this->assertEquals('/data/id', $error->source()->getPointer());
95125
}
96126

127+
public function testMissingId()
128+
{
129+
unset($this->valid->{Relationship::DATA}->{ResourceIdentifier::ID});
130+
131+
$this->assertFalse($this->validator->isValid($this->valid));
132+
$error = $this->getError();
133+
$this->assertEquals(BelongsToValidator::ERROR_INCOMPLETE_IDENTIFIER, $error->getCode());
134+
$this->assertEquals(400, $error->getStatus());
135+
$this->assertEquals('/data', $error->source()->getPointer());
136+
}
137+
97138
public function testHasMany()
98139
{
99140
$this->valid->{Relationship::DATA} = [];
100141
$this->assertFalse($this->validator->isValid($this->valid));
101142

102-
/** @var ErrorObject $error */
103-
$error = current($this->validator->getErrors()->getAll());
104-
105-
$this->assertInstanceOf(ErrorObject::class, $error);
143+
$error = $this->getError();
106144
$this->assertEquals(BelongsToValidator::ERROR_INVALID_VALUE, $error->getCode());
107145
$this->assertEquals(400, $error->getStatus());
108146
$this->assertEquals('/data', $error->source()->getPointer());
@@ -120,10 +158,7 @@ public function testDoNotAcceptNull()
120158
$this->assertSame($this->validator, $this->validator->setAllowEmpty(false));
121159
$this->assertFalse($this->validator->isValid($this->valid));
122160

123-
/** @var ErrorObject $error */
124-
$error = current($this->validator->getErrors()->getAll());
125-
126-
$this->assertInstanceOf(ErrorObject::class, $error);
161+
$error = $this->getError();
127162
$this->assertEquals(BelongsToValidator::ERROR_NULL_DISALLOWED, $error->getCode());
128163
$this->assertEquals(422, $error->getStatus());
129164
$this->assertEquals('/data', $error->source()->getPointer());
@@ -155,10 +190,7 @@ public function testCallbackInvalid()
155190

156191
$this->assertFalse($this->validator->isValid($this->valid));
157192

158-
/** @var ErrorObject $error */
159-
$error = current($this->validator->getErrors()->getAll());
160-
161-
$this->assertInstanceOf(ErrorObject::class, $error);
193+
$error = $this->getError();
162194
$this->assertEquals(BelongsToValidator::ERROR_NOT_FOUND, $error->getCode());
163195
$this->assertEquals(404, $error->getStatus());
164196
$this->assertEquals('/data', $error->source()->getPointer());

0 commit comments

Comments
 (0)