Skip to content

Commit 23def87

Browse files
authored
Merge pull request #82 from phpsa/feature/response_overiders
Feature/response overiders
2 parents 2608b18 + b890572 commit 23def87

File tree

5 files changed

+114
-55
lines changed

5 files changed

+114
-55
lines changed

README.md

+58-48
Original file line numberDiff line numberDiff line change
@@ -30,38 +30,38 @@ php artisan vendor:publish --provider="Phpsa\LaravelApiController\ServiceProvide
3030

3131
This will create a Api/ModelNameController for you and you will have the basic routes in place as follows:
3232

33-
- GET `api/v1/{model_name}` - list all/paged/filtered (class::index)
34-
- GET `api/v1/{model_name}/$id` - Show a specified id (class::show)
35-
- POST `api/v1/{model_name}` - Insert a new record (class::store)
36-
- PUT `api/v1/{model_name}/$id` - Update an existing record (class::update)
37-
- DELETE `api/v1/{model_name}/$id` - Delete an existing record (class::destroy)
33+
- GET `api/v1/{model_name}` - list all/paged/filtered (class::index)
34+
- GET `api/v1/{model_name}/$id` - Show a specified id (class::show)
35+
- POST `api/v1/{model_name}` - Insert a new record (class::store)
36+
- PUT `api/v1/{model_name}/$id` - Update an existing record (class::update)
37+
- DELETE `api/v1/{model_name}/$id` - Delete an existing record (class::destroy)
3838

3939
You can override the methods by simply putting in your own methods to override - method names in braces above
4040

4141
## Events
4242

43-
- POST (class::store) - triggers a new `Phpsa\LaravelApiController\Events\Created` Event which has the new record available as `$record`
44-
- PUT (class::update) - triggers a new `Phpsa\LaravelApiController\Events\Updated` Event which has the updated record available as `$record`
45-
- DELETE (class::destry) - triggers a new `Phpsa\LaravelApiController\Events\Deleted` Event which has the deleted record available as `$record`
43+
- POST (class::store) - triggers a new `Phpsa\LaravelApiController\Events\Created` Event which has the new record available as `$record`
44+
- PUT (class::update) - triggers a new `Phpsa\LaravelApiController\Events\Updated` Event which has the updated record available as `$record`
45+
- DELETE (class::destry) - triggers a new `Phpsa\LaravelApiController\Events\Deleted` Event which has the deleted record available as `$record`
4646

4747
## Policies
4848

4949
Policies: https://laravel.com/docs/6.x/authorization#generating-policies
5050

5151
Generate with `php artisan make:policy PostPolicy --model=Post`
5252

53-
- Get list - calls the `viewAny` policy
54-
- Get single - calls the `view` policy
55-
- Post New - calls the `create` policy
56-
- Put Update - calls the `update` policy
57-
- Delete item - calls the `delete` policy
53+
- Get list - calls the `viewAny` policy
54+
- Get single - calls the `view` policy
55+
- Post New - calls the `create` policy
56+
- Put Update - calls the `update` policy
57+
- Delete item - calls the `delete` policy
5858

5959
Query/Data modifiers in policies for the api endpoints
6060

