Skip to content

Commit 8933576

Browse files
committed
Merge branch '4.x'
2 parents 3cf7262 + 3b1ee25 commit 8933576

File tree

12 files changed

+486
-57
lines changed

12 files changed

+486
-57
lines changed

database/migrations/updates/relate_form_submissions_by_handle.php.stub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ return new class extends Migration {
1414
}
1515

1616
Schema::table($this->prefix('form_submissions'), function (Blueprint $table) {
17-
$table->string('form', 30)->nullable()->index()->after('id');
17+
$table->string('form')->nullable()->index()->after('id');
1818
});
1919

2020
$forms = FormModel::all()->pluck('handle', 'id');

src/Commands/ExportForms.php

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
use Statamic\Contracts\Forms\FormRepository as FormRepositoryContract;
1111
use Statamic\Contracts\Forms\Submission as SubmissionContract;
1212
use Statamic\Contracts\Forms\SubmissionRepository as SubmissionRepositoryContract;
13-
use Statamic\Eloquent\Forms\Form;
14-
use Statamic\Eloquent\Forms\FormRepository;
15-
use Statamic\Eloquent\Forms\Submission;
16-
use Statamic\Eloquent\Forms\SubmissionRepository;
13+
use Statamic\Eloquent\Forms\FormModel;
14+
use Statamic\Eloquent\Forms\SubmissionModel;
15+
use Statamic\Facades\Form;
16+
use Statamic\Facades\FormSubmission;
1717
use Statamic\Forms\Form as StacheForm;
1818
use Statamic\Forms\FormRepository as StacheFormRepository;
1919
use Statamic\Forms\Submission as StacheSubmission;
2020
use Statamic\Stache\Repositories\SubmissionRepository as StacheSubmissionRepository;
21-
use Statamic\Statamic;
2221

2322
class ExportForms extends Command
2423
{
@@ -29,14 +28,17 @@ class ExportForms extends Command
2928
*
3029
* @var string
3130
*/
32-
protected $signature = 'statamic:eloquent:export-forms';
31+
protected $signature = 'statamic:eloquent:export-forms
32+
{--force : Force the export to run, with all prompts answered "yes"}
33+
{--only-forms : Only export forms}
34+
{--only-submissions : Only export submissions}';
3335

3436
/**
3537
* The console command description.
3638
*
3739
* @var string
3840
*/
39-
protected $description = 'Export eloquent based forms to flat files.';
41+
protected $description = 'Export eloquent based forms and submissions to flat files.';
4042

4143
/**
4244
* Execute the console command.
@@ -47,11 +49,9 @@ public function handle()
4749
{
4850
$this->usingDefaultRepositories(function () {
4951
$this->exportForms();
52+
$this->exportSubmissions();
5053
});
5154

52-
$this->newLine();
53-
$this->info('Forms exported');
54-
5555
return 0;
5656
}
5757

@@ -62,57 +62,75 @@ private function usingDefaultRepositories(Closure $callback)
6262
Facade::clearResolvedInstance(SubmissionContract::class);
6363
Facade::clearResolvedInstance(SubmissionRepositoryContract::class);
6464

65-
app()->bind(FormContract::class, Form::class);
66-
app()->bind(FormRepositoryContract::class, FormRepository::class);
67-
app()->bind(SubmissionContract::class, Submission::class);
68-
app()->bind(SubmissionRepositoryContract::class, SubmissionRepository::class);
69-
app()->bind(\Statamic\Contracts\Forms\SubmissionQueryBuilder::class, \Statamic\Eloquent\Forms\SubmissionQueryBuilder::class);
65+
app()->bind(FormContract::class, StacheForm::class);
66+
app()->bind(FormRepositoryContract::class, StacheFormRepository::class);
67+
app()->bind(SubmissionContract::class, StacheSubmission::class);
68+
app()->bind(SubmissionRepositoryContract::class, StacheSubmissionRepository::class);
7069

7170
$callback();
7271
}
7372

7473
private function exportForms()
7574
{
76-
$forms = (new FormRepository)->all();
75+
if (! $this->shouldExportForms()) {
76+
return;
77+
}
7778

78-
app()->bind(FormContract::class, StacheForm::class);
79+
$forms = FormModel::all();
7980

8081
$this->withProgressBar($forms, function ($form) {
81-
$newForm = (new StacheForm)
82-
->handle($form->handle())
83-
->title($form->title())
84-
->store($form->store())
85-
->email($form->email())
86-
->honeypot($form->honeypot());
87-
88-
Statamic::repository(FormRepositoryContract::class, StacheFormRepository::class);
89-
Facade::clearResolvedInstance(SubmissionRepositoryContract::class);
90-
91-
$newForm->save();
82+
Form::make()
83+
->handle($form->handle)
84+
->title($form->title)
85+
->store($form->settings['store'] ?? null)
86+
->email($form->settings['email'] ?? null)
87+
->honeypot($form->settings['honeypot'] ?? null)
88+
->data($form->settings['data'] ?? [])
89+
->save();
90+
});
9291

93-
Statamic::repository(FormRepositoryContract::class, FormRepository::class);
94-
Facade::clearResolvedInstance(SubmissionRepositoryContract::class);
92+
$this->newLine();
93+
$this->info('Forms exported');
94+
}
9595

96-
$form->submissions()->each(function ($submission) use ($newForm) {
97-
$id = $submission->date()->getPreciseTimestamp(4);
98-
$id = substr($id, 0, -4).'.'.substr($id, -4);
96+
private function exportSubmissions()
97+
{
98+
if (! $this->shouldExportSubmissions()) {
99+
return;
100+
}
99101

100-
$newSubmission = (new StacheSubmission)
101-
->id($id)
102-
->form($newForm)
103-
->data($submission->data());
102+
$submissions = SubmissionModel::all();
104103

105-
Statamic::repository(SubmissionRepositoryContract::class, StacheSubmissionRepository::class);
106-
Facade::clearResolvedInstance(SubmissionRepositoryContract::class);
104+
$this->withProgressBar($submissions, function ($submission) {
105+
if (! $form = Form::find($submission->form)) {
106+
return;
107+
}
107108

108-
$newSubmission->save();
109+
$id = $submission->created_at->getPreciseTimestamp(4);
110+
$id = substr($id, 0, -4).'.'.substr($id, -4);
109111

110-
Statamic::repository(SubmissionRepositoryContract::class, SubmissionRepository::class);
111-
Facade::clearResolvedInstance(SubmissionRepositoryContract::class);
112-
});
112+
FormSubmission::make()
113+
->id($id)
114+
->form($form)
115+
->data($submission->data)
116+
->save();
113117
});
114118

115119
$this->newLine();
116-
$this->info('Forms exported');
120+
$this->info('Submissions exported');
121+
}
122+
123+
private function shouldExportForms(): bool
124+
{
125+
return $this->option('only-forms')
126+
|| ! $this->option('only-submissions')
127+
&& ($this->option('force') || $this->confirm('Do you want to export forms?'));
128+
}
129+
130+
private function shouldExportSubmissions(): bool
131+
{
132+
return $this->option('only-submissions')
133+
|| ! $this->option('only-forms')
134+
&& ($this->option('force') || $this->confirm('Do you want to export submissions?'));
117135
}
118136
}

src/Commands/SyncAssets.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Statamic\Eloquent\Commands;
44

55
use Illuminate\Console\Command;
6+
use Statamic\Assets\AssetContainerContents;
67
use Statamic\Console\RunsInPlease;
78
use Statamic\Contracts\Assets\AssetContainer;
89
use Statamic\Eloquent\Assets\AssetModel;
@@ -44,6 +45,12 @@ private function processContainer(AssetContainer $container)
4445
$this->info("Container: {$container->handle()}");
4546

4647
$this->processFolder($container);
48+
49+
$contents = app(AssetContainerContents::class);
50+
51+
$contents->cacheStore()->forget('asset-folder-contents-'.$container->handle());
52+
53+
$contents->container($container)->directories();
4754
}
4855

4956
private function processFolder(AssetContainer $container, $folder = '/')
@@ -87,6 +94,45 @@ private function processFolder(AssetContainer $container, $folder = '/')
8794
});
8895
});
8996

97+
// delete any sub-folders we have a db entry for that no longer exist
98+
$filesystemFolders = $contents
99+
->reject(fn ($item) => $item['type'] != 'dir')
100+
->pluck('path');
101+
102+
// The folder variable is passed with a leading slash. This must be removed
103+
// in order to match against the folder column in the database.
104+
$folderNoLeadingSlash = Str::chopStart($folder, '/');
105+
106+
AssetModel::query()
107+
->where('container', $container->handle())
108+
->when(
109+
$folder == '/',
110+
fn ($query) => $query->where('folder', 'not like', '%/'),
111+
fn ($query) => $query->where('folder', 'like', $folderNoLeadingSlash.'/%')
112+
)
113+
->select('folder')
114+
->distinct()
115+
->pluck('folder')
116+
->unique()
117+
->each(function ($folder) use ($filesystemFolders, $container) {
118+
if ($filesystemFolders->contains(fn ($fsFolder) => Str::startsWith($folder, $fsFolder.'/'))) {
119+
return;
120+
}
121+
122+
$this->error("Deleting assets in {$folder}");
123+
AssetModel::query()
124+
->where('container', $container->handle())
125+
->where('folder', 'like', $folder)
126+
->orWhere('folder', 'like', $folder.'/%')
127+
->chunk(100, function ($assets) {
128+
$assets->each(function ($asset) {
129+
$this->error("Deleting {$asset->path}");
130+
131+
$asset->delete();
132+
});
133+
});
134+
});
135+
90136
// process any sub-folders of this folder
91137
$contents
92138
->reject(fn ($item) => $item['type'] != 'dir')

src/Entries/EntryQueryBuilder.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,15 @@ protected function getJsonCasts(): IlluminateCollection
214214
$wheres = collect($this->builder->getQuery()->wheres);
215215
$collectionWhere = $wheres->firstWhere('column', 'collection');
216216

217-
if (
218-
! $collectionWhere
219-
|| ! isset($collectionWhere['value'])
220-
|| ! ($collection = Collection::find($collectionWhere['value']))
221-
) {
217+
if (! $collectionWhere) {
218+
return collect([]);
219+
}
220+
221+
if (isset($collectionWhere['values']) && count($collectionWhere['values']) == 1) {
222+
$collectionWhere['value'] = $collectionWhere['values'][0];
223+
}
224+
225+
if (! isset($collectionWhere['value']) || ! $collection = Collection::find($collectionWhere['value'])) {
222226
return collect([]);
223227
}
224228

@@ -245,4 +249,21 @@ private function entryColumnsAndMappings()
245249
{
246250
return Blink::once('eloquent-entry-data-column-mappings', fn () => array_merge(self::COLUMNS, (new EloquentEntry)->getDataColumnMappings($this->builder->getModel())));
247251
}
252+
253+
protected function getBlueprintsForRelations()
254+
{
255+
$collections = empty($this->collections)
256+
? Collection::all()
257+
: $this->collections;
258+
259+
return collect($collections)->flatMap(function ($collection) {
260+
if (is_string($collection)) {
261+
$collection = Collection::find($collection);
262+
}
263+
264+
return $collection ? $collection->entryBlueprints() : false;
265+
})
266+
->filter()
267+
->unique();
268+
}
248269
}

src/Entries/EntryRepository.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Statamic\Contracts\Entries\QueryBuilder;
77
use Statamic\Eloquent\Jobs\UpdateCollectionEntryOrder;
88
use Statamic\Eloquent\Jobs\UpdateCollectionEntryParent;
9+
use Statamic\Entries\EntryCollection;
910
use Statamic\Facades\Blink;
1011
use Statamic\Stache\Repositories\EntryRepository as StacheRepository;
1112

@@ -51,6 +52,30 @@ public function findByUri(string $uri, ?string $site = null): ?EntryContract
5152
return $this->substitutionsById[$item->id()] ?? $item;
5253
}
5354

55+
public function whereInId($ids): EntryCollection
56+
{
57+
$cached = collect($ids)->flip()->map(fn ($_, $id) => Blink::get("eloquent-entry-{$id}"));
58+
$missingIds = $cached->reject()->keys();
59+
60+
$missingById = $this->query()
61+
->whereIn('id', $missingIds)
62+
->get()
63+
->keyBy->id();
64+
65+
$missingById->each(function ($entry, $id) {
66+
Blink::put("eloquent-entry-{$id}", $entry);
67+
});
68+
69+
$items = $cached
70+
->map(fn ($entry, $id) => $entry ?? $missingById->get($id))
71+
->filter()
72+
->values();
73+
74+
$this->applySubstitutions($items);
75+
76+
return EntryCollection::make($items);
77+
}
78+
5479
public function save($entry)
5580
{
5681
$model = $entry->toModel();

0 commit comments

Comments
 (0)