Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8dd9081
Make Form submissions queryable and add facade
ryanmitchell Aug 5, 2022
6adc349
Remove extra line break
ryanmitchell Aug 5, 2022
879c9d0
Remove debug
ryanmitchell Aug 5, 2022
33f052b
Handle when $this->data() is a collection
ryanmitchell Aug 5, 2022
ce03447
Provide methods required by ExistsAsFile to Submission
ryanmitchell Aug 5, 2022
1cc4320
SubmissionStoreTest
ryanmitchell Aug 5, 2022
8bbe278
StyleCI
ryanmitchell Aug 5, 2022
dd98c50
Revert unintentional config change
ryanmitchell Aug 5, 2022
ecd91af
Update Submission class to use stache directory
ryanmitchell Aug 5, 2022
0ea42ca
SubmissionQueryBuilderTests
ryanmitchell Aug 5, 2022
653c510
Update TestCase to ensure tests run correctly
ryanmitchell Aug 5, 2022
1a9ef3f
StyleCI
ryanmitchell Aug 5, 2022
5249af0
StyleCI
ryanmitchell Aug 5, 2022
08afa94
Final tests
ryanmitchell Aug 5, 2022
cf42882
Revert test change as it was causing errors
ryanmitchell Aug 5, 2022
a5eb654
A bit more test jigging
ryanmitchell Aug 5, 2022
c4c161e
And some more
ryanmitchell Aug 5, 2022
4d224e4
Remove unnecessary orwheres
ryanmitchell Aug 6, 2022
0ea591e
Support for querying by date
ryanmitchell Aug 8, 2022
40a2b7f
StyleCI
ryanmitchell Aug 8, 2022
42acaea
Use FormSubmission facade to get and make submissions
ryanmitchell Jun 23, 2023
e98d600
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jun 23, 2023
053e9e4
Bug fixes
ryanmitchell Jun 23, 2023
716129d
Thank you Jason
ryanmitchell Jun 23, 2023
c8b7d10
Overload fileData on the Submission class rather than changing the trait
ryanmitchell Jun 23, 2023
6a1bdab
Also delete through the Facade
ryanmitchell Jun 23, 2023
b87bdfd
Defer to Submission respository for finding, allows it to be more eff…
ryanmitchell Jun 24, 2023
6fd9b83
Bind QueryBuilder to make it easier to override elsewhere
ryanmitchell Jun 24, 2023
acde848
Merge branch '4.x' into pr/6455
duncanmcclean Dec 6, 2023
7f143b6
Run Pint 🍺
duncanmcclean Dec 6, 2023
79ef397
Merge branch '4.x' into feature/submissions-query-builder
ryanmitchell Jan 11, 2024
62b81bb
fixes
ryanmitchell Jan 11, 2024
df3caec
:beer:
ryanmitchell Jan 11, 2024
5b001a9
Fix test failure
ryanmitchell Jan 11, 2024
4d8706c
Merge branch 'master' into pr/6455
duncanmcclean Apr 3, 2024
c0088ca
swap out string helpers
duncanmcclean Apr 3, 2024
d3b38ca
🍺
duncanmcclean Apr 3, 2024
190896f
plural like the rest
jasonvarga Apr 4, 2024
447c108
move assets back, one less diff
jasonvarga Apr 4, 2024
7f096a0
nitpick and update test
jasonvarga Apr 4, 2024
d6dadd0
copypasta
jasonvarga Apr 4, 2024
103620a
not needed
jasonvarga Apr 4, 2024
6589b88
fix var
jasonvarga Apr 4, 2024
6ce621c
keep date so it gets warmed
jasonvarga Apr 4, 2024
3e5c7b7
copy pasta
jasonvarga Apr 4, 2024
134442b
not needed
jasonvarga Apr 5, 2024
4b0c50a
prevent saving new files to disk
jasonvarga Apr 5, 2024
ac1ef0c
this either
jasonvarga Apr 5, 2024
7c81812
test creation
jasonvarga Apr 5, 2024
3ceadf7
not needed
jasonvarga Apr 5, 2024
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
5 changes: 5 additions & 0 deletions config/stache.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,11 @@
'directory' => base_path('users'),
],

'form-submissions' => [
'class' => Stores\SubmissionsStore::class,
'directory' => storage_path('forms'),
],

],