61-
- `qualifyCollectionQueryWithUser($user, $repository)` -> return void - add any queries to the repository (ie ->where('x','))
62-
- `qualifyItemQueryWithUser($user, $repository)`-> return void - add any queries to the repository (ie ->where('x','))
63-
- `qualifyStoreDataWithUser($data)` - return the updated data array
64-
- `qualifyUpdateDataWithUser($data)` - return the updated data array
61+
- `qualifyCollectionQueryWithUser($user, $repository)` -> return void - add any queries to the repository (ie ->where('x','))
62+
- `qualifyItemQueryWithUser($user, $repository)`-> return void - add any queries to the repository (ie ->where('x','))
63+
- `qualifyStoreDataWithUser($data)` - return the updated data array
64+
- `qualifyUpdateDataWithUser($data)` - return the updated data array
6565

6666
## Resources / Collections (Transforming)
6767

@@ -84,8 +84,8 @@ in your controller override the following params:
8484

8585
## Snake vs Camel
8686

87-
- middleware to convert all camel to snake: `Phpsa\LaravelApiController\Http\Middleware\SnakeCaseInputs`
88-
- set request header `X-Accept-Case-Type` to either `snake` or `camel` to alter your data response
87+
- middleware to convert all camel to snake: `Phpsa\LaravelApiController\Http\Middleware\SnakeCaseInputs`
88+
- set request header `X-Accept-Case-Type` to either `snake` or `camel` to alter your data response
8989

9090
## Filtering
9191

@@ -137,15 +137,15 @@ You can easily filter using any related model that is configured for `include`.
137137

138138
By default all fields are returned, you can limit that to specific fields in the following ways:
139139

140-
- Api Controller parameter `$defaultFields` default as `protected $defaultFields = ['*'];` - switch to include an array of fields
141-
- fields param in url querystring: ie `fields=id,name,age` = will only return those, this will also override the above.
142-
- in your response resource you can set the static::allowedFields to lock down which fields are returnable
143-
- `addfields` and `removefields` params in url querystring will work with these.
144-
- Use laravel [eloquent model `$appends`](https://laravel.com/docs/6.x/eloquent-serialization#appending-values-to-json) property to automatically include custom attribute accessors.
140+
- Api Controller parameter `$defaultFields` default as `protected $defaultFields = ['*'];` - switch to include an array of fields
141+
- fields param in url querystring: ie `fields=id,name,age` = will only return those, this will also override the above.
142+
- in your response resource you can set the static::allowedFields to lock down which fields are returnable
143+
- `addfields` and `removefields` params in url querystring will work with these.
144+
- Use laravel [eloquent model `$appends`](https://laravel.com/docs/6.x/eloquent-serialization#appending-values-to-json) property to automatically include custom attribute accessors.
145145

146146
## Relationships
147147

148-
- Using the relationships defined in your models, you can pass a comma delimited list eg `include=join1,join2` which will return those joins (one or many).
148+
- Using the relationships defined in your models, you can pass a comma delimited list eg `include=join1,join2` which will return those joins (one or many).
149149

150150
Simply add a `protected static $mapResources` to your `Resource` to define which resources to assign your related data. E.e., for a one to many relationship, you should specify a collection, and a one-to-one relationship specify the related resource directly. This will allow the API to properly format the related record.
151151

@@ -158,24 +158,24 @@ Simply add a `protected static $mapResources` to your `Resource` to define which
158158

159159
- You can automatically update and create related records for most types of relationships. Just include the related resource name in your POST or PUT request.
160160

161-
For `BelongsToMany` or `MorphToMany` relationships, you can choose the sync strategy. By default, this will take an *additive* strategy. That is to say, related records sent will be ADDED to any existing related records. On a request-by-request basis, you can opt for a *sync* strategy which will remove the pivot for any related records not listed in the request. Note the actual related record will not be removed, just the pivot entry.
161+
For `BelongsToMany` or `MorphToMany` relationships, you can choose the sync strategy. By default, this will take an _additive_ strategy. That is to say, related records sent will be ADDED to any existing related records. On a request-by-request basis, you can opt for a _sync_ strategy which will remove the pivot for any related records not listed in the request. Note the actual related record will not be removed, just the pivot entry.
162162

163-
To opt for the *sync* behavaiour, set `?sync[field]=true` in your request.
163+
To opt for the _sync_ behavaiour, set `?sync[field]=true` in your request.
164164

165165
## Sorting
166166

167-
- Sorts can be passed as comma list aswell, ie `sort=age asc` or `sort=age asc,name desc,eyes` - generates sql of `sort age asc` and `sort age asc, name desc, eyes asc` respectively
168-
- Default sort can also be added on the controller using by overrideing the `protected $defaultSort = null;` parameter
167+
- Sorts can be passed as comma list aswell, ie `sort=age asc` or `sort=age asc,name desc,eyes` - generates sql of `sort age asc` and `sort age asc, name desc, eyes asc` respectively
168+
- Default sort can also be added on the controller using by overrideing the `protected $defaultSort = null;` parameter
169169

170170
## Pagination
171171

172-
- pagination can be enabled/disbled on the controller by overriding the `protected $defaultLimit = 25;` on the controller
173-
- pagination can also be passed via the url using `limit=xx&page=y`
174-
- pagination can also be limited to a max per page by overriding the `protected $maximumLimit = false;` parameter
172+
- pagination can be enabled/disbled on the controller by overriding the `protected $defaultLimit = 25;` on the controller
173+
- pagination can also be passed via the url using `limit=xx&page=y`
174+
- pagination can also be limited to a max per page by overriding the `protected $maximumLimit = false;` parameter
175175

176176
## Validation
177177

178-
- When Posting a new record, validation can be done by adding a `rulesForCreate` method to your controller returning an array eg
178+
- When Posting a new record, validation can be done by adding a `rulesForCreate` method to your controller returning an array eg
179179

180180
```php
181181
[
@@ -186,22 +186,22 @@ To opt for the *sync* behavaiour, set `?sync[field]=true` in your request.
186186

187187
see https://laravel.com/docs/5.8/validation#conditionally-adding-rules
188188

189-
- for updating a record, add a method `rulesForUpdate` per above.
189+
- for updating a record, add a method `rulesForUpdate` per above.
190190

191191
## Defaults
192192

193193
The following parameters are set in the Base Api controller and can be overwritten by your Controller on a case by case basis:
194194

195-
- **DEPRECATED** `protected $resourceKeySingular = 'data';`
196-
- **DEPRECATED** `protected $resourceKeyPlural = 'data';`
195+
- **DEPRECATED** `protected $resourceKeySingular = 'data';`
196+
- **DEPRECATED** `protected $resourceKeyPlural = 'data';`
197197

198-
- `protected $resourceSingle = JsonResource::class;` Collection to use for your single resource
199-
- `protected $resourceCollection = ResourceCollection::class;` Collection to use for your resource collection
200-
- `protected $defaultFields = ['*'];` Default Fields to respond with
201-
- `protected $defaultSort = null;` Set the default sorting for queries.
202-
- `protected $defaultLimit = 25;` Number of items displayed at once if not specified. (0 = maximumLimit)
203-
- `protected $maximumLimit = 0;` Maximum limit that can be set via \$\_GET['limit']. - this ties in with the defaultLimit aswell, and if wanting to disable pagination , both should be 0. ) will allow all records to be returned in a single call.
204-
- `protected $unguard = false;` Do we need to unguard the model before create/update?
198+
- `protected $resourceSingle = JsonResource::class;` Collection to use for your single resource
199+
- `protected $resourceCollection = ResourceCollection::class;` Collection to use for your resource collection
200+
- `protected $defaultFields = ['*'];` Default Fields to respond with
201+
- `protected $defaultSort = null;` Set the default sorting for queries.
202+
- `protected $defaultLimit = 25;` Number of items displayed at once if not specified. (0 = maximumLimit)
203+
- `protected $maximumLimit = 0;` Maximum limit that can be set via \$\_GET['limit']. - this ties in with the defaultLimit aswell, and if wanting to disable pagination , both should be 0. ) will allow all records to be returned in a single call.
204+
- `protected $unguard = false;` Do we need to unguard the model before create/update?
205205

206206
## Scopes
207207

@@ -222,21 +222,31 @@ class MyModelResource extends ApiResource
222222

223223
you can now append `withTrashed=1` or `onlyTrashed=1` to your query.
224224

225+
## Responses
226+
227+
you can override responses for each point by overriding the following protected methods:
228+
229+
- handleIndexResponse
230+
- handleStoreResponse
231+
- handleShowResponse
232+
- handleUpdateResponse
233+
- handleDestroyResponse
234+
225235
## Security
226236

227237
If you discover any security related issues, please email
228238
instead of using the issue tracker.
229239

230240
## Credits
231241

232-
- [Craig G Smith](https://github.com/phpsa)
233-
- [Sam Sehnert](https://github.com/samatcd)
234-
- [Phil Taylor](https://github.com/codeberry)
235-
- [All contributors](https://github.com/phpsa/laravel-api-controller/graphs/contributors)
242+
- [Craig G Smith](https://github.com/phpsa)
243+
- [Sam Sehnert](https://github.com/samatcd)
244+
- [Phil Taylor](https://github.com/codeberry)
245+
- [All contributors](https://github.com/phpsa/laravel-api-controller/graphs/contributors)
236246

237247
## Sponsors
238248

239-
- [Custom D](https://customd.com)
249+
- [Custom D](https://customd.com)
240250

241251
[badge_laravel]: https://img.shields.io/badge/Laravel-5.8%20to%207-orange.svg?style=flat-square
242252
[badge_issues]: https://img.shields.io/github/issues/ARCANEDEV/Support.svg?style=flat-square

src/Contracts/Relationships.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ protected function storeRelated($item, array $includes, array $data): void
110110
break;
111111
default:
112112
throw new ApiException("$type mapping not implemented yet");
113-
break;
113+
break;
114114
}
115115
$item->load($with);
116116
}

src/Helpers.php

+23
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,35 @@ public static function filterFieldsFromRequest($request, ?array $defaultFields,
149149
$extra = $request->has($includeFieldParam) ? explode(',', $request->input($includeFieldParam)) : [];
150150
$fields = array_merge($fields, $extra);
151151

152+
//put || post
153+
154+
$fields = array_merge($fields, self::fieldsFromPutPost($request, $fields));
155+
152156
$excludes = $request->has($removeFieldParam) ? explode(',', $request->input($removeFieldParam)) : [];
153157
$remaining = self::excludeArrayValues($fields, $excludes, $extraFields);
154158

155159
return array_unique($remaining);
156160
}
157161

162+
protected static function fieldsFromPutPost($request, $fields): array
163+
{
164+
$method = $request->method();
165+
if (! in_array($method, ['PUT','POST','PATCH'])) {
166+
return [];
167+
}
168+
return array_values(collect($request->all())->filter(function ($item, $key) use ($fields) {
169+
if (in_array($key, $fields)) {
170+
return false;
171+
}
172+
if (! is_array($item) && ! is_object($key)) {
173+
return false;
174+
}
175+
return true;
176+
})->map(function ($item, $key) {
177+
return $key;
178+
})->toArray());
179+
}
180+
158181
/**
159182
* method to remove array values.
160183
*

src/Http/Api/Contracts/HasResponse.php

+26
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,30 @@ protected function errorNotImplemented($message = 'Not implemented', ?array $err
242242
}
243243
throw new HttpException(501, $message);
244244
}
245+
246+
247+
protected function handleIndexResponse($items)
248+
{
249+
return $this->respondWithMany($items);
250+
}
251+
252+
protected function handleStoreResponse($item)
253+
{
254+
return $this->respondItemCreated($item);
255+
}
256+
257+
protected function handleShowResponse($item)
258+
{
259+
return $this->respondWithOne($item);
260+
}
261+
262+
protected function handleUpdateResponse($item)
263+
{
264+
return $this->respondWithOne($item);
265+
}
266+
267+
protected function handleDestroyResponse($id)
268+
{
269+
return $this->respondNoContent();
270+
}
245271
}

src/Http/Api/Controller.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public function handleIndexAction($request, array $extraParams = [])
8686

8787
$items = $limit > 0 ? $this->repository->paginate($limit, $fields) : $this->repository->get($fields);
8888

89-
return $this->respondWithMany($items);
89+
return $this->handleIndexResponse($items);
9090
}
9191

9292
public function handleIndexActionRaw($request, array $extraParams = [])
@@ -97,7 +97,7 @@ public function handleIndexActionRaw($request, array $extraParams = [])
9797

9898
$items = $limit > 0 ? $this->repository->paginateRaw($limit, $fields) : $this->repository->getRaw($fields);
9999

100-
return $this->respondWithMany($items);
100+
return $this->handleIndexResponse($items);
101101
}
102102

103103
protected function handleIndexActionCommon($request, array $extraParams = [])
@@ -159,7 +159,7 @@ public function handleStoreAction($request, array $extraParams = [])
159159

160160
DB::commit();
161161

162-
return $this->respondItemCreated($this->repository->getById($item->getKey()));
162+
return $this->handleStoreResponse($item);
163163
} catch (\Exception $exception) {
164164
$message = config('app.debug') ? $exception->getMessage() : 'Failed to create Record';
165165

@@ -191,7 +191,7 @@ public function handleShowAction($id, $request, array $extraParams = [])
191191
return $this->errorNotFound('Record not found');
192192
}
193193

194-
return $this->respondWithOne($item);
194+
return $this->handleShowResponse($item);
195195
}
196196

197197
/**
@@ -237,7 +237,7 @@ public function handleUpdateAction($id, $request, array $extraParams = [])
237237

238238
DB::commit();
239239

240-
return $this->respondWithOne($item);
240+
return $this->handleUpdateResponse($item);
241241
} catch (\Exception $exception) {
242242
$message = config('app.debug') ? $exception->getMessage() : 'Failed to update Record';
243243
DB::rollback();
@@ -268,6 +268,6 @@ public function handleDestroyAction($id, $request)
268268
return $this->errorNotFound('Record not found');
269269
}
270270

271-
return $this->respondNoContent();
271+
return $this->handleDestroyResponse($id);
272272
}
273273
}

0 commit comments

Comments
 (0)