MakePublicationTypeCommand::getCanonicalField()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Publications\Commands;
6
7
use Hyde\Hyde;
8
use Illuminate\Support\Str;
9
use InvalidArgumentException;
10
use Illuminate\Support\Collection;
11
use LaravelZero\Framework\Commands\Command;
12
use Hyde\Publications\Concerns\PublicationFieldTypes;
13
use Hyde\Publications\Actions\CreatesNewPublicationType;
14
use Hyde\Publications\Models\PublicationFieldDefinition;
15
16
use function trim;
17
use function count;
18
use function is_dir;
19
use function is_file;
20
use function scandir;
21
use function sprintf;
22
use function in_array;
23
use function array_keys;
24
use 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
 */
32
class 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);
0 ignored issues
show
Bug introduced by
The method path() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

76
        /** @scrutinizer ignore-call */ 
77
        $path = Hyde::path($directoryName);
Loading history...
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([
0 ignored issues
show
Bug introduced by
array(new Hyde\Publicati...tetime, '__createdAt')) of type array<integer,Hyde\Publi...icationFieldDefinition> is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::make(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

86
        $this->fields = Collection::make(/** @scrutinizer ignore-type */ [
Loading history...
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(
0 ignored issues
show
Bug introduced by
It seems like $this->choice('Enter typ...pes::names(), 'String') can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
        return PublicationFieldTypes::from(strtolower(/** @scrutinizer ignore-type */ $this->choice(
Loading history...
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(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fields->fi...(), $options->first())) could return the type null which is incompatible with the type-hinted return Hyde\Publications\Models...licationFieldDefinition. Consider adding an additional type-check to rule them out.
Loading history...
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);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->choice('Ch...dNames()->toArray(), 0) could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
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
}
187