Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the make:publicationType command class #773

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
505f171
Use the enum types
caendesilva Dec 23, 2022
f6b805b
Remove unused length validation
caendesilva Dec 23, 2022
f1ed5af
Merge branch 'publications-feature' into refactor-MakePublicationType…
caendesilva Dec 23, 2022
8205f09
Sort PublicationFieldTypes enum cases
caendesilva Dec 23, 2022
422e2b7
Add array of the field types that can be canonical
caendesilva Dec 23, 2022
64cf0f1
Use a more readable approach for overriding the labels
caendesilva Dec 23, 2022
4451803
Remove label overrides to reduce complexity and as cases should alrea…
caendesilva Dec 23, 2022
4932339
Update helper method to return the enum instance
caendesilva Dec 23, 2022
dadf3cb
Update expected order
caendesilva Dec 23, 2022
fc9c3ba
Remove overcounter
caendesilva Dec 23, 2022
50875fd
Rename local parameter to clarify it
caendesilva Dec 23, 2022
4007c50
Refactor to return the enum type
caendesilva Dec 23, 2022
9df715f
Use the canonicable array to see if field can be canonical
caendesilva Dec 23, 2022
b5b8718
Warn and default to meta field when there are no selectable options
caendesilva Dec 23, 2022
56ebb79
Update warning message
caendesilva Dec 23, 2022
198638d
Update question message
caendesilva Dec 23, 2022
215819c
Split comma-separated values into multiple lines
caendesilva Dec 23, 2022
66555a9
Apply fixes from StyleCI
StyleCIBot Dec 23, 2022
a009421
Test the canonicable method
caendesilva Dec 23, 2022
ef6ed80
Clean up filesystem in teardown method
caendesilva Dec 23, 2022
3aec858
Use built in confirmation helper
caendesilva Dec 23, 2022
08ffd7c
Test command with multiple fields of the same name
caendesilva Dec 23, 2022
aadbbd6
Use strict array validation
caendesilva Dec 23, 2022
af3d9e6
Sort options for better ease of access
caendesilva Dec 23, 2022
6cc96cc
Use the built in confirm helper
caendesilva Dec 23, 2022
4a5d8e3
Add option to use default values
caendesilva Dec 23, 2022
5a9bb3f
Revert "Add option to use default values"
caendesilva Dec 23, 2022
ecd1eb7
Update argument name 'title' to 'name' to match rest of the code
caendesilva Dec 23, 2022
8340439
Display the field number
caendesilva Dec 23, 2022
d122dae
Update MakePublicationTypeCommandTest.php
caendesilva Dec 23, 2022
fd23b4c
Convert variable to class property
caendesilva Dec 23, 2022
6d4375b
Use default value
caendesilva Dec 23, 2022
992ba9b
Use standard output styling
caendesilva Dec 23, 2022
1efc07a
Extract method to get the pagination settings
caendesilva Dec 23, 2022
2bc7791
Reorder methods
caendesilva Dec 23, 2022
1bbeaa6
Convert array to short associative syntax
caendesilva Dec 23, 2022
4b3e7d3
Create array of default values
caendesilva Dec 23, 2022
5f2faad
Partially revert "Revert "Add option to use default values""
caendesilva Dec 23, 2022
5a6e3cc
Return paginationDefaults when use-defaults flag is set
caendesilva Dec 23, 2022
eb3319a
Don't ask to add another when use-defaults flag is set
caendesilva Dec 23, 2022
108997c
Use __createdAt as canonical when use-defaults flag is set
caendesilva Dec 23, 2022
21a8771
Add todo
caendesilva Dec 23, 2022
1c3a8c6
Split out if block
caendesilva Dec 23, 2022
9c98578
Refactor to add date meta field as the zeroth field
caendesilva Dec 23, 2022
3dc9eb4
Use the first field as canonical when use-defaults flag is set
caendesilva Dec 23, 2022
1c0c2a6
Unwrap if-block as array is now never empty
caendesilva Dec 23, 2022
4bde7c2
Use option instead of hasOption
caendesilva Dec 23, 2022
225437b
Refactor sort label logic
caendesilva Dec 23, 2022
61852d0
Refactor add primitive array data and only construct objects at the end
caendesilva Dec 23, 2022
9b9f3b8
Normalize string at the end
caendesilva Dec 23, 2022
600a31e
Revert "Normalize string at the end"
caendesilva Dec 23, 2022
b9c91e6
Revert "Refactor add primitive array data and only construct objects …
caendesilva Dec 23, 2022
4667395
Remove label overrides for __createdAt meta field
caendesilva Dec 23, 2022
d8a4842
Add option to not normalize the name
caendesilva Dec 23, 2022
2812656
Don't normalize the __createdAt name
caendesilva Dec 23, 2022
6eeac7e
Expect the meta field to be added
caendesilva Dec 23, 2022
cf71ce5
Remove descriptors for sort directions
caendesilva Dec 23, 2022
e35fc6f
Ask if user wants to configure pagination settings
caendesilva Dec 23, 2022
4b7aec5
Add count prefixes to ask messages as mockery seems completely unable…
caendesilva Dec 23, 2022
5346f4e
Update MakePublicationTypeCommandTest.php
caendesilva Dec 23, 2022
e2fcdc2
Update MakePublicationTypeCommandTest.php
caendesilva Dec 23, 2022
c162deb
Apply fixes from StyleCI
StyleCIBot Dec 23, 2022
800d09a
Refactor to use class property instead of throwing around local variable
caendesilva Dec 23, 2022
8d844b3
Join comma-separated values into a single line
caendesilva Dec 23, 2022
0b18dae
Use the class property
caendesilva Dec 23, 2022
99cfabb
Simplify list return values
caendesilva Dec 23, 2022
62d418a
Split comma-separated values into multiple lines and inline variables
caendesilva Dec 23, 2022
222965d
Invert 'if' statement
caendesilva Dec 23, 2022
9101a36
Revert "Invert 'if' statement"
caendesilva Dec 23, 2022
4315cef
Join comma-separated values into a single line
caendesilva Dec 23, 2022
f4f4d11
Inline local variable
caendesilva Dec 23, 2022
2055314
Use stricter comparison
caendesilva Dec 23, 2022
4ec96a5
Simplify return statement and inline variable
caendesilva Dec 23, 2022
22505fd
Apply fixes from StyleCI
StyleCIBot Dec 23, 2022
589d83d
Extract method
caendesilva Dec 23, 2022
095f9ab
Rename local variable
caendesilva Dec 23, 2022
32d26f7
Initialize variable
caendesilva Dec 23, 2022
a0106ee
Simplify 'if'
caendesilva Dec 23, 2022
dcc5c6b
Inline variable
caendesilva Dec 23, 2022
dbe4484
Convert concatenation to 'sprintf()' call
caendesilva Dec 23, 2022
a3198b7
Break out message assembly
caendesilva Dec 23, 2022
369f8dd
Replace do while loop with recursion
caendesilva Dec 23, 2022
ce0363c
Use the notIn Laravel validation rule
caendesilva Dec 23, 2022
25a77ba
Revert "Use the notIn Laravel validation rule"
caendesilva Dec 23, 2022
99f5763
Merge dynamic message handler with state boolean
caendesilva Dec 23, 2022
b5acbe7
Remove unused tag logic and display a tip instead
caendesilva Dec 23, 2022
f20dd88
Apply fixes from StyleCI
StyleCIBot Dec 23, 2022
ad34e53
Remove code comment
caendesilva Dec 23, 2022
614604f
Introduce local variable
caendesilva Dec 23, 2022
fc5f9c6
Re-sort enum cases for ease of access
caendesilva Dec 24, 2022
9a90f50
Create method to get the enum names
caendesilva Dec 24, 2022
0b74421
Revert "Add option to not normalize the name"
caendesilva Dec 24, 2022
1707462
Don't normalize meta field names
caendesilva Dec 24, 2022
1235922
Extract method
caendesilva Dec 24, 2022
7aec7fc
Ask to create another after the first one has been added
caendesilva Dec 24, 2022
6fa612b
Add marker
caendesilva Dec 24, 2022
4cc59b8
Split out local array variable
caendesilva Dec 24, 2022
8088068
Merge local variables
caendesilva Dec 24, 2022
eeecfd4
Extract method
caendesilva Dec 24, 2022
c59883a
Move up helper method
caendesilva Dec 24, 2022
d9c49b8
Simplify object construction
caendesilva Dec 24, 2022
23436fe
Apply fixes from StyleCI
StyleCIBot Dec 24, 2022
bcf0c15
Add method to count added items
caendesilva Dec 24, 2022
1586164
Refactor to use the actual data count instead of class property
caendesilva Dec 24, 2022
80bd4bc
Add offset parameter to support string interpolation
caendesilva Dec 24, 2022
be6f212
Apply fixes from StyleCI
StyleCIBot Dec 24, 2022
ab47175
Mark setting as deprecation candidate
caendesilva Dec 24, 2022
a97de95
Break out code from if block so it also validates argument data
caendesilva Dec 24, 2022
9b1554f
Test existence of files and directories independently of each other
caendesilva Dec 24, 2022
f424ef3
Use absolute paths for filesystem calls
caendesilva Dec 24, 2022
dc7cc6e
Use is_file instead of file_exists
caendesilva Dec 24, 2022
4ffa472
Test conflict handling
caendesilva Dec 24, 2022
c19f722
Apply fixes from StyleCI
StyleCIBot Dec 24, 2022
22d14ed
Extract method
caendesilva Dec 24, 2022
72e6c5d
Shift slug call
caendesilva Dec 24, 2022
4c4b005
Expand variable name abbreviation
caendesilva Dec 24, 2022
359d990
Shorten and normalize argument description
caendesilva Dec 24, 2022
7990cf5
Simplify if
caendesilva Dec 24, 2022
d928bb2
Extract method
caendesilva Dec 24, 2022
0da2de8
Add clarifying parentheses
caendesilva Dec 24, 2022
7655b05
Move deprecation candidate notice to data object
caendesilva Dec 24, 2022
24881f0
Expand property name in question output to make message more fluent
caendesilva Dec 24, 2022
754bbae
Remove redundant arguments
caendesilva Dec 24, 2022
5d2df5b
Use expectsConfirmation instead
caendesilva Dec 24, 2022
bd8b153
Join comma-separated values into a single line
caendesilva Dec 24, 2022
cb978a8
Move up helper methods
caendesilva Dec 24, 2022
b304347
Import used function
caendesilva Dec 24, 2022
550b0e3
Unwrap line
caendesilva Dec 24, 2022
3de00f3
Apply fixes from StyleCI
StyleCIBot Dec 24, 2022
3dba236
Make action pagination parameters nullable and shift defaults to there
caendesilva Dec 24, 2022
72c9b25
Reorder questions to match data order
caendesilva Dec 24, 2022
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
224 changes: 111 additions & 113 deletions packages/framework/src/Console/Commands/MakePublicationTypeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@

