|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace Aerni\Factory\Factories\Concerns; |
| 4 | + |
| 5 | +use Statamic\Fields\Field; |
| 6 | +use Statamic\Fields\Value; |
| 7 | +use Statamic\Fields\Fields; |
| 8 | +use Statamic\Fields\Values; |
| 9 | +use Statamic\Fieldtypes\Bard; |
| 10 | +use Statamic\Fieldtypes\Grid; |
| 11 | +use Illuminate\Http\UploadedFile; |
| 12 | +use Illuminate\Support\Collection; |
| 13 | +use Statamic\Fieldtypes\Replicator; |
| 14 | +use Illuminate\Filesystem\Filesystem; |
| 15 | +use Statamic\Contracts\Entries\Entry; |
| 16 | +use Statamic\Fieldtypes\Assets\Assets; |
| 17 | +use Illuminate\Support\Facades\Storage; |
| 18 | +use Statamic\Forms\Uploaders\AssetsUploader; |
| 19 | + |
| 20 | +trait WithAssets |
| 21 | +{ |
| 22 | + // TODO: This can be a lot of work since we are augmenting the full entry upfront. |
| 23 | + // Can we do it differently by only augmenting the fields we need? |
| 24 | + public function moveAssets(Entry $entry): void |
| 25 | + { |
| 26 | + $field = $entry->toAugmentedCollection() |
| 27 | + ->map(fn ($field) => match (true) { |
| 28 | + $field->fieldtype() instanceof Replicator => $field->value(), |
| 29 | + $field->fieldtype() instanceof Bard => $field->value(), |
| 30 | + $field->fieldtype() instanceof Grid => $field->value(), |
| 31 | + default => $field, |
| 32 | + }) |
| 33 | + ->dot() |
| 34 | + ->map(fn ($value) => $value instanceof Values ? $value->all() : $value) |
| 35 | + ->flatten() |
| 36 | + ->filter(fn ($value) => $value instanceof Value && $value->fieldtype() instanceof Assets) |
| 37 | + ->each(fn ($value) => $value->value()?->move($this->assetsFolder($value->fieldtype()))); |
| 38 | + } |
| 39 | + |
| 40 | + protected function assetsFolder(Assets $fieldtype): ?string |
| 41 | + { |
| 42 | + if (! in_array($field = $fieldtype->config('dynamic'), ['id', 'slug', 'author'])) { |
| 43 | + return null; |
| 44 | + } |
| 45 | + |
| 46 | + if (! ($parent = $fieldtype->field()->parent()) instanceof Entry) { |
| 47 | + return null; |
| 48 | + } |
| 49 | + |
| 50 | + $value = $parent->$field; |
| 51 | + |
| 52 | + return match (true) { |
| 53 | + $value instanceof Collection => $value->first(), /* If the author field doesn't have a max_items of 1, it'll be a collection, so grab the first one. */ |
| 54 | + is_object($value) => $value->id(), /* If the author field had max_items 1 it would be a user, or since we got it above, use its id. */ |
| 55 | + default => $value |
| 56 | + }; |
| 57 | + } |
| 58 | + |
| 59 | + public function asset(string $field, int $width, int $height): string |
| 60 | + { |
| 61 | + $fields = $this->processFields($this->blueprint()->fields()->all()); |
| 62 | + |
| 63 | + $field = collect($fields)->dot()->get($field); |
| 64 | + |
| 65 | + $image = $this->image($width, $height); |
| 66 | + |
| 67 | + $uploadedFile = $this->uploadedFile($image); |
| 68 | + |
| 69 | + $id = AssetsUploader::field($field->config())->upload($uploadedFile); |
| 70 | + |
| 71 | + return $field->setValue($id)->process()->value(); |
| 72 | + } |
| 73 | + |
| 74 | + // TODO: This is basically a copy from the DefinitionGenerator. Can we abstract and reuse it? |
| 75 | + protected function processFields(Collection $fields): array |
| 76 | + { |
| 77 | + return $fields->map(fn (Field $field) => match ($field->type()) { |
| 78 | + 'bard' => $this->processBardAndReplicator($field), |
| 79 | + 'replicator' => $this->processBardAndReplicator($field), |
| 80 | + 'grid' => $this->processGrid($field), |
| 81 | + default => $field, |
| 82 | + })->all(); |
| 83 | + } |
| 84 | + |
| 85 | + // TODO: This is basically a copy from the DefinitionGenerator. Can we abstract and reuse it? |
| 86 | + protected function processBardAndReplicator(Field $field): array |
| 87 | + { |
| 88 | + return collect($field->toArray()['sets']) |
| 89 | + ->flatMap(function ($setGroup) { |
| 90 | + return collect($setGroup['sets'])->mapWithKeys(function ($set, $type) { |
| 91 | + return [$type => $this->processFields((new Fields($set['fields']))->all())]; |
| 92 | + }); |
| 93 | + })->toArray(); |
| 94 | + } |
| 95 | + |
| 96 | + // TODO: This is basically a copy from the DefinitionGenerator. Can we abstract and reuse it? |
| 97 | + protected function processGrid(Field $field): array |
| 98 | + { |
| 99 | + $fields = (new Fields($field->toArray()['fields'])) |
| 100 | + ->all() |
| 101 | + ->pipe($this->processFields(...)); |
| 102 | + |
| 103 | + return $fields; |
| 104 | + } |
| 105 | + |
| 106 | + protected function image(int $width, int $height): string |
| 107 | + { |
| 108 | + $storage = Storage::disk('local'); |
| 109 | + |
| 110 | + $dir = 'factory-tmp'; |
| 111 | + |
| 112 | + if (! $storage->exists($dir)) { |
| 113 | + $storage->makeDirectory($dir); |
| 114 | + } |
| 115 | + |
| 116 | + return $this->faker->image($storage->path($dir), $width, $height); |
| 117 | + } |
| 118 | + |
| 119 | + protected function uploadedFile(string $path): UploadedFile |
| 120 | + { |
| 121 | + $filesystem = new Filesystem; |
| 122 | + |
| 123 | + $name = $filesystem->name($path); |
| 124 | + $extension = $filesystem->extension($path); |
| 125 | + $originalName = "{$name}.{$extension}"; |
| 126 | + $mimeType = $filesystem->mimeType($path); |
| 127 | + $error = null; |
| 128 | + $test = true; |
| 129 | + |
| 130 | + return new UploadedFile($path, $originalName, $mimeType, $error, $test); |
| 131 | + } |
| 132 | +} |
0 commit comments