/*
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/Forms/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionQueryBuilder
{
//
}
22 changes: 22 additions & 0 deletions src/Contracts/Forms/SubmissionRepository.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
<?php

namespace Statamic\Contracts\Forms;

interface SubmissionRepository
{
public function all();

public function whereForm(string $handle);

public function whereInForm(array $handles);

public function find($id);

public function make();

public function query();

public function save($entry);

public function delete($entry);
}
29 changes: 29 additions & 0 deletions src/Facades/FormSubmission.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
<?php

namespace Statamic\Facades;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Contracts\Forms\SubmissionRepository;
use Statamic\Stache\Query\SubmissionQueryBuilder;

/**
* @method static Collection all()
* @method static Collection whereForm(string $handle)
* @method static Collection whereInForm(array $handles)
* @method static SubmissionContract find($id)
* @method static SubmissionContract make()
* @method static SubmissionQueryBuilder query()
* @method static save()
* @method static delete()
*
* @see \Statamic\Contracts\Forms\FormRepository
*/
class FormSubmission extends Facade
{
protected static function getFacadeAccessor()
{
return SubmissionRepository::class;
}
}
25 changes: 4 additions & 21 deletions src/Forms/Form.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Forms;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Log;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Forms\Form as FormContract;
Expand All@@ -18,15 +17,14 @@
use Statamic\Events\FormSaving;
use Statamic\Facades\Blueprint;
use Statamic\Facades\File;
use Statamic\Facades\Folder;
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
use Statamic\Yaml\ParseException;

