Skip to content

Commit 3b1ee25

Browse files
authored
Support whereHas etc on Entries and Terms (#512)
* Support on EntryQueryBuilder * Support terms too * Remove concern as its provided by base builder * version bump * 🍺 * Fix terms test * Fix failing tests from other 5.68 changes * and the last
1 parent c750c21 commit 3b1ee25

File tree

7 files changed

+235
-5
lines changed

7 files changed

+235
-5
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
},
2626
"require": {
2727
"php": "^8.1",
28-
"statamic/cms": "^5.67"
28+
"statamic/cms": "^5.68"
2929
},
3030
"require-dev": {
3131
"doctrine/dbal": "^3.8",

src/Entries/EntryQueryBuilder.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,21 @@ private function entryColumnsAndMappings()
249249
{
250250
return Blink::once('eloquent-entry-data-column-mappings', fn () => array_merge(self::COLUMNS, (new EloquentEntry)->getDataColumnMappings($this->builder->getModel())));
251251
}
252+
253+
protected function getBlueprintsForRelations()
254+
{
255+
$collections = empty($this->collections)
256+
? Collection::all()
257+
: $this->collections;
258+
259+
return collect($collections)->flatMap(function ($collection) {
260+
if (is_string($collection)) {
261+
$collection = Collection::find($collection);
262+
}
263+
264+
return $collection ? $collection->entryBlueprints() : false;
265+
})
266+
->filter()
267+
->unique();
268+
}
252269
}

src/Taxonomies/TermQueryBuilder.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,21 @@ public function with($relations, $callback = null)
295295
{
296296
return $this;
297297
}
298+
299+
protected function getBlueprintsForRelations()
300+
{
301+
$taxonomies = empty($this->taxonomies)
302+
? Taxonomy::handles()
303+
: $this->taxonomies;
304+
305+
return collect($taxonomies)->flatMap(function ($taxonomy) {
306+
if (is_string($taxonomy)) {
307+
$taxonomy = Taxonomy::find($taxonomy);
308+
}
309+
310+
return $taxonomy ? $taxonomy->termBlueprints() : false;
311+
})
312+
->filter()
313+
->unique();
314+
}
298315
}

