Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Revision Query builder #10437

Open
wants to merge 6 commits into
base: 5.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/stache.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@
'directory' => storage_path('forms'),
],

'revisions' => [
'class' => Stores\RevisionsStore::class,
],

],

/*
Expand Down
7 changes: 7 additions & 0 deletions src/Contracts/Revisions/RevisionQueryBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Statamic\Contracts\Revisions;

interface RevisionQueryBuilder
{
}
4 changes: 3 additions & 1 deletion src/Revisions/Revision.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use Statamic\Contracts\Auth\User;
use Statamic\Contracts\Revisions\Revision as Contract;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\RevisionDeleted;
use Statamic\Events\RevisionSaved;
use Statamic\Events\RevisionSaving;
Expand All @@ -15,7 +17,7 @@

class Revision implements Arrayable, Contract
{
use ExistsAsFile, FluentlyGetsAndSets;
use ExistsAsFile, FluentlyGetsAndSets, TracksQueriedColumns, TracksQueriedRelations;

protected $id;
protected $key;
Expand Down
67 changes: 44 additions & 23 deletions src/Revisions/RevisionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@

use Illuminate\Support\Carbon;
use Statamic\Contracts\Revisions\Revision as RevisionContract;
use Statamic\Contracts\Revisions\RevisionQueryBuilder;
use Statamic\Contracts\Revisions\RevisionRepository as Contract;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\YAML;
use Statamic\Support\FileCollection;
use Statamic\Support\Str;
use Statamic\Stache\Stache;

class RevisionRepository implements Contract
{
protected $stache;
protected $store;

public function __construct(Stache $stache)
{
$this->stache = $stache;
$this->store = $stache->store('revisions')->directory($this->directory());
}

public function directory()
{
return config('statamic.revisions.path');
Expand All @@ -25,17 +33,12 @@ public function make(): RevisionContract

public function whereKey($key)
{
$directory = $this->directory().'/'.$key;

$files = Folder::getFiles($directory);

return FileCollection::make($files)->filterByExtension('yaml')->reject(function ($path) {
return Str::endsWith($path, 'working.yaml');
})->map(function ($path) use ($key) {
return $this->makeRevisionFromFile($key, $path);
})->keyBy(function ($revision) {
return $revision->date()->timestamp;
});
return $this->query()
->where('key', $key)
->get()
->keyBy(function ($revision) {
return $revision->date()->timestamp;
});
}

public function findWorkingCopyByKey($key)
Expand All @@ -46,32 +49,50 @@ public function findWorkingCopyByKey($key)
return null;
}

return $this->makeRevisionFromFile($key, $path);
return $this->store->makeItemFromFile($path, '');
}

public function save(RevisionContract $revision)
{
File::put($revision->path(), $revision->fileContents());

$revision->id($revision->date()->timestamp);

$this->store->save($revision);
}

public function delete(RevisionContract $revision)
{
File::delete($revision->path());
$this->store->delete($revision);
}

public function query()
{
return app(RevisionQueryBuilder::class);
}

// @deprecated - use makeRevisionFromArray
protected function makeRevisionFromFile($key, $path)
{
$yaml = YAML::parse(File::get($path));

return $this->makeRevisionFromArray($key, $yaml);
}

public function makeRevisionFromArray($key, $data = [])
{
return (new Revision)
->key($key)
->action($yaml['action'] ?? false)
->id($date = $yaml['date'])
->action($data['action'] ?? false)
->id($date = $data['date'])
->date(Carbon::createFromTimestamp($date))
->user($yaml['user'] ?? false)
->message($yaml['message'] ?? false)
->attributes($yaml['attributes']);
->user($data['user'] ?? false)
->message($data['message'] ?? false)
->attributes($data['attributes']);
}

public static function bindings(): array
{
return [
RevisionQueryBuilder::class => \Statamic\Stache\Query\RevisionQueryBuilder::class,
];
}
}
9 changes: 8 additions & 1 deletion src/Revisions/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@

use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
use Statamic\Contracts\Revisions\RevisionRepository as RevisionRepositoryContract;
use Statamic\Stache\Query\RevisionQueryBuilder;
use Statamic\Stache\Stache;
use Statamic\Statamic;

class ServiceProvider extends LaravelServiceProvider
{
public function register()
{
$this->app->bind(RevisionRepositoryContract::class, RevisionRepository::class);
Statamic::repository(RevisionRepositoryContract::class, RevisionRepository::class);

$this->app->bind(RevisionQueryBuilder::class, function () {
return new RevisionQueryBuilder($this->app->make(Stache::class)->store('revisions'));
});
}
}
54 changes: 54 additions & 0 deletions src/Stache/Query/RevisionQueryBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Revisions\RevisionQueryBuilder as QueryBuilderContract;

class RevisionQueryBuilder extends Builder implements QueryBuilderContract
{
protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
if (! empty($this->wheres)) {
return $this->getKeysWithWheres($this->wheres);
}

return collect($this->store->paths()->keys());
}

protected function getKeysWithWheres($wheres)
{
return collect($wheres)->reduce(function ($ids, $where) {
$keys = $where['type'] == 'Nested'
? $this->getKeysWithWheres($where['query']->wheres)
: $this->getKeysWithWhere($where);

return $this->intersectKeysFromWhereClause($ids, $keys, $where);
});
}

protected function getKeysWithWhere($where)
{
$items = app('stache')
->store('revisions')
->index($where['column'])->items();

$method = 'filterWhere'.$where['type'];

return $this->{$method}($items, $where)->keys();
}

protected function getOrderKeyValuesByIndex()
{
return collect($this->orderBys)->mapWithKeys(function ($orderBy) {
$items = $this->store->index($orderBy->sort)->items()->all();

return [$orderBy->sort => $items];
});
}
}
60 changes: 60 additions & 0 deletions src/Stache/Stores/RevisionsStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace Statamic\Stache\Stores;

use Statamic\Facades\File;
use Statamic\Facades\Path;
use Statamic\Facades\Revision;
use Statamic\Facades\YAML;
use Statamic\Revisions\WorkingCopy;
use Statamic\Support\Str;
use Symfony\Component\Finder\SplFileInfo;

class RevisionsStore extends BasicStore
{
public function getItemFilter(SplFileInfo $file)
{
$path = Path::tidy($file->getPathname());

if (Str::endsWith($path, 'working.yaml')) {
return false;
}

return $file->getExtension() === 'yaml';
}

public function key()
{
return 'revisions';
}

public function makeItemFromFile($path, $contents)
{
$yaml = YAML::parse(File::get($path));
$key = (string) Str::of(Path::tidy($path))->beforeLast('/')->after(Path::tidy($this->directory()));

return Revision::makeRevisionFromArray($key, $yaml);
}

public function save($item)
{
if ($item instanceof WorkingCopy) {
$this->writeItemToDisk($item);

return;
}

return parent::save($item);
}

public function delete($item)
{
if ($item instanceof WorkingCopy) {
File::delete($item->path()); // windows fix - deleteItemFromDisk didnt work

return;
}

return parent::delete($item);
}
}
34 changes: 33 additions & 1 deletion tests/Revisions/RepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Support\Collection;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Contracts\Revisions\RevisionQueryBuilder;
use Statamic\Revisions\Revision;
use Statamic\Revisions\RevisionRepository;
use Tests\TestCase;
Expand All @@ -16,7 +17,7 @@ public function setUp(): void
{
parent::setUp();
config(['statamic.revisions.path' => __DIR__.'/__fixtures__']);
$this->repo = (new RevisionRepository);
$this->repo = (new RevisionRepository($this->app->make('stache')));
}

#[Test]
Expand All @@ -28,4 +29,35 @@ public function it_gets_revisions_and_excludes_working_copies()
$this->assertCount(2, $revisions);
$this->assertContainsOnlyInstancesOf(Revision::class, $revisions);
}

#[Test]
public function it_returns_a_query_builder()
{
$builder = $this->repo->query();

$this->assertInstanceOf(RevisionQueryBuilder::class, $builder);
}

#[Test]
public function it_gets_and_filters_items_using_query_builder()
{
$builder = $this->repo->query();

$revisions = $builder->get();

$this->assertInstanceOf(Collection::class, $revisions);
$this->assertCount(3, $revisions);
$this->assertContainsOnlyInstancesOf(Revision::class, $revisions);

$revisions = $builder->where('key', '123')->get();

$this->assertInstanceOf(Collection::class, $revisions);
$this->assertCount(2, $revisions);
$this->assertContainsOnlyInstancesOf(Revision::class, $revisions);

$revisions = $builder->where('key', '1234')->get();

$this->assertInstanceOf(Collection::class, $revisions);
$this->assertCount(0, $revisions);
}
}
2 changes: 2 additions & 0 deletions tests/Revisions/__fixtures__/456/1553546521.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
date: 1553546521
attributes: {}
1 change: 1 addition & 0 deletions tests/Revisions/__fixtures__/456/working.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
attributes: {}
Loading