Skip to content

Commit 45a8e61

Browse files
committed
member names
1 parent 5b7afaf commit 45a8e61

File tree

5 files changed

+95
-25
lines changed

5 files changed

+95
-25
lines changed

src/JsonApiParser.php

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,20 @@
1313
class JsonApiParser extends JsonParser
1414
{
1515
/**
16-
* @var array|callable|null
16+
* Converts 'type' member to form name
17+
* If not set, type will be converted to singular form.
18+
* For example, 'articles' will be converted to 'Article'
19+
* @var callable
1720
*/
18-
protected $formNameCallback;
19-
20-
public function __construct($formNameCallback = null)
21-
{
22-
if ($formNameCallback === null) {
23-
$formNameCallback = [$this, 'typeToFormName'];
24-
}
25-
if (!is_callable($formNameCallback, true)) {
26-
throw new InvalidConfigException('JsonApiParser::formNameCallback should be callable');
27-
}
28-
29-
$this->formNameCallback = $formNameCallback;
30-
}
21+
public $formNameCallback;
3122

23+
/**
24+
* Converts member names to variable names
25+
* If not set, all special characters will be replaced by underscore
26+
* For example, 'first-name' will be converted to 'first_name'
27+
* @var callable
28+
*/
29+
public $memberNameCallback;
3230

3331
/**
3432
* Parse resource object into the input data to populates the model
@@ -38,9 +36,9 @@ public function parse($rawBody, $contentType)
3836
{
3937
$array = parent::parse($rawBody, $contentType);
4038
if ($type = ArrayHelper::getValue($array, 'data.type')) {
41-
$formName = call_user_func($this->formNameCallback, $type);
39+
$formName = $this->typeToFormName($type);
4240
if ($attributes = ArrayHelper::getValue($array, 'data.attributes')) {
43-
$result[$formName] = $attributes;
41+
$result[$formName] = array_combine($this->parseMemberNames(array_keys($attributes)), array_values($attributes));
4442
} elseif ($id = ArrayHelper::getValue($array, 'data.id')) {
4543
$result[$formName] = ['id' => $id, 'type' => $type];
4644
}
@@ -49,12 +47,12 @@ public function parse($rawBody, $contentType)
4947
if (isset($relationship[0])) {
5048
foreach ($relationship as $item) {
5149
if (isset($item['type']) && isset($item['id'])) {
52-
$formName = call_user_func($this->formNameCallback, $item['type']);
50+
$formName = $this->typeToFormName($item['type']);
5351
$result[$name][$formName][] = $item;
5452
}
5553
}
5654
} elseif (isset($relationship['type']) && isset($relationship['id'])) {
57-
$formName = call_user_func($this->formNameCallback, $relationship['type']);
55+
$formName = $this->typeToFormName($relationship['type']);
5856
$result[$name][$formName] = $relationship;
5957
}
6058
}
@@ -63,16 +61,35 @@ public function parse($rawBody, $contentType)
6361
$data = ArrayHelper::getValue($array, 'data', []);
6462
foreach ($data as $relationLink) {
6563
if (isset($relationLink['type']) && isset($relationLink['id'])) {
66-
$formName = call_user_func($this->formNameCallback, $relationLink['type']);
64+
$formName = $this->typeToFormName($relationLink['type']);
6765
$result[$formName][] = $relationLink;
6866
}
6967
}
7068
}
7169
return isset($result) ? $result : $array;
7270
}
7371

72+
/**
73+
* @param $type 'type' member of the document
74+
* @return string form name
75+
*/
7476
protected function typeToFormName($type)
7577
{
78+
if ($this->formNameCallback !== null) {
79+
return call_user_func($this->formNameCallback, $type);
80+
}
7681
return Inflector::id2camel(Inflector::singularize($type));
7782
}
83+
84+
/**
85+
* @param array $memberNames
86+
* @return array variable names
87+
*/
88+
protected function parseMemberNames(array $memberNames = [])
89+
{
90+
$callback = $this->memberNameCallback !== null ? $this->memberNameCallback : function($name) {
91+
return str_replace(' ', '_', preg_replace('/[^A-Za-z0-9]+/', ' ', $name));
92+
};
93+
return array_map($callback, $memberNames);
94+
}
7895
}

src/Serializer.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use yii\base\Model;
1111
use yii\data\DataProviderInterface;
1212
use yii\data\Pagination;
13+
use yii\helpers\Inflector;
1314
use yii\web\Link;
1415
use yii\web\Linkable;
1516
use yii\web\Request;
@@ -54,6 +55,15 @@ class Serializer extends Component
5455
*/
5556
public $pluralize = true;
5657