tests/Data/Entries/EntryQueryBuilderTest.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,4 +976,114 @@ public function entries_are_found_using_where_data()
976976
$this->assertCount(2, $entries);
977977
$this->assertEquals(['Post 1', 'Post 3'], $entries->map->title->all());
978978
}
979+
980+
#[Test]
981+
public function entries_are_found_using_where_has_when_max_items_1()
982+
{
983+
$blueprint = Blueprint::makeFromFields(['entries_field' => ['type' => 'entries', 'max_items' => 1]]);
984+
Blueprint::shouldReceive('in')->with('collections/posts')->andReturn(collect(['posts' => $blueprint]));
985+
986+
$this->createDummyCollectionAndEntries();
987+
988+
Entry::find('1')
989+
->merge([
990+
'entries_field' => 2,
991+
])
992+
->save();
993+
994+
Entry::find('3')
995+
->merge([
996+
'entries_field' => 1,
997+
])
998+
->save();
999+
1000+
$entries = Entry::query()->whereHas('entries_field')->get();
1001+
1002+
$this->assertCount(2, $entries);
1003+
$this->assertEquals(['Post 1', 'Post 3'], $entries->map->title->all());
1004+
1005+
$entries = Entry::query()->whereHas('entries_field', function ($subquery) {
1006+
$subquery->where('title', 'Post 2');
1007+
})
1008+
->get();
1009+
1010+
$this->assertCount(1, $entries);
1011+
$this->assertEquals(['Post 1'], $entries->map->title->all());
1012+
1013+
$entries = Entry::query()->whereNull('entries_field')->orWhereDoesntHave('entries_field', function ($subquery) {
1014+
$subquery->where('title', 'Post 2');
1015+
})
1016+
->get();
1017+
1018+
$this->assertCount(2, $entries);
1019+
$this->assertEquals(['Post 2', 'Post 3'], $entries->map->title->all());
1020+
}
1021+
1022+
#[Test]
1023+
public function entries_are_found_using_where_has_when_max_items_not_1()
1024+
{
1025+
$blueprint = Blueprint::makeFromFields(['entries_field' => ['type' => 'entries']]);
1026+
Blueprint::shouldReceive('in')->with('collections/posts')->andReturn(collect(['posts' => $blueprint]));
1027+
1028+
$this->createDummyCollectionAndEntries();
1029+
1030+
Entry::find('1')
1031+
->merge([
1032+
'entries_field' => [2, 1],
1033+
])
1034+
->save();
1035+
1036+
Entry::find('3')
1037+
->merge([
1038+
'entries_field' => [1, 2],
1039+
])
1040+
->save();
1041+
1042+
$entries = Entry::query()->whereHas('entries_field')->get();
1043+
1044+
$this->assertCount(2, $entries);
1045+
$this->assertEquals(['Post 1', 'Post 3'], $entries->map->title->all());
1046+
1047+
$entries = Entry::query()->whereHas('entries_field', function ($subquery) {
1048+
$subquery->where('title', 'Post 2');
1049+
})
1050+
->get();
1051+
1052+
$this->assertCount(2, $entries);
1053+
$this->assertEquals(['Post 1', 'Post 3'], $entries->map->title->all());
1054+
1055+
$entries = Entry::query()->whereDoesntHave('entries_field', function ($subquery) {
1056+
$subquery->where('title', 'Post 2');
1057+
})
1058+
->get();
1059+
1060+
$this->assertCount(1, $entries);
1061+
$this->assertEquals(['Post 2'], $entries->map->title->all());
1062+
}
1063+
1064+
#[Test]
1065+
public function entries_are_found_using_where_relation()
1066+
{
1067+
$blueprint = Blueprint::makeFromFields(['entries_field' => ['type' => 'entries']]);
1068+
Blueprint::shouldReceive('in')->with('collections/posts')->andReturn(collect(['posts' => $blueprint]));
1069+
1070+
$this->createDummyCollectionAndEntries();
1071+
1072+
Entry::find('1')
1073+
->merge([
1074+
'entries_field' => [2, 1],
1075+
])
1076+
->save();
1077+
1078+
Entry::find('3')
1079+
->merge([
1080+
'entries_field' => [1, 2],
1081+
])
1082+
->save();
1083+
1084+
$entries = Entry::query()->whereRelation('entries_field', 'title', 'Post 2')->get();
1085+
1086+
$this->assertCount(2, $entries);
1087+
$this->assertEquals(['Post 1', 'Post 3'], $entries->map->title->all());
1088+
}
9791089
}