namespace Hyde\Console\Commands;

use function array_flip;
use function array_keys;
use function array_merge;
use function file_exists;
use Hyde\Console\Concerns\ValidatingCommand;
use Hyde\Framework\Actions\CreatesNewPublicationType;
use Hyde\Framework\Features\Publications\Models\PublicationField;
use Hyde\Framework\Features\Publications\PublicationFieldTypes;
use Hyde\Framework\Features\Publications\PublicationService;
use Hyde\Hyde;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use function in_array;
use InvalidArgumentException;
use function is_dir;
use function is_file;
use LaravelZero\Framework\Commands\Command;
use function scandir;
use function strtolower;
Expand All @@ -32,179 +31,178 @@ class MakePublicationTypeCommand extends ValidatingCommand
{
/** @var string */
protected $signature = 'make:publicationType
{title? : The name of the Publication Type to create. Will be used to generate the storage directory}';
{name? : The name of the publication type to create}
{--use-defaults : Select the default options wherever possible}';

/** @var string */
protected $description = 'Create a new publication type definition';

protected Collection $fields;

public function safeHandle(): int
{
$this->title('Creating a new Publication Type!');

$title = $this->argument('title');
if (! $title) {
$title = trim($this->askWithValidation('name', 'Publication type name', ['required', 'string']));
$dirname = Str::slug($title);
if (file_exists($dirname) && is_dir($dirname) && count(scandir($dirname)) > 2) {
throw new InvalidArgumentException("Storage path [$dirname] already exists");
}
}

$fields = $this->captureFieldsDefinitions();
$title = $this->getTitle();

$sortField = $this->getSortField($fields);
$this->validateStorageDirectory(Str::slug($title));

$sortAscending = $this->getSortDirection();
$this->fields = $this->captureFieldsDefinitions();

$pageSize = $this->getPageSize();
$prevNextLinks = $this->getPrevNextLinks();
[$sortField, $sortAscending, $prevNextLinks, $pageSize] = ($this->getPaginationSettings());

$canonicalField = $this->getCanonicalField($fields);
$canonicalField = $this->getCanonicalField();

$creator = new CreatesNewPublicationType($title, $fields, $canonicalField, $sortField, $sortAscending, $prevNextLinks, $pageSize, $this->output);
$creator = new CreatesNewPublicationType($title, $this->fields, $canonicalField->name, $sortField, $sortAscending, $prevNextLinks, $pageSize, $this->output);
$creator->create();

$this->info('Publication type created successfully!');

return Command::SUCCESS;
}

protected function getTitle(): string
{
return $this->argument('name') ?: trim($this->askWithValidation('name', 'Publication type name', ['required', 'string']));
}

protected function validateStorageDirectory(string $directoryName): void
{
if (is_file(Hyde::path($directoryName)) || (is_dir(Hyde::path($directoryName)) && (count(scandir($directoryName)) > 2))) {
throw new InvalidArgumentException("Storage path [$directoryName] already exists");
}
}

protected function captureFieldsDefinitions(): Collection
{
$this->output->writeln('<bg=magenta;fg=white>You now need to define the fields in your publication type:</>');
$count = 1;
$fields = Collection::make();
do {
$this->line('');
$this->output->writeln("<bg=cyan;fg=white>Field #$count:</>");
$this->line('You now need to define the fields in your publication type:');
$this->fields = Collection::make();

$fieldData = [];
do {
$fieldData['name'] = Str::kebab(trim($this->askWithValidation('name', 'Field name', ['required'])));
$duplicate = $this->checkIfFieldIsDuplicate($fields, $fieldData['name']);
} while ($duplicate);
$this->addCreatedAtMetaField();

$type = $this->getFieldType();
do {
$this->fields->add($this->captureFieldDefinition());

if ($type === 10) {
$fieldData = $this->getFieldDataForTag($fieldData);
if ($this->option('use-defaults') === true) {
$addAnother = false;
} else {
$addAnother = $this->confirm("Field #{$this->getCount(-1)} added! Add another field?");
}
$addAnother = $this->askWithValidation('addAnother', '<bg=magenta;fg=white>Add another field (y/n)</>', ['required', 'string', 'in:y,n'], 'n');
} while ($addAnother);

// map field choice to actual field type
$fieldData['type'] = PublicationFieldTypes::values()[$type - 1];
return $this->fields;
}

protected function captureFieldDefinition(): PublicationField
{
$this->line('');

$fieldName = $this->getFieldName();

$fieldType = $this->getFieldType();

if ($fieldType === PublicationFieldTypes::Tag) {
$this->comment('Tip: Hyde will look for tags matching the name of the publication!');
}

$fields->add(PublicationField::fromArray($fieldData));
$count++;
} while (strtolower($addAnother) !== 'n');
// TODO: Here we could collect other data like the "rules" array for the field.

return $fields;
return new PublicationField($fieldType, $fieldName);
}

protected function getFieldType(): int
protected function getFieldName(?string $message = null): string
{
$options = PublicationFieldTypes::cases();
foreach ($options as $key => $value) {
$options[$key] = $value->name;
$selected = Str::kebab(trim($this->askWithValidation('name', $message ?? "Enter name for field #{$this->getCount()}", ['required'])));

if ($this->checkIfFieldIsDuplicate($selected)) {
return $this->getFieldName("Try again: Enter name for field #{$this->getCount()}");
}
$options[4] = 'Datetime (YYYY-MM-DD (HH:MM:SS))';
$options[5] = 'URL';
$options[8] = 'Local Image';
$options[9] = 'Tag (select value from list)';

return (int) $this->choice('Field type', $options, 1) + 1;
return $selected;
}

protected function getSortField(Collection $fields): string
protected function getFieldType(): PublicationFieldTypes
{
$options = array_merge(['dateCreated (meta field)'], $fields->pluck('name')->toArray());
$options = PublicationFieldTypes::names();

$selected = $this->choice('Choose the default field you wish to sort by', $options, 'dateCreated (meta field)');
$choice = $this->choice("Enter type for field #{$this->getCount()}", $options, 'String');

return $selected === 'dateCreated (meta field)' ? '__createdAt' : $options[(array_flip($options)[$selected])];
return PublicationFieldTypes::from(strtolower($choice));
}

protected function getSortDirection(): bool
protected function getCanonicalField(): PublicationField
{
$options = [
'Ascending (oldest items first if sorting by dateCreated)' => true,
'Descending (newest items first if sorting by dateCreated)' => false,
];
$selectableFields = $this->fields->reject(function (PublicationField $field): bool {
return in_array($field, PublicationFieldTypes::canonicable());
});

if ($this->option('use-defaults')) {
return $selectableFields->first();
}

$options = $selectableFields->pluck('name');

$selected = $this->choice('Choose a canonical name field (this will be used to generate filenames, so the values need to be unique)',
$options->toArray(),
$options->first()
);

return $options[$this->choice('Choose the default sort direction', array_keys($options), 'Ascending (oldest items first if sorting by dateCreated)')];
return $this->fields->firstWhere('name', $selected);
}

protected function getPageSize(): int
protected function checkIfFieldIsDuplicate($name): bool
{
return (int) $this->askWithValidation(
'pageSize',
'Enter the pageSize (0 for no limit)',
['required', 'integer', 'between:0,100'],
25
);
if ($this->fields->where('name', $name)->count() > 0) {
$this->error("Field name [$name] already exists!");

return true;
}

return false;
}

protected function getPrevNextLinks(): bool
protected function addCreatedAtMetaField(): void
{
return (bool) $this->askWithValidation(
'prevNextLinks',
'Generate previous/next links in detail view (y/n)',
['required', 'string', 'in:y,n'],
'y'
);
$this->fields->add(new PublicationField(PublicationFieldTypes::Datetime, '__createdAt'));
}

protected function getCanonicalField(Collection $fields): string
protected function getPaginationSettings(): array
{
$options = $fields->reject(function (PublicationField $field): bool {
// Temporary verbose check to see code coverage
if ($field->type === 'image') {
return true;
} elseif ($field->type === 'tag') {
return true;
} else {
return false;
}
})->pluck('name');
if ($this->option('use-defaults') || ! $this->confirm('Do you want to configure pagination settings?')) {
return [null, null, null, null];
}

return $this->choice('Choose a canonical name field (the values of this field have to be unique!)', $options->toArray(), $options->first());
return [$this->getSortField(), $this->getSortDirection(), $this->getPrevNextLinks(), $this->getPageSize()];
}

protected function validateLengths(string $min, string $max): bool
protected function getSortField(): string
{
if ($max < $min) {
$this->error('Field length [max] cannot be less than [min]');
return $this->choice('Choose the default field you wish to sort by', $this->fields->pluck('name')->toArray(), '__dateCreated');
}

return false;
}
protected function getSortDirection(): bool
{
$options = ['Ascending' => true, 'Descending' => false];

return true;
return $options[$this->choice('Choose the default sort direction', array_keys($options), 'Ascending')];
}

protected function getFieldDataForTag(array $fieldData): array
protected function getPrevNextLinks(): bool
{
$allTags = PublicationService::getAllTags();
$offset = 1;
foreach ($allTags as $k => $v) {
$this->line(" $offset - $k");
$offset++;
}
$offset--; // The above loop overcounts by 1
$selected = $this->askWithValidation('tagGroup', 'Tag Group', ['required', 'integer', "between:1,$offset"], 0);
$fieldData['tagGroup'] = $allTags->keys()->{$selected - 1};
$fieldData['min'] = 0;
$fieldData['max'] = 0;

return $fieldData;
return $this->confirm('Generate previous/next links in detail view?', true);
}

protected function checkIfFieldIsDuplicate(Collection $fields, $name): bool
protected function getPageSize(): int
{
$duplicate = $fields->where('name', $name)->count();
if ($duplicate) {
$this->error("Field name [$name] already exists!");
}
return (int) $this->askWithValidation('pageSize',
'Enter the page size (0 for no limit)',
['required', 'integer', 'between:0,100'],
25
);
}

return (bool) $duplicate;
protected function getCount(int $offset = 0): int
{
return $this->fields->count() + $offset;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ public function __construct(
protected string $name,
protected Collection $fields,
protected string $canonicalField,
protected string $sortField,
protected bool $sortAscending,
protected bool $prevNextLinks,
protected int $pageSize,
protected ?string $sortField,
protected ?bool $sortAscending,
protected ?bool $prevNextLinks,
protected ?int $pageSize,
protected ?OutputStyle $output = null,
) {
$this->dirName = $this->formatStringForStorage($this->name);
Expand All @@ -42,10 +42,10 @@ protected function handleCreate(): void
"{$this->dirName}_detail",
"{$this->dirName}_list",
[
$this->sortField,
$this->sortAscending,
$this->prevNextLinks,
$this->pageSize,
$this->sortField ?? '__createdAt',
$this->sortAscending ?? true,
$this->prevNextLinks ?? true,
$this->pageSize ?? 25,
],
$this->fields->toArray()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class PaginationSettings implements SerializableContract

public string $sortField = '__createdAt';
public bool $sortAscending = true;
/** @deprecated This setting might be deprecated as its unlikely one would enable page size limits without a way to traverse them */
public bool $prevNextLinks = true;
public int $pageSize = 25;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use function str_starts_with;
use function strtolower;

/**
Expand All @@ -37,7 +38,7 @@ public static function fromArray(array $array): static
public function __construct(PublicationFieldTypes|string $type, string $name, array $rules = [])
{
$this->type = $type instanceof PublicationFieldTypes ? $type : PublicationFieldTypes::from(strtolower($type));
$this->name = Str::kebab($name);
$this->name = str_starts_with($name, '__') ? $name : Str::kebab($name);
$this->rules = $rules;
}

Expand Down
Loading