58+
/**
59+
* Prepares the member name that should be returned.
60+
* If not set, all member names will be converted to recommended format.
61+
* For example, both 'firstName' and 'first_name' will be converted to 'first-name'.
62+
* @var callable
63+
*/
64+
public $prepareMemberName;
65+
66+
5767
/**
5868
* @inheritdoc
5969
*/
@@ -94,10 +104,13 @@ public function serialize($data)
94104
protected function serializeModel(ResourceInterface $model)
95105
{
96106
$fields = $this->getRequestedFields();
107+
$fields = isset($fields[$model->getType()]) ? $fields[$model->getType()] : [];
108+
109+
$attributes = $model->getResourceAttributes($fields);
110+
$attributes = array_combine($this->prepareMemberNames(array_keys($attributes)), array_values($attributes));
97111

98-
$attributes = isset($fields[$model->getType()]) ? $fields[$model->getType()] : [];
99112
$data = array_merge($this->serializeIdentifier($model), [
100-
'attributes' => $model->getResourceAttributes($attributes),
113+
'attributes' => $attributes,
101114
]);
102115

103116
$included = $this->getIncluded();
@@ -291,7 +304,7 @@ protected function getRequestedFields()
291304
$fields = [];
292305
}
293306
foreach ($fields as $key => $field) {
294-
$fields[$key] = preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY);
307+
$fields[$key] = preg_split('/\s*,\s*/', $field, -1, PREG_SPLIT_NO_EMPTY);
295308
}
296309
return $fields;
297310
}
@@ -301,4 +314,19 @@ protected function getIncluded()
301314
$include = $this->request->get($this->expandParam);
302315
return is_string($include) ? preg_split('/\s*,\s*/', $include, -1, PREG_SPLIT_NO_EMPTY) : [];
303316
}
317+
318+
319+
/**
320+
* Format member names according to recommendations for JSON API implementations
321+
* @link http://jsonapi.org/format/#document-member-names
322+
* @param array $memberNames
323+
* @return array
324+
*/
325+
protected function prepareMemberNames(array $memberNames = [])
326+
{
327+
$callback = $this->prepareMemberName !== null ? $this->prepareMemberName : function($name) {
328+
return Inflector::camel2id(Inflector::variablize($name));
329+
};
330+
return array_map($callback, $memberNames);
331+
}
304332
}

tests/JsonApiParserTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public function testParse()
2121
'attributes' => [
2222
'field1' => 'test',
2323
'field2' => 2,
24+
'first-name' => 'Bob'
2425
],
2526
'relationships' => [
2627
'author' => [
@@ -33,7 +34,8 @@ public function testParse()
3334
$this->assertEquals([
3435
'ResourceModel' => [
3536
'field1' => 'test',
36-
'field2' => 2
37+
'field2' => 2,
38+
'first_name' => 'Bob',
3739
],
3840
'author' => [
3941
'ResourceModel' => [

tests/SerializerTest.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ public function testSerializeModelData()
6666
]
6767
], $serializer->serialize($model));
6868

69-
ResourceModel::$fields = ['field1'];
69+
ResourceModel::$fields = ['first_name'];
7070
ResourceModel::$extraFields = [];
7171
$this->assertSame([
7272
'data' => [
7373
'id' => '123',
7474
'type' => 'resource-models',
7575
'attributes' => [
76-
'field1' => 'test',
76+
'first-name' => 'Bob',
7777
],
7878
'links' => [
7979
'self' => ['href' => 'http://example.com/resource/123']
@@ -291,6 +291,28 @@ public function testSerializeDataProvider($dataProvider, $expectedResult)
291291
$this->assertEquals($expectedResult, $serializer->serialize($dataProvider));
292292
}
293293

294+
public function testFieldSets()
295+
{
296+
$serializer = new Serializer();
297+
ResourceModel::$id = 123;
298+
ResourceModel::$fields = ['field1', 'first_name', 'field2'];
299+
$model = new ResourceModel();
300+
301+
\Yii::$app->request->setQueryParams(['fields' => ['resource-models' => 'first_name']]);
302+
$this->assertSame([
303+
'data' => [
304+
'id' => '123',
305+
'type' => 'resource-models',
306+
'attributes' => [
307+
'first-name' => 'Bob',
308+
],
309+
'links' => [
310+
'self' => ['href' => 'http://example.com/resource/123']
311+
]
312+
]
313+
], $serializer->serialize($model));
314+
}
315+
294316
public function testTypeInflection()
295317
{
296318
$serializer = new Serializer();

tests/data/ResourceModel.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class ResourceModel extends Model implements ResourceInterface, LinksInterface
2121
public static $extraFields = [];
2222
public $field1 = 'test';
2323
public $field2 = 2;
24+
public $first_name = 'Bob';
2425
public $username = '';
2526
public $extraField1 = 'testExtra';
2627
public $extraField2 = 42;

0 commit comments

Comments
 (0)