tests/Data/Taxonomies/TermQueryBuilderTest.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,4 +593,86 @@ public function terms_are_found_using_where_json_length()
593593
$this->assertCount(2, $entries);
594594
$this->assertEquals(['2', '5'], $entries->map->slug()->all());
595595
}
596+
597+
public function terms_are_found_using_where_has_when_max_items_1()
598+
{
599+
$blueprint = Blueprint::makeFromFields(['terms_field' => ['type' => 'terms', 'max_items' => 1, 'taxonomies' => ['tags']]]);
600+
Blueprint::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint]));
601+
602+
Taxonomy::make('tags')->save();
603+
Term::make('a')->taxonomy('tags')->data([])->save();
604+
Term::make('b')->taxonomy('tags')->data(['terms_field' => 'a'])->save();
605+
Term::make('c')->taxonomy('tags')->data(['terms_field' => 'b'])->save();
606+
607+
$terms = Term::query()->whereHas('terms_field')->get();
608+
609+
$this->assertCount(2, $terms);
610+
$this->assertEquals(['b', 'c'], $terms->map->slug->all());
611+
612+
$terms = Term::query()->whereHas('terms_field', function ($subquery) {
613+
$subquery->where('title', 'a');
614+
})
615+
->get();
616+
617+
$this->assertCount(1, $terms);
618+
$this->assertEquals(['b'], $terms->map->slug->all());
619+
620+
$terms = Term::query()->whereDoesntHave('terms_field', function ($subquery) {
621+
$subquery->where('title', 'a');
622+
})
623+
->get();
624+
625+
$this->assertCount(2, $terms);
626+
$this->assertEquals(['a', 'c'], $terms->map->slug->all());
627+
}
628+
629+
#[Test]
630+
public function terms_are_found_using_where_has_when_max_items_not_1()
631+
{
632+
$blueprint = Blueprint::makeFromFields(['terms_field' => ['type' => 'terms', 'taxonomies' => ['tags']]]);
633+
Blueprint::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint]));
634+
635+
Taxonomy::make('tags')->save();
636+
Term::make('a')->taxonomy('tags')->data(['title' => 'a'])->save();
637+
Term::make('b')->taxonomy('tags')->data(['title' => 'b', 'terms_field' => ['a', 'c']])->save();
638+
Term::make('c')->taxonomy('tags')->data(['title' => 'c', 'terms_field' => ['b', 'a']])->save();
639+
640+
$terms = Term::query()->whereHas('terms_field')->get();
641+
642+
$this->assertCount(2, $terms);
643+
$this->assertEquals(['b', 'c'], $terms->map->slug->all());
644+
645+
$terms = Term::query()->whereHas('terms_field', function ($subquery) {
646+
$subquery->where('slug', 'b');
647+
})
648+
->get();
649+
650+
$this->assertCount(1, $terms);
651+
$this->assertEquals(['c'], $terms->map->slug->all());
652+
653+
$terms = Term::query()->whereDoesntHave('terms_field', function ($subquery) {
654+
$subquery->where('title', 'b');
655+
})
656+
->get();
657+
658+
$this->assertCount(2, $terms);
659+
$this->assertEquals(['a', 'b'], $terms->map->slug->all());
660+
}
661+
662+
#[Test]
663+
public function terms_are_found_using_where_relation()
664+
{
665+
$blueprint = Blueprint::makeFromFields(['terms_field' => ['type' => 'terms', 'taxonomies' => ['tags']]]);
666+
Blueprint::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint]));
667+
668+
Taxonomy::make('tags')->save();
669+
Term::make('a')->taxonomy('tags')->data([])->save();
670+
Term::make('b')->taxonomy('tags')->data(['terms_field' => ['a', 'c']])->save();
671+
Term::make('c')->taxonomy('tags')->data(['terms_field' => ['b', 'a']])->save();
672+
673+
$terms = Term::query()->whereRelation('terms_field', 'slug', 'b')->get();
674+
675+
$this->assertCount(1, $terms);
676+
$this->assertEquals(['c'], $terms->map->slug->all());
677+
}
596678
}

tests/Entries/EntryRepositoryTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public function it_updates_the_uris_of_all_entries_in_a_collection()
3131

3232
$collection->routes('posts/{slug}')->save();
3333

34+
Blink::store('entry-uris')->flush();
35+
3436
// Assert that the URIs are unchanged, to make sure that saving
3537
// the collection isn't what caused the URIs to be updated.
3638
$this->assertEquals([
@@ -59,6 +61,8 @@ public function it_updates_the_uris_of_specific_entries_in_a_collection()
5961

6062
$collection->routes('posts/{slug}')->save();
6163

64+
Blink::store('entry-uris')->flush();
65+
6266
// Assert that the URIs are unchanged, to make sure that saving
6367
// the collection isn't what caused the URIs to be updated.
6468
$this->assertEquals([

tests/Entries/EntryTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,14 @@ public function it_stores_and_retrieves_mapped_data_values()
292292
{
293293
config()->set('statamic.eloquent-driver.entries.map_data_to_columns', true);
294294

295+
\Illuminate\Support\Facades\Schema::table('entries', function ($table) {
296+
$table->string('foo', 30)->nullable();
297+
});
298+
295299
$collection = Collection::make('blog')->title('blog')->routes([
296300
'en' => '/blog/{slug}',
297301
])->save();
298302

299-
\Illuminate\Support\Facades\Schema::table('entries', function ($table) {
300-
$table->string('foo', 30);
301-
});
302-
303303
$entry = (new Entry)
304304
->collection('blog')
305305
->slug('the-slug')

0 commit comments

Comments
 (0)