class Form implements Arrayable, Augmentable, FormContract
{
Expand DownExpand Up@@ -295,20 +293,7 @@ public function metrics($metrics = null)
*/
public function submissions()
{
$path = config('statamic.forms.submissions').'/'.$this->handle();

return collect(Folder::getFilesByType($path, 'yaml'))->map(function ($file) {
try {
$data = YAML::parse(File::get($file));
} catch (ParseException $e) {
$data = [];
Log::warning('Could not parse form submission file: '.$file);
}

return $this->makeSubmission()
->id(pathinfo($file)['filename'])
->data($data);
});
return FormSubmission::whereForm($this->handle());
}

/**
Expand All@@ -319,9 +304,7 @@ public function submissions()
*/
public function submission($id)
{
return $this->submissions()->filter(function ($submission) use ($id) {
return $submission->id() === $id;
})->first();
return FormSubmission::find($id);
}

/**
Expand All@@ -331,7 +314,7 @@ public function submission($id)
*/
public function makeSubmission()
{
$submission = app(Submission::class);
$submission = FormSubmission::make();

$submission->form($this);

Expand Down
29 changes: 23 additions & 6 deletions src/Forms/Submission.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,23 +6,26 @@
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Forms\Submission as SubmissionContract;
use Statamic\Data\ContainsData;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedData;
use Statamic\Data\TracksQueriedColumns;
use Statamic\Data\TracksQueriedRelations;
use Statamic\Events\SubmissionCreated;
use Statamic\Events\SubmissionCreating;
use Statamic\Events\SubmissionDeleted;
use Statamic\Events\SubmissionSaved;
use Statamic\Events\SubmissionSaving;
use Statamic\Facades\Asset;
use Statamic\Facades\File;
use Statamic\Facades\YAML;
use Statamic\Facades\FormSubmission;
use Statamic\Facades\Stache;
use Statamic\Forms\Uploaders\AssetsUploader;
use Statamic\Forms\Uploaders\FilesUploader;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Submission implements Augmentable, SubmissionContract
{
use ContainsData, FluentlyGetsAndSets, HasAugmentedData;
use ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedData, TracksQueriedColumns, TracksQueriedRelations;

/**
* @var string
Expand DownExpand Up@@ -167,7 +170,7 @@ public function save()
}
}

File::put($this->getPath(), YAML::dump(Arr::removeNullValues($this->data()->all())));
FormSubmission::save($this);

foreach ($afterSaveCallbacks as $callback) {
$callback($this);
Expand All@@ -187,7 +190,7 @@ public function save()
*/
public function delete()
{
File::delete($this->getPath());
FormSubmission::delete($this);

SubmissionDeleted::dispatch($this);
}
Expand All@@ -199,7 +202,16 @@ public function delete()
*/
public function getPath()
{
return config('statamic.forms.submissions').'/'.$this->form()->handle().'/'.$this->id().'.yaml';
return $this->path();
}

public function path()
{
return vsprintf('%s/%s/%s.yaml', [
rtrim(Stache::store('form-submissions')->directory(), '/'),
$this->form()->handle(),
$this->id(),
]);
}

/**
Expand DownExpand Up@@ -237,6 +249,11 @@ public function blueprint()
return $this->form->blueprint();
}

public function fileData()
{
return $this->data()->all();
}

public function __get($key)
{
return $this->get($key);
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AppServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -121,6 +121,7 @@ public function register()
\Statamic\Contracts\Structures\NavigationRepository::class => \Statamic\Stache\Repositories\NavigationRepository::class,
\Statamic\Contracts\Assets\AssetRepository::class => \Statamic\Assets\AssetRepository::class,
\Statamic\Contracts\Forms\FormRepository::class => \Statamic\Forms\FormRepository::class,
\Statamic\Contracts\Forms\SubmissionRepository::class => \Statamic\Stache\Repositories\SubmissionRepository::class,
])->each(function ($concrete, $abstract) {
if (! $this->app->bound($abstract)) {
Statamic::repository($abstract, $concrete);
Expand DownExpand Up@@ -155,6 +156,7 @@ public function register()

collect([
'entries' => fn () => Facades\Entry::query(),
'form-submissions' => fn () => Facades\FormSubmission::query(),
'terms' => fn () => Facades\Term::query(),
'assets' => fn () => Facades\Asset::query(),
'users' => fn () => Facades\User::query(),
Expand Down
125 changes: 125 additions & 0 deletions src/Stache/Query/SubmissionQueryBuilder.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
<?php

namespace Statamic\Stache\Query;

use Illuminate\Support\Collection;
use Statamic\Contracts\Forms\SubmissionQueryBuilder as QueryBuilderContract;
use Statamic\Facades;

class SubmissionQueryBuilder extends Builder implements QueryBuilderContract
{
protected $forms;

public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column === 'form') {
$this->forms[] = $operator;

return $this;
}

return parent::where($column, $operator, $value, $boolean);
}

public function whereIn($column, $values, $boolean = 'and')
{
if (in_array($column, ['form', 'forms'])) {
$this->forms = array_merge($this->forms ?? [], $values);

return $this;
}

return parent::whereIn($column, $values, $boolean);
}

protected function collect($items = [])
{
return Collection::make($items);
}

protected function getFilteredKeys()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return empty($this->wheres)
? $this->getKeysFromForms($forms)
: $this->getKeysFromFormsWithWheres($forms, $this->wheres);
}

protected function getKeysFromForms($forms)
{
return collect($forms)->flatMap(function ($form) {
$keys = $this->store->store($form)->paths()->keys();

return collect($keys)->map(function ($key) use ($form) {
return "{$form}::{$key}";
});
});
}

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

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

protected function getKeysFromFormsWithWhere($forms, $where)
{
$items = collect($forms)->flatMap(function ($form) use ($where) {
return $this->getWhereColumnKeysFromStore($form, $where);
});

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

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

protected function getOrderKeyValuesByIndex()
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

// First, we'll get the values from each index grouped by form
$keys = collect($forms)->map(function ($form) {
$store = $this->store->store($form);

return collect($this->orderBys)->mapWithKeys(function ($orderBy) use ($form, $store) {
$items = $store->index($orderBy->sort)
->items()
->mapWithKeys(function ($item, $key) use ($form) {
return ["{$form}::{$key}" => $item];
})->all();

return [$orderBy->sort => $items];
});
});

// Then, we'll merge all the corresponding index values together from each form.
return $keys->reduce(function ($carry, $form) {
foreach ($form as $sort => $values) {
$carry[$sort] = array_merge($carry[$sort] ?? [], $values);
}

return $carry;
}, collect());
}

protected function getWhereColumnKeyValuesByIndex($column)
{
$forms = empty($this->forms)
? Facades\Form::all()->map->handle()
: $this->forms;

return collect($forms)->flatMap(function ($form) use ($column) {
return $this->getWhereColumnKeysFromStore($form, ['column' => $column]);
});
}
}
Loading