Skip to content

Commit 533443e

Browse files
authoredNov 22, 2018
πŸ”Ž Searching improvements (#84)
* Cleanup Bakery facade * Fix searching on collection queries * Apply fixes from StyleCI * Implement marking fields as searchable * Apply fixes from StyleCI
1 parent 033f106 commit 533443e

12 files changed

+134
-57
lines changed
 

β€Žsrc/Bakery.php

-20
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,4 @@ public function graphiql($route, $headers = [])
6565
['endpoint' => route($route), 'headers' => $headers]
6666
);
6767
}
68-
69-
/**
70-
* Get the type instances of Bakery.
71-
*
72-
* @return array
73-
*/
74-
public function getTypeInstances()
75-
{
76-
return $this->typeInstances;
77-
}
78-
79-
/**
80-
* Set the type instances.
81-
*
82-
* @param array $typeInstances
83-
*/
84-
public function setTypeInstances(array $typeInstances)
85-
{
86-
$this->typeInstances = $typeInstances;
87-
}
8868
}

β€Žsrc/Eloquent/ModelSchema.php

+34
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ public function isIndexable()
171171
return $this->indexable;
172172
}
173173

174+
/**
175+
* Returns if the schema is searchable.
176+
*
177+
* @return bool
178+
*/
179+
public function isSearchable()
180+
{
181+
return $this->getSearchableFields()->merge($this->getSearchableRelationFields())->isNotEmpty();
182+
}
183+
174184
/**
175185
* Return the typename of the model schema.
176186
*
@@ -231,6 +241,30 @@ public function getFillableFields(): Collection
231241
});
232242
}
233243

244+
/**
245+
* The fields that can be used in fulltext search.
246+
*
247+
* @return \Illuminate\Support\Collection
248+
*/
249+
public function getSearchableFields(): Collection
250+
{
251+
return $this->getFields()->filter(function (Field $field) {
252+
return $field->isSearchable();
253+
});
254+
}
255+
256+
/**
257+
* The relation fields that can be used in fulltext search.
258+
*
259+
* @return \Illuminate\Support\Collection
260+
*/
261+
public function getSearchableRelationFields(): Collection
262+
{
263+
return $this->getRelationFields()->filter(function (Field $field) {
264+
return $field->isSearchable();
265+
});
266+
}
267+
234268
/**
235269
* The fields that can be used to look up this model.
236270
*

β€Žsrc/Fields/Field.php

+28
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ class Field
5656
*/
5757
protected $fillable = true;
5858

59+
/**
60+
* @var bool
61+
*/
62+
protected $searchable = false;
63+
5964
/**
6065
* @var bool
6166
*/
@@ -329,6 +334,29 @@ public function isFillable(): bool
329334
return $this->fillable;
330335
}
331336

