Skip to content

Commit a6faf8e

Browse files
authored
Merge pull request #48 from gjinali/mappers
Implement Snake Case Mapping for ViewModel Properties and Methods
2 parents 5cf6d83 + 90c72ef commit a6faf8e

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

src/ViewModel.php

+12-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ abstract class ViewModel implements Arrayable, Responsable
2020
protected $view = '';
2121

2222
protected $_data = [];
23+
protected $snakeCase = false;
2324

2425
public function toArray(): array
2526
{
@@ -58,15 +59,15 @@ protected function items(): Collection
5859
return $this->shouldIgnore($property->getName());
5960
})
6061
->mapWithKeys(function (ReflectionProperty $property) {
61-
return [$property->getName() => $this->{$property->getName()}];
62+
return [$this->resolveName($property->getName()) => $this->{$property->getName()}];
6263
});
6364

6465
$publicMethods = collect($class->getMethods(ReflectionMethod::IS_PUBLIC))
6566
->reject(function (ReflectionMethod $method) {
6667
return $this->shouldIgnore($method->getName());
6768
})
6869
->mapWithKeys(function (ReflectionMethod $method) {
69-
return [$method->getName() => $this->createVariableFromMethod($method)];
70+
return [$this->resolveName($method->getName()) => $this->createVariableFromMethod($method)];
7071
});
7172

7273

@@ -99,4 +100,13 @@ protected function createVariableFromMethod(ReflectionMethod $method)
99100

100101
return Closure::fromCallable([$this, $method->getName()]);
101102
}
103+
104+
protected function resolveName(string $name): string
105+
{
106+
if (! $this->snakeCase) {
107+
return $name;
108+
}
109+
110+
return Str::snake($name);
111+
}
102112
}

tests/SnakeCaseViewModel.php

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Spatie\ViewModels\Tests;
4+
5+
use Spatie\ViewModels\ViewModel;
6+
use stdClass;
7+
8+
class SnakeCaseViewModel extends ViewModel
9+
{
10+
protected $snakeCase = true;
11+
12+
public $dummyProperty = 'abc';
13+
14+
protected $ignore = ['ignoredMethod'];
15+
16+
public function __construct()
17+
{
18+
// This one is here for testing purposes
19+
}
20+
21+
public function publishedPost(): stdClass
22+
{
23+
return (object) [
24+
'title' => 'title',
25+
'body' => 'body',
26+
];
27+
}
28+
29+
public function availableCategories(): array
30+
{
31+
return [
32+
(object) [
33+
'name' => 'category A',
34+
],
35+
(object) [
36+
'name' => 'category B',
37+
],
38+
];
39+
}
40+
41+
public function ignoredMethod(): bool
42+
{
43+
return true;
44+
}
45+
46+
public function callableMethod(string $name): string
47+
{
48+
return $name;
49+
}
50+
}

tests/SnakeCaseViewModelTest.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
use Illuminate\Http\JsonResponse;
4+
use Spatie\ViewModels\Tests\SnakeCaseViewModel;
5+
6+
beforeEach(function () {
7+
$this->viewModel = new SnakeCaseViewModel();
8+
});
9+
10+
test('public methods are listed and mapped as snake case', function () {
11+
$array = $this->viewModel->toArray();
12+
13+
expect($array)->toHaveKeys(['published_post', 'available_categories']);
14+
});
15+
16+
test('public properties are listed and mapped as snake case', function () {
17+
$array = $this->viewModel->toArray();
18+
19+
expect($array)->toHaveKey('dummy_property');
20+
});
21+
22+
test('values are kept as they are', function () {
23+
$array = $this->viewModel->toArray();
24+
25+
expect($array['published_post']->title)->toEqual('title');
26+
});
27+
28+
test('callables can be stored', function () {
29+
$array = $this->viewModel->toArray();
30+
31+
expect($array['callable_method']('foo'))->toEqual('foo');
32+
});
33+
34+
test('to response returns json by default', function () {
35+
$response = $this->viewModel->toResponse(createRequest());
36+
37+
expect($response)->toBeInstanceOf(JsonResponse::class);
38+
39+
$array = getResponseBody($response);
40+
41+
expect($array)->toHaveKeys(['published_post', 'available_categories']);
42+
});

0 commit comments

Comments
 (0)