You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: README.md
+293-14
Original file line number
Diff line number
Diff line change
@@ -1,15 +1,15 @@
1
-
# :package_description
1
+
# This is my package LaravelDataResource
2
2
3
-
[](https://packagist.org/packages/vendor_slug/package_slug)
[](https://packagist.org/packages/spatie/laravel-data-resource)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
25
25
@@ -30,19 +30,19 @@ We highly appreciate you sending us a postcard from your hometown, mentioning wh
This is the contents of the published config file:
@@ -54,9 +54,288 @@ return [
54
54
55
55
## Usage
56
56
57
+
Data objects are structured entities within Sail that fullfill the role of a data transfer object and a resource.
58
+
59
+
They have a few advantages:
60
+
61
+
- One structure for both the resource and data
62
+
- Can be lazy (=only send required data needed)
63
+
- They are type safe
64
+
- TypeScript transformer knows exactly what to do with them
65
+
66
+
There are situations where the unified data objects aren't a good fit. For example when the structure of the DTO and resource differ too much or in the case where a DTO is required but a resource isn't. In such case you'd better create a seperate resource and/or dto.
67
+
68
+
## Creating Data objects
69
+
70
+
A data object extends from `Data` and looks like this:
71
+
72
+
```php
73
+
class ContentThemeData extends Data
74
+
{
75
+
public function __construct(
76
+
public string $name
77
+
) {
78
+
}
79
+
80
+
public static function create(ContentTheme $theme): self
81
+
{
82
+
return new self(
83
+
$theme->name
84
+
);
85
+
}
86
+
}
87
+
88
+
```
89
+
90
+
In the constructor we define the properties associated with this data object. Each data object also should have a static `create` method that will create the object based upon a model. This is required for automatically creating collections of resources and logging activity.
91
+
92
+
## Using Data objects as dto
93
+
94
+
Since the data objects are just simple PHP objects with some extra methods added to them, you can use them like regular PHP dto's:
95
+
96
+
```php
97
+
$data = new ContentThemeResource('Hello world');
98
+
```
99
+
100
+
You probably going to create a dto when receiving data from a form in the frontend. There are going to be two points where this happens: when you create something and when you edit something. That's why we'll create the data object within the request:
101
+
102
+
```php
103
+
class ContentThemeRequest extends Request
104
+
{
105
+
public function rules(): array
106
+
{
107
+
return [
108
+
'name' => ['required', 'string'],
109
+
];
110
+
}
111
+
112
+
public function getData(): ContentThemeData
113
+
{
114
+
$validated = $this->validated();
115
+
116
+
return new ContentThemeData(
117
+
$validated['name']
118
+
);
119
+
}
120
+
}
121
+
```
122
+
123
+
This has two advantages:
124
+
125
+
- your validation rules and data objects will be created in the same class
126
+
- you can create the same data object in different requests with slightly different properties
127
+
128
+
Since PHP supports the spread operator, for simple data objects you could do the following:
129
+
130
+
```php
131
+
public function getData(): ContentThemeData
132
+
{
133
+
return new ContentThemeData(...$this->validated());
134
+
}
135
+
```
136
+
137
+
## Using Data objects as resource
138
+
139
+
When creating a resource you'll probably have a model, so you can call the `create` method:
140
+
141
+
```php
142
+
ContentThemeData::create($this->contentTheme);
143
+
```
144
+
145
+
At the moment you're creating a new model, in this case the model is `null`, you can use the `empty` method:
146
+
147
+
```php
148
+
ContentThemeData::empty();
149
+
```
150
+
151
+
This will return an array that follows the structure of the data object, it is possible to change the default values within this array by providing them in the constructor of the data object:
152
+
153
+
```php
154
+
class ContentThemeData extends Data
155
+
{
156
+
public function __construct(
157
+
public string $name = 'Hello world'
158
+
) {
159
+
}
160
+
161
+
public static function create(ContentTheme $theme): self
162
+
{
163
+
return new self(
164
+
$theme->name
165
+
);
166
+
}
167
+
}
168
+
```
169
+
170
+
Or by passing defaults within the `empty` call:
171
+
172
+
173
+
```php
174
+
ContentThemeData::empty([
175
+
'name' => 'Hello world',
176
+
]);
177
+
```
178
+
179
+
In your view models the `values method will now look like this:
180
+
181
+
```php
182
+
public function values(): ContentThemeData | array
183
+
{
184
+
return $this->contentTheme
185
+
? ContentThemeData::create($this->contentTheme)
186
+
: ContentThemeData::empty();
187
+
}
188
+
```
189
+
190
+
### Indexes
191
+
192
+
We already took a look at using data objects within create and edit pages, but index pages also need resources albeit a collection of resources. You can easily create a collection of resources as such:
As you can see we provide the `collection` method a paginated collection, the data object is smart enough to create a paginated response from this with links to the next, previous, last, ... pages.
208
+
209
+
When you yust provide a collection or array of resources to the `collection` method of a data object, then it will just return a `DataCollection` which is just a simple collection of resources:
210
+
211
+
```php
212
+
ContentThemeIndexData::collection($contentThemes); // No pagination here
213
+
```
214
+
215
+
### endpoints
216
+
217
+
Each data object can also have endpoints, you define these within a seperate `endpoints` method:
218
+
219
+
```php
220
+
class ContentThemeIndexData extends Data
221
+
{
222
+
public function __construct(
223
+
public ContentThemeUuid $uuid,
224
+
public string $name
225
+
) {
226
+
}
227
+
228
+
public static function create(ContentTheme $theme): self
When this object is transformed to a resource, an extra key will be present with the endpoints. Also TypeScript transformer is smart enough to understand which endpoints this data object has.
247
+
248
+
When creating this data object as an dto, no endpoints will be added. And when an `endpoints` method wasn't added to the data object, then the resource representation of the data object won't include an endpoints key.
249
+
250
+
### Using collections of data objects within data objects
251
+
252
+
When you have a data object which has a collection of data objects as a property, then always type this as a `DataCollection` with an annotation for the resources within the collection:
253
+
254
+
```php
255
+
class ApplicationData extends Data
256
+
{
257
+
public function __construct(
258
+
/** @var \App\Data\ContentThemeData[] */
259
+
public DataCollection $themes
260
+
) {
261
+
}
262
+
263
+
public static function create(Event $event): static
264
+
{
265
+
return new self(
266
+
ContentThemeData::collection($app->themes)
267
+
);
268
+
}
269
+
}
270
+
```
271
+
272
+
This will make sure data objects can be lazy loaded and that activity logging works as expected.
273
+
274
+
### Resolving resources from Data objects
275
+
276
+
You can convert a data object to a resource(array) as such:
277
+
278
+
```php
279
+
ContentThemeData::create($theme)->toArray();
280
+
```
281
+
282
+
When you want an array representation of the data object without transforming the properties like converting the underlying resources into arrays, then you can use:
283
+
284
+
285
+
```php
286
+
ContentThemeData::create($theme)->toArray();
287
+
```
288
+
289
+
A data object is also `Responsable` so it can be returned in a controller:
290
+
291
+
```php
292
+
public function (ContentTheme $theme): ContentThemeData
293
+
{
294
+
return ContentThemeData::create($theme;)
295
+
}
296
+
```
297
+
298
+
Collections of data objects are also `Responsable` and `toArray()` can be called on them.
299
+
300
+
## Lazy properties
301
+
302
+
Data objects support lazy properties by default, they allow you to only output certain properties when converting a data object to a resource. You create a lazy property as such:
303
+
304
+
```php
305
+
class ContentThemeData extends Data
306
+
{
307
+
public function __construct(
308
+
public string $name,
309
+
public int | Lazy $usages,
310
+
) {
311
+
}
312
+
313
+
public static function create(ContentTheme $theme): self
314
+
{
315
+
return new self(
316
+
$theme->name,
317
+
Lazy::create(fn() => $theme->calculateUsages()),
318
+
);
319
+
}
320
+
}
321
+
```
322
+
323
+
Now when you output this data object as a resource, the `usages` property will be missing since it is quite expensive to calculate:
ContentThemeData::create($theme)->include('usages')->toArray(); // with usages
333
+
```
334
+
335
+
This will also work when we use a data object within a collection, let's take a look at the `ApplicationData` resource from earlier. This one has a `DataCollection` property with `ContentThemeResources` within it. We now can include the `usages` property for all these resources by using the dot notation:
336
+
57
337
```php
58
-
$skeleton = new VendorName\Skeleton();
59
-
echo $skeleton->echoPhrase('Hello, Spatie!');
338
+
ContentThemeData::create($theme)->include('themes.usages')->toArray(); // with usages
60
339
```
61
340
62
341
## Testing
@@ -79,7 +358,7 @@ Please review [our security policy](../../security/policy) on how to report secu
0 commit comments