337+
/**
338+
* Set if the field is searchable.
339+
*
340+
* @param bool $searchable
341+
* @return \Bakery\Fields\Field
342+
*/
343+
public function searchable(bool $searchable = true): self
344+
{
345+
$this->searchable = $searchable;
346+
347+
return $this;
348+
}
349+
350+
/**
351+
* Return if the field is searchable.
352+
*
353+
* @return bool
354+
*/
355+
public function isSearchable(): bool
356+
{
357+
return $this->searchable;
358+
}
359+
332360
/**
333361
* Set if the field is unique.
334362
*

β€Žsrc/Queries/EloquentCollectionQuery.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,12 @@ public function args(): array
5959
'page' => $this->registry->int()->nullable(),
6060
'count' => $this->registry->int()->nullable(),
6161
'filter' => $this->registry->type($this->modelSchema->typename().'Filter')->nullable(),
62-
'search' => $this->registry->type($this->modelSchema->typename().'RootSearch')->nullable(),
6362
]);
6463

64+
if ($this->modelSchema->isSearchable()) {
65+
$args->put('search', $this->registry->type($this->modelSchema->typename().'RootSearch')->nullable());
66+
}
67+
6568
if (! empty($this->modelSchema->getFields())) {
6669
$args->put('orderBy', $this->registry->type($this->modelSchema->typename().'OrderBy')->nullable());
6770
}

β€Žsrc/Support/Facades/Bakery.php

-16
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,6 @@
44

55
use Illuminate\Support\Facades\Facade;
66

7-
/**
8-
* @method static \Bakery\Types\Definitions\ReferenceType string
9-
* @method static \Bakery\Types\Definitions\ReferenceType int
10-
* @method static \Bakery\Types\Definitions\ReferenceType boolean
11-
* @method static \Bakery\Types\Definitions\ReferenceType float
12-
* @method static \Bakery\Types\Definitions\ReferenceType ID
13-
* @method static \Bakery\Types\Definitions\EloquentType model(string $definition)
14-
* @method static \Bakery\Types\Definitions\EloquentType collection(string $definition)
15-
* @method static \Bakery\Types\Definitions\ReferenceType list(string $definition)
16-
* @method static \Bakery\Types\Definitions\ReferenceType type(string $definition)
17-
* @method static \Bakery\Types\Definitions\PolymorphicField polymorphic(array $definitions)
18-
* @method static \GraphQL\Type\Definition\NamedType resolve(string $definition)
19-
* @method static \Bakery\Eloquent\ModelSchema getModelSchema(string $class)
20-
* @method static bool hasSchemaForModel(\Illuminate\Database\Eloquent\Model|string $model)
21-
* @method static \Bakery\Eloquent\ModelSchema getSchemaForModel(\Illuminate\Database\Eloquent\Model|string $model)
22-
*/
237
class Bakery extends Facade
248
{
259
protected static function getFacadeAccessor()

β€Žsrc/Support/Schema.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,13 @@ protected function getModelTypes(): Collection
202202
->push(new Types\CollectionFilterType($this->registry, $schema))
203203
->push(new Types\CollectionOrderByType($this->registry, $schema));
204204

205+
if ($schema->isSearchable()) {
206+
$types->push(new Types\CollectionSearchType($this->registry, $schema));
207+
}
208+
205209
if ($schema->isIndexable()) {
206210
$types->push(new Types\EntityCollectionType($this->registry, $schema))
207-
->push(new Types\CollectionRootSearchType($this->registry, $schema))
208-
->push(new Types\CollectionSearchType($this->registry, $schema));
211+
->push(new Types\CollectionRootSearchType($this->registry, $schema));
209212
}
210213

211214
if ($schema->isMutable()) {

β€Žsrc/Traits/SearchesQueries.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ trait SearchesQueries
2323
*/
2424
protected function applySearch(Builder $query, array $args)
2525
{
26+
// If the query is empty, we don't need to perform any search.
27+
if (empty($args['query'])) {
28+
return $query;
29+
}
30+
2631
/** @var \Illuminate\Database\Connection $connection */
2732
$connection = DB::connection();
2833

@@ -75,7 +80,7 @@ protected function applyRelationalSearch(Builder $query, Model $model, string $r
7580
$this->joinRelation($query, $relation, 'left');
7681

7782
foreach ($fields as $key => $value) {
78-
$schema = Bakery::getSchemaForModel(get_class($related));
83+
$schema = $this->registry->getSchemaForModel($related);
7984

8085
$relations = $schema->getRelationFields();
8186
if ($relations->keys()->contains($key)) {

β€Žsrc/Types/CollectionRootSearchType.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function name(): string
2424
public function fields(): array
2525
{
2626
return [
27-
'query' => $this->registry->field($this->registry->string()),
27+
'query' => $this->registry->field($this->registry->string())->nullable(),
2828
'fields' => $this->registry->field($this->modelSchema->typename().'Search'),
2929
];
3030
}

β€Žsrc/Types/CollectionSearchType.php

+9-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace Bakery\Types;
44

55
use Bakery\Fields\Field;
6-
use Bakery\Types\Definitions\EloquentType;
6+
use Bakery\Fields\EloquentField;
77
use Bakery\Types\Definitions\EloquentInputType;
88

99
class CollectionSearchType extends EloquentInputType
@@ -25,18 +25,16 @@ public function name(): string
2525
*/
2626
public function fields(): array
2727
{
28-
$fields = collect($this->modelSchema->getFields());
29-
30-
$fields->each(function (Field $field, $name) use (&$fields) {
31-
$fields->put($name, $this->registry->field($this->registry->boolean())->nullable());
28+
$fields = $this->modelSchema->getSearchableFields()->map(function (Field $field) {
29+
return $this->registry->field($this->registry->boolean())->nullable();
3230
});
3331

34-
foreach ($this->modelSchema->getRelationFields() as $relation => $field) {
35-
if ($field instanceof EloquentType) {
36-
$fields[$relation] = $this->registry->field($field->getName().'Search')->nullable();
37-
}
38-
}
32+
$relations = $this->modelSchema->getSearchableRelationFields()->map(function (EloquentField $field) {
33+
$searchTypeName = $field->getName().'Search';
34+
35+
return $this->registry->field($searchTypeName)->nullable();
36+
});
3937

40-
return $fields->toArray();
38+
return $fields->merge($relations)->toArray();
4139
}
4240
}

β€Žtests/Stubs/Schemas/ArticleSchema.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ public function fields(): array
1414
{
1515
return [
1616
'slug' => Field::string()->unique(),
17-
'title' => Field::string(),
18-
'content' => Field::string(),
17+
'title' => Field::string()->searchable(),
18+
'content' => Field::string()->searchable(),
1919
'created_at' => Field::type('Timestamp')->fillable(false),
2020
];
2121
}
2222

2323
public function relations(): array
2424
{
2525
return [
26-
'user' => Field::model(UserSchema::class)->nullable(),
26+
'user' => Field::model(UserSchema::class)->nullable()->searchable(),
2727
'tags' => Field::collection(TagSchema::class)->nullable(),
2828
'category' => Field::model(CategorySchema::class)->nullable(),
2929
'comments' => Field::collection(CommentSchema::class),

β€Žtests/Stubs/Schemas/UserSchema.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ class UserSchema extends ModelSchema
1414
public function fields(): array
1515
{
1616
return [
17-
'name' => Field::string(),
18-
'email' => Field::string()->unique(),
17+
'name' => Field::string()->searchable(),
18+
'email' => Field::string()->unique()->searchable(),
1919
'type' => Field::string()->canStoreWhen('setType'),
2020
'password' => Field::string()->canSeeWhen('readPassword'),
2121
'secret_information' => Field::string()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Bakery\Tests\Types;
4+
5+
use Bakery\Tests\TestCase;
6+
use Bakery\Types\CollectionSearchType;
7+
use Bakery\Tests\Stubs\Schemas\ArticleSchema;
8+
use Bakery\Tests\Fixtures\IntegrationTestSchema;
9+
10+
class CollectionSearchTypeTest extends TestCase
11+
{
12+
/** @test */
13+
public function it_generates_search_fields_for_string_scalar_fields()
14+
{
15+
$schema = new IntegrationTestSchema();
16+
$schema->toGraphQLSchema();
17+
$type = new CollectionSearchType($schema->getRegistry(), new ArticleSchema($schema->getRegistry()));
18+
19+
$actual = $type->resolveFields();
20+
$this->assertArrayHasKey('title', $actual);
21+
$this->assertArrayHasKey('content', $actual);
22+
23+
$this->assertArrayNotHasKey('slug', $actual);
24+
$this->assertArrayNotHasKey('created_at', $actual);
25+
}
26+
27+
/** @test */
28+
public function it_generates_search_fields_for_relation_fields()
29+
{
30+
$schema = new IntegrationTestSchema();
31+
$schema->toGraphQLSchema();
32+
$type = new CollectionSearchType($schema->getRegistry(), new ArticleSchema($schema->getRegistry()));
33+
34+
$actual = $type->resolveFields();
35+
$this->assertArrayHasKey('user', $actual);
36+
37+
$this->assertArrayNotHasKey('tags', $actual);
38+
$this->assertArrayNotHasKey('category', $actual);
39+
$this->assertArrayNotHasKey('comments', $actual);
40+
$this->assertArrayNotHasKey('upvotes', $actual);
41+
}
42+
}

0 commit comments

Comments
 (0)
Please sign in to comment.