Skip to content

Commit a27c864

Browse files
committed
Merge branch 'release/0.4.1'
Closes cloudcreativity#11
2 parents d385788 + 8a8b95f commit a27c864

File tree

4 files changed

+227
-26
lines changed

4 files changed

+227
-26
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22
All notable changes to this project will be documented in this file. This project adheres to
33
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).
44

5+
## [0.4.1] - 2016-07-27
6+
7+
### Added
8+
9+
- Support for find-many requests in the default Eloquent model search implementation.
10+
- Hooks in `EloquentController` for `creating`, `created`, `updating` and `updated` events. This makes it
11+
easier for child classes to implement customer logic, e.g. dispatching events, jobs, etc.
12+
13+
### Changed
14+
15+
- Removed dependency between model test helper and Laravel's database test helper, as there was no need for
16+
this dependency to exist.
17+
18+
### Fixed
19+
20+
- Asserting that a model has been created now correctly checks expected attributes.
21+
522
## [0.4.0] - 2016-07-20
623

724
This is a substantial refactoring, based on using this package in production environments. We also updated

src/Http/Controllers/EloquentController.php

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,47 @@ protected function hydrate(ResourceInterface $resource, Model $model)
244244
*/
245245
protected function commit(Model $model)
246246
{
247-
return $model->save();
247+
$isUpdating = $model->exists;
248+
249+
$this->beforeCommit($model, $isUpdating);
250+
251+
$result = $model->save();
252+
253+
if ($result) {
254+
$this->afterCommit($model, $isUpdating);
255+
}
256+
257+
return $result;
258+
}
259+
260+
/**
261+
* Determines which callback to use before creating or updating a model.
262+
*
263+
* @param Model $model
264+
* @param bool $isUpdating
265+
*/
266+
protected function beforeCommit(Model $model, $isUpdating)
267+
{
268+
if ($isUpdating) {
269+
$this->updating($model);
270+
} else {
271+
$this->creating($model);
272+
}
273+
}
274+
275+
/**
276+
* Determines which callback to use after a model is updated or created.
277+
*
278+
* @param Model $model
279+
* @param bool $isUpdating
280+
*/
281+
protected function afterCommit(Model $model, $isUpdating)
282+
{
283+
if ($isUpdating) {
284+
$this->updated($model);
285+
} else {
286+
$this->created($model);
287+
}
248288
}
249289

250290
/**
@@ -350,4 +390,48 @@ private function doDestroy(Model $model)
350390
return $this->destroy($model);
351391
});
352392
}
393+
394+
/**
395+
* Called before the model is created.
396+
*
397+
* Child classes can overload this method if they need to do any logic pre-creation.
398+
*
399+
* @param Model $model
400+
*/
401+
protected function creating(Model $model)
402+
{
403+
}
404+
405+
/**
406+
* Called after the model has been created.
407+
*
408+
* Child classes can overload this method if they need to do any logic post-creation.
409+
*
410+
* @param Model $model
411+
*/
412+
protected function created(Model $model)
413+
{
414+
}
415+
416+
/**
417+
* Called before the model is updated.
418+
*
419+
* Child classes can overload this method if they need to do any logic pre-updating.
420+
*
421+
* @param Model $model
422+
*/
423+
protected function updating(Model $model)
424+
{
425+
}
426+
427+
/**
428+
* Called after the model has been updated.
429+
*
430+
* Child classes can overload this method if they need to do any logic post-updating.
431+
*
432+
* @param Model $model
433+
*/
434+
protected function updated(Model $model)
435+
{
436+
}
353437
}

src/Search/AbstractSearch.php

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
2626
use Illuminate\Database\Eloquent\Model;
2727
use Illuminate\Support\Collection;
28+
use Neomerx\JsonApi\Contracts\Document\DocumentInterface;
2829
use Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface;
2930
use Neomerx\JsonApi\Contracts\Encoder\Parameters\SortParameterInterface;
3031

@@ -61,6 +62,13 @@ abstract class AbstractSearch implements SearchInterface
6162
*/
6263
protected $simplePagination = false;
6364

65+
/**
66+
* The filter param for a find-many request.
67+
*
68+
* @var string
69+
*/
70+
protected $findManyFilter = DocumentInterface::KEYWORD_ID;
71+
6472
/**
6573
* Apply the supplied filters to the builder instance.
6674
*
@@ -86,13 +94,16 @@ abstract protected function sort(Builder $builder, array $sortBy);
8694
abstract protected function isSearchOne(Collection $filters);
8795

8896
/**
89-
* @param Builder $builder
90-
* @param EncodingParametersInterface $parameters
91-
* @return Paginator|EloquentCollection
97+
* @inheritdoc
9298
*/
9399
public function search(Builder $builder, EncodingParametersInterface $parameters)
94100
{
95101
$filters = new Collection((array) $parameters->getFilteringParameters());
102+
103+
if ($this->isFindMany($filters)) {
104+
return $this->findMany($builder, $filters);
105+
}
106+
96107
$this->filter($builder, $filters);
97108
$this->sort($builder, (array) $parameters->getSortParameters());
98109

@@ -103,6 +114,23 @@ public function search(Builder $builder, EncodingParametersInterface $parameters
103114
return $this->isPaginated() ? $this->paginate($builder) : $this->all($builder);
104115
}
105116

117+
/**
118+
* @param Collection $filters
119+
* @return bool
120+
*/
121+
protected function isFindMany(Collection $filters)
122+
{
123+
return $filters->has($this->getFindManyKey());
124+
}
125+
126+
/**
127+
* @return string
128+
*/
129+
protected function getFindManyKey()
130+
{
131+
return $this->findManyFilter;
132+
}
133+
106134
/**
107135
* Should the result set be paginated?
108136
*
@@ -150,6 +178,27 @@ protected function first(Builder $builder)
150178
return $builder->first();
151179
}
152180

181+
/**
182+
* @param Builder $builder
183+
* @param Collection $filters
184+
* @return EloquentCollection
185+
*/
186+
protected function findMany(Builder $builder, Collection $filters)
187+
{
188+
$ids = $filters->get($this->getFindManyKey());
189+
190+
return $builder->findMany($this->normalizeIds($ids));
191+
}
192+
193+
/**
194+
* @param $ids
195+
* @return array
196+
*/
197+
protected function normalizeIds($ids)
198+
{
199+
return is_array($ids) ? $ids : explode(',', (string) $ids);
200+
}
201+
153202
/**
154203
* @return PageParameterHandlerInterface
155204
*/

src/Testing/InteractsWithModels.php

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424
/**
2525
* Class InteractsWithModels
2626
* @package CloudCreativity\LaravelJsonApi\Testing
27-
*
28-
* This trait MUST be used on a class that also uses this trait:
29-
* Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase
3027
*/
3128
trait InteractsWithModels
3229
{
@@ -36,35 +33,37 @@ trait InteractsWithModels
3633
*
3734
* @param Model $model
3835
* a representation of the model that should have been created.
39-
* @param $expectedId
40-
* the expected id of the model.
36+
* @param $expectedResourceId
37+
* the expected resource id of the model.
4138
* @param string|string[]|null $attributeKeys
4239
* the keys of the model attributes that should be checked, or null to check all.
4340
* @param string|null $keyName
44-
* the key name to use for the id - defaults to `Model::getKeyName()`
41+
* the key name to use for the resource id - defaults to `Model::getKeyName()`
42+
* @return $this
4543
*/
46-
public function assertModelCreated(
44+
protected function assertModelCreated(
4745
Model $model,
48-
$expectedId,
46+
$expectedResourceId,
4947
$attributeKeys = null,
5048
$keyName = null
5149
) {
52-
if (!$keyName) {
53-
$keyName = $model->getKeyName();
54-
}
55-
50+
$keyName = $keyName ?: $model->getKeyName();
5651
$attributes = $model->getAttributes();
52+
$expected = [$keyName => $expectedResourceId];
5753

5854
if (is_null($attributeKeys)) {
5955
$attributeKeys = array_keys($attributes);
6056
}
6157

6258
foreach ((array) $attributeKeys as $attr) {
59+
if ($keyName === $attr) {
60+
continue;
61+
}
62+
6363
$expected[$attr] = isset($attributes[$attr]) ? $attributes[$attr] : null;
6464
}
6565

66-
$expected = [$keyName => $expectedId];
67-
$this->seeInDatabase($model->getTable(), $expected, $model->getConnectionName());
66+
return $this->seeModelInDatabase($model, $expected);
6867
}
6968

7069
/**
@@ -76,8 +75,9 @@ public function assertModelCreated(
7675
* the expected changed attributes - key to value pairs.
7776
* @param string|string[] $unchangedKeys
7877
* the keys of the attributes that should not have changed.
78+
* @return $this
7979
*/
80-
public function assertModelPatched(Model $model, array $changedAttributes, $unchangedKeys = [])
80+
protected function assertModelPatched(Model $model, array $changedAttributes, $unchangedKeys = [])
8181
{
8282
/** We need to ensure values are cast to database values */
8383
$expected = $model->newInstance($changedAttributes)->getAttributes();
@@ -88,29 +88,80 @@ public function assertModelPatched(Model $model, array $changedAttributes, $unch
8888
}
8989

9090
$expected[$model->getKeyName()] = $model->getKey();
91-
$this->seeInDatabase($model->getTable(), $expected, $model->getConnectionName());
91+
92+
return $this->seeModelInDatabase($model, $expected);
9293
}
9394

9495
/**
9596
* Assert that a model was deleted.
9697
*
9798
* @param Model $model
99+
* @return $this
98100
*/
99-
public function assertModelDeleted(Model $model)
101+
protected function assertModelDeleted(Model $model)
100102
{
101-
$this->notSeeInDatabase($model->getTable(), [
102-
$model->getKeyName() => $model->getKey()
103-
], $model->getConnectionName());
103+
return $this->notSeeModelInDatabase($model, [$model->getKeyName() => $model->getKey()]);
104104
}
105105

106106
/**
107107
* Assert that a model was soft deleted.
108108
*
109109
* @param Model $model
110+
* @return $this
110111
*/
111-
public function assertModelTrashed(Model $model)
112+
protected function assertModelTrashed(Model $model)
112113
{
113114
PHPUnit::assertNull($model->fresh(), 'Model is not trashed.');
114-
$this->seeInDatabase($model->getTable(), [$model->getKeyName() => $model->getKey()], $model->getConnectionName());
115+
return $this->seeModelInDatabase($model, [$model->getKeyName() => $model->getKey()]);
116+
}
117+
118+
/**
119+
* @param Model $model
120+
* @param array $expected
121+
* @return $this
122+
*/
123+
protected function seeModelInDatabase(Model $model, array $expected)
124+
{
125+
$message = sprintf(
126+
'Unable to find model in database table [%s] that matched attributes [%s].',
127+
$model->getTable(),
128+
json_encode($expected)
129+
);
130+
131+
PHPUnit::assertGreaterThan(0, $this->countModels($model, $expected), $message);
132+
133+
return $this;
134+
}
135+
136+
/**
137+
* @param Model $model
138+
* @param array $expected
139+
* @return $this
140+
*/
141+
protected function notSeeModelInDatabase(Model $model, array $expected)
142+
{
143+
$message = sprintf(
144+
'Found model in database table [%s] that matched attributes [%s].',
145+
$model->getTable(),
146+
json_encode($expected)
147+
);
148+
149+
PHPUnit::assertEquals(0, $this->countModels($model, $expected), $message);
150+
151+
return $this;
152+
}
153+
154+
/**
155+
* @param Model $model
156+
* @param array $expected
157+
* @return int
158+
*/
159+
private function countModels(Model $model, array $expected)
160+
{
161+
return $model
162+
->getConnection()
163+
->table($model->getTable())
164+
->where($expected)
165+
->count();
115166
}
116167
}

0 commit comments

Comments
 (0)