Skip to content

Commit

Permalink
[5.x] Extract whereSite query method to trait (#9991)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonvarga authored May 3, 2024
1 parent fce280c commit 341f506
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 87 deletions.
2 changes: 1 addition & 1 deletion src/Entries/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,6 @@ public function __sleep()
$this->slug = $slug($this);
}

return array_keys(Arr::except(get_object_vars($this), ['cachedKeys', 'computedCallbackCache', 'siteCache']));
return array_keys(Arr::except(get_object_vars($this), ['cachedKeys', 'computedCallbackCache', 'siteCache', 'augmentationReferenceKey']));
}
}
114 changes: 28 additions & 86 deletions src/Stache/Query/EntryQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

class EntryQueryBuilder extends Builder implements QueryBuilder
{
use QueriesTaxonomizedEntries;

private const STATUSES = ['published', 'draft', 'scheduled', 'expired'];
use QueriesEntryStatus, QueriesTaxonomizedEntries;

protected $collections = [];

Expand Down Expand Up @@ -70,39 +68,6 @@ protected function getFilteredKeys()
: $this->getKeysFromCollectionsWithWheres($collections, $this->wheres);
}

private function addCollectionWheres(): void
{
$this->collections = $this->getCollectionWheres();
}

private function getCollectionWheres(): array
{
// If the collections property isn't empty, it means the user has explicitly
// queried for them. In that case, we'll use them and skip the auto-detection.
if (! empty($this->collections)) {
return $this->collections;
}

// Otherwise, we'll detect them by looking at where clauses targeting the "id" column.
$ids = collect($this->wheres)->where('column', 'id')->flatMap(fn ($where) => $where['values'] ?? [$where['value']]);

// If no IDs were queried, fall back to all collections.
if ($ids->isEmpty()) {
return Collection::handles()->all();
}

return Blink::once('entry-to-collection-map', function () {
return Collection::handles()
->flatMap(fn ($collection) => $this->getWhereColumnKeysFromStore($collection, ['column' => 'collectionHandle']))
->keys()
->mapWithKeys(function ($value) {
[$collection, $id] = explode('::', $value);

return [$id => $collection];
});
})->only($ids->all())->unique()->values()->all();
}

protected function getKeysFromCollections($collections)
{
return collect($collections)->flatMap(function ($collection) {
Expand Down Expand Up @@ -178,69 +143,46 @@ protected function getWhereColumnKeyValuesByIndex($column)
});
}

public function whereStatus(string $status)
private function ensureCollectionsAreQueriedForStatusQuery(): void
{
$this->addCollectionWheres();

if (! in_array($status, self::STATUSES)) {
throw new \Exception("Invalid status [$status]");
}

if ($status === 'draft') {
return $this->where('published', false);
// If the collections property isn't empty, it means the user has explicitly
// queried for them. In that case, we'll use them and skip the auto-detection.
if (! empty($this->collections)) {
return;
}

$this->where('published', true);

return $this->where(fn ($query) => $this
->getCollectionsForStatus()
->each(fn ($collection) => $query->orWhere(fn ($q) => $this->addCollectionStatusLogicToQuery($q, $status, $collection))));
}

private function getCollectionsForStatus()
{
// Since we have to add nested queries for each collection, if collections have been provided,
// we'll use those to avoid the need for adding unnecessary query clauses.
// Otherwise, we'll detect them by looking at where clauses targeting the "id" column.
$ids = collect($this->wheres)->where('column', 'id')->flatMap(fn ($where) => $where['values'] ?? [$where['value']]);

if (empty($this->collections)) {
return Collection::all();
}
// If no IDs were queried, fall back to all collections.
$this->collections = $ids->isEmpty()
? Collection::handles()->all()
: Blink::once('entry-to-collection-map', function () {
return Collection::handles()
->flatMap(fn ($collection) => $this->getWhereColumnKeysFromStore($collection, ['column' => 'collectionHandle']))
->keys()
->mapWithKeys(function ($value) {
[$collection, $id] = explode('::', $value);

return collect($this->collections)->map(fn ($handle) => Collection::find($handle));
return [$id => $collection];
});
})->only($ids->all())->unique()->values()->all();
}

private function addCollectionStatusLogicToQuery($query, $status, $collection)
protected function addCollectionWhereToStatusQuery($query, $collection): void
{
// Using collectionHandle instead of collection because we intercept collection
// and put it on a property. In this case we actually want the indexed value.
// We can probably refactor this elsewhere later.
$query->where('collectionHandle', $collection->handle());

if ($collection->futureDateBehavior() === 'public' && $collection->pastDateBehavior() === 'public') {
if ($status === 'scheduled' || $status === 'expired') {
$query->where('date', 'invalid'); // intentionally trigger no results.
}
}

if ($collection->futureDateBehavior() === 'private') {
$status === 'scheduled'
? $query->where('date', '>', now())
: $query->where('date', '<', now());

if ($status === 'expired') {
$query->where('date', 'invalid'); // intentionally trigger no results.
}
}
$query->where('collectionHandle', $collection);
}

if ($collection->pastDateBehavior() === 'private') {
$status === 'expired'
? $query->where('date', '<', now())
: $query->where('date', '>', now());
protected function getCollectionsForStatusQuery(): \Illuminate\Support\Collection
{
// Since we have to add nested queries for each collection, we only want to add clauses for the
// applicable collections. By this point, there should be where clauses on the collection column.

if ($status === 'scheduled') {
$query->where('date', 'invalid'); // intentionally trigger no results.
}
}
return collect($this->collections)->map(fn ($handle) => Collection::find($handle));
}

public function prepareForFakeQuery(): array
Expand Down
67 changes: 67 additions & 0 deletions src/Stache/Query/QueriesEntryStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;

trait QueriesEntryStatus
{
public function whereStatus(string $status)
{
if (! in_array($status, ['published', 'draft', 'scheduled', 'expired'])) {
throw new \Exception("Invalid status [$status]");
}

$this->ensureCollectionsAreQueriedForStatusQuery();

if ($status === 'draft') {
return $this->where('published', false);
}

$this->where('published', true);

return $this->where(fn ($query) => $this
->getCollectionsForStatusQuery()
->each(fn ($collection) => $query->orWhere(fn ($q) => $this->addCollectionStatusLogicToQuery($q, $status, $collection))));
}

private function addCollectionStatusLogicToQuery($query, $status, $collection): void
{
$this->addCollectionWhereToStatusQuery($query, $collection->handle());

if ($collection->futureDateBehavior() === 'public' && $collection->pastDateBehavior() === 'public') {
if ($status === 'scheduled' || $status === 'expired') {
$query->where('date', 'invalid'); // intentionally trigger no results.
}
}

if ($collection->futureDateBehavior() === 'private') {
$status === 'scheduled'
? $query->where('date', '>', now())
: $query->where('date', '<', now());

if ($status === 'expired') {
$query->where('date', 'invalid'); // intentionally trigger no results.
}
}

if ($collection->pastDateBehavior() === 'private') {
$status === 'expired'
? $query->where('date', '<', now())
: $query->where('date', '>', now());

if ($status === 'scheduled') {
$query->where('date', 'invalid'); // intentionally trigger no results.
}
}
}

protected function addCollectionWhereToStatusQuery($query, $collection): void
{
$query->where('collection', $collection);
}

abstract protected function ensureCollectionsAreQueriedForStatusQuery(): void;

abstract protected function getCollectionsForStatusQuery(): Collection;
}

0 comments on commit 341f506

Please sign in to comment.