Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
65 / 65
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
MakePublicationTypeCommand
100.00% covered (success)
100.00%
65 / 65
100.00% covered (success)
100.00%
14 / 14
22
100.00% covered (success)
100.00%
1 / 1
 safeHandle
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 getTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 validateStorageDirectory
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
4
 captureFieldsDefinitions
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 captureFieldDefinition
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getFieldName
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 getFieldType
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getCanonicalField
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getSortField
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSortDirection
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getPageSize
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 checkIfFieldIsDuplicate
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getCount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 availableCanonicableFieldNames
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Hyde\Publications\Commands;
6
7use Hyde\Hyde;
8use Illuminate\Support\Str;
9use InvalidArgumentException;
10use Illuminate\Support\Collection;
11use LaravelZero\Framework\Commands\Command;
12use Hyde\Publications\Concerns\PublicationFieldTypes;
13use Hyde\Publications\Actions\CreatesNewPublicationType;
14use Hyde\Publications\Models\PublicationFieldDefinition;
15
16use function trim;
17use function count;
18use function is_dir;
19use function is_file;
20use function scandir;
21use function sprintf;
22use function in_array;
23use function array_keys;
24use function strtolower;
25
26/**
27 * Hyde command to create a new publication type.
28 *
29 * @see \Hyde\Publications\Actions\CreatesNewPublicationType
30 * @see \Hyde\Publications\Testing\Feature\MakePublicationTypeCommandTest
31 */
32class MakePublicationTypeCommand extends ValidatingCommand
33{
34    /** @var string */
35    protected $signature = 'make:publicationType {name? : The name of the publication type to create}';
36
37    /** @var string */
38    protected $description = 'Create a new publication type';
39
40    protected Collection $fields;
41
42    public function safeHandle(): int
43    {
44        $this->title('Creating a new Publication Type!');
45
46        $title = $this->getTitle();
47        $this->validateStorageDirectory(Str::slug($title));
48
49        $this->captureFieldsDefinitions();
50
51        $canonicalField = $this->getCanonicalField();
52        $sortField = $this->getSortField();
53        $sortAscending = $this->getSortDirection();
54        $pageSize = $this->getPageSize();
55
56        if ($this->fields->first()->name === '__createdAt') {
57            $this->fields->shift();
58        }
59
60        $creator = new CreatesNewPublicationType($title, $this->fields, $canonicalField->name, $sortField, $sortAscending, $pageSize);
61        $this->output->writeln("Saving publication data to [{$creator->getOutputPath()}]");
62        $creator->create();
63
64        $this->info('Publication type created successfully!');
65
66        return Command::SUCCESS;
67    }
68
69    protected function getTitle(): string
70    {
71        return $this->argument('name') ?: trim($this->askWithValidation('name', 'Publication type name', ['required', 'string']));
72    }
73
74    protected function validateStorageDirectory(string $directoryName): void
75    {
76        $path = Hyde::path($directoryName);
77        if (is_file($path) || (is_dir($path) && (count(scandir($path)) > 2))) {
78            throw new InvalidArgumentException("Storage path [$directoryName] already exists");
79        }
80    }
81
82    protected function captureFieldsDefinitions(): void
83    {
84        $this->line('Now please define the fields for your publication type:');
85
86        $this->fields = Collection::make([
87            new PublicationFieldDefinition(PublicationFieldTypes::Datetime, '__createdAt'),
88        ]);
89
90        do {
91            $this->fields->add($this->captureFieldDefinition());
92
93            $addAnother = $this->confirm(sprintf('Field #%d added! Add another field?', $this->getCount() - 1), $this->input->isInteractive());
94        } while ($addAnother);
95    }
96
97    protected function captureFieldDefinition(): PublicationFieldDefinition
98    {
99        $this->line('');
100
101        $fieldName = $this->getFieldName();
102        $fieldType = $this->getFieldType();
103
104        return new PublicationFieldDefinition($fieldType, $fieldName);
105    }
106
107    protected function getFieldName(?string $message = null): string
108    {
109        $message ??= "Enter name for field #{$this->getCount()}";
110        $default = $this->input->isInteractive() ? null : 'Example Field';
111
112        $selected = Str::kebab(trim($this->askWithValidation(
113            'name', $message, ['required'], default: $default
114        )));
115
116        if ($this->checkIfFieldIsDuplicate($selected)) {
117            return $this->getFieldName("Try again: Enter name for field #{$this->getCount()}");
118        }
119
120        return $selected;
121    }
122
123    protected function getFieldType(): PublicationFieldTypes
124    {
125        return PublicationFieldTypes::from(strtolower($this->choice(
126            "Enter type for field #{$this->getCount()}",
127            PublicationFieldTypes::names(),
128            'String'
129        )));
130    }
131
132    protected function getCanonicalField(): PublicationFieldDefinition
133    {
134        $options = $this->availableCanonicableFieldNames();
135
136        return $this->fields->firstWhere('name', $this->choice(
137            'Choose a canonical name field <fg=gray>(this will be used to generate filenames, so the values need to be unique)</>',
138            $options->toArray(),
139            $options->first()
140        ));
141    }
142
143    protected function getSortField(): string
144    {
145        return $this->choice('Choose the field you wish to sort by', $this->availableCanonicableFieldNames()->toArray(), 0);
146    }
147
148    protected function getSortDirection(): bool
149    {
150        $options = ['Ascending' => true, 'Descending' => false];
151
152        return $options[$this->choice('Choose the sort direction', array_keys($options), 'Ascending')];
153    }
154
155    protected function getPageSize(): int
156    {
157        return (int) $this->askWithValidation('pageSize',
158            'How many links should be shown on the listing page? <fg=gray>(any value above 0 will enable <href=https://docs.hydephp.com/search?query=pagination>pagination</>)</>',
159            ['required', 'integer', 'between:0,100'],
160            0
161        );
162    }
163
164    protected function checkIfFieldIsDuplicate(string $name): bool
165    {
166        if ($this->fields->where('name', $name)->count() > 0) {
167            $this->error("Field name [$name] already exists!");
168
169            return true;
170        }
171
172        return false;
173    }
174
175    protected function getCount(): int
176    {
177        return $this->fields->count();
178    }
179
180    protected function availableCanonicableFieldNames(): Collection
181    {
182        return $this->fields->reject(function (PublicationFieldDefinition $field): bool {
183            return ! in_array($field->type, PublicationFieldTypes::canonicable());
184        })->pluck('name');
185    }
186}