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

Committing code to 'tag' field type. #716

Merged
merged 13 commits into from
Nov 28, 2022
372 changes: 184 additions & 188 deletions composer.lock

Large diffs are not rendered by default.

42 changes: 29 additions & 13 deletions packages/framework/src/Console/Commands/MakePublicationCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function safeHandle(): int
return Command::SUCCESS;
}

protected function captureFieldInput(PublicationFieldType $field, Collection $mediaFiles): string|array
protected function captureFieldInput(PublicationFieldType $field, PublicationType $pubType): string|array
{
if ($field->type === 'text') {
$lines = [];
Expand All @@ -70,7 +70,7 @@ protected function captureFieldInput(PublicationFieldType $field, Collection $me
$lines[] = $line;
} while (true);

return implode("\n", $lines);
return $lines;
}

if ($field->type === 'array') {
Expand All @@ -81,25 +81,43 @@ protected function captureFieldInput(PublicationFieldType $field, Collection $me
if ($line === '') {
break;
}
$lines[] = $line;
$lines[] = trim($line);
} while (true);

return $lines;
}

if ($field->type === 'image') {
$this->output->writeln($field->name.' (end with an empty line)');
$offset = 0;
foreach ($mediaFiles as $index => $file) {
$offset = $index + 1;
$this->output->writeln(" $offset: $file");
}
$selected = $this->askWithValidation($field->name, $field->name, ['required', 'integer', "between:1,$offset"]);
do {
$offset = 0;
$mediaFiles = PublicationService::getMediaForPubType($pubType);
foreach ($mediaFiles as $index => $file) {
$offset = $index + 1;
$this->output->writeln(" $offset: $file");
}
$selected = (int) $this->askWithValidation($field->name, $field->name, ['required', 'integer', "between:1,$offset"]);
} while ($selected == 0);
$file = $mediaFiles->{$selected - 1};

return '_media/'.Str::of($file)->after('media/')->toString();
}

if ($field->type === 'tag') {
$this->output->writeln($field->name.' (enter 0 to reload tag definitions)');
do {
$offset = 0;
$tagsForGroup = PublicationService::getAllTags()->{$field->tagGroup};
foreach ($tagsForGroup as $index=>$value) {
$offset = $index + 1;
$this->output->writeln(" $offset: $value");
}
$selected = (int) $this->askWithValidation($field->name, $field->name, ['required', 'integer', "between:0,$offset"]);
} while ($selected == 0);

return $tagsForGroup->{$selected - 1};
}

// Fields which are not of type array, text or image
$fieldRules = Collection::create(PublicationFieldType::DEFAULT_RULES)->{$field->type};
if ($fieldRules->contains('between')) {
Expand Down Expand Up @@ -151,10 +169,8 @@ protected function collectFieldData(PublicationType $pubType): Collection
{
$this->output->writeln("\n<bg=magenta;fg=white>Now please enter the field data:</>");

$mediaFiles = PublicationService::getMediaForPubType($pubType);

return Collection::make($pubType->fields)->mapWithKeys(function ($field) use ($mediaFiles) {
return [$field['name'] => $this->captureFieldInput(PublicationFieldType::fromArray($field), $mediaFiles)];
return Collection::make($pubType->fields)->mapWithKeys(function ($field) use ($pubType) {
return [$field['name'] => $this->captureFieldInput(PublicationFieldType::fromArray($field), $pubType)];
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Hyde\Console\Commands;

use Hyde\Console\Commands\Interfaces\CommandHandleInterface;
use Hyde\Console\Concerns\ValidatingCommand;
use Hyde\Framework\Features\Publications\PublicationService;
use Hyde\Hyde;
use Illuminate\Support\Str;
use LaravelZero\Framework\Commands\Command;
use function Safe\file_put_contents;
use function Safe\json_encode;

/**
* Hyde Command to create a new publication type.
*
* @see \Hyde\Framework\Testing\Feature\Commands\MakePublicationTypeCommandTest
*/
class MakePublicationTagCommand extends ValidatingCommand implements CommandHandleInterface
{
/** @var string */
protected $signature = 'make:publicationTag';

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

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

$filename = Hyde::pathToRelative('tags.json');
$tags = PublicationService::getAllTags();
$tagName = $this->askWithValidation('name', 'Tag name', ['required', 'string']);
if (isset($tags[$tagName])) {
$this->output->error("Tag [$tagName] already exists");

return Command::FAILURE;
}

$lines = [];
$this->output->writeln('<bg=magenta;fg=white>Enter the tag values (end with an empty line):</>');
caendesilva marked this conversation as resolved.
Show resolved Hide resolved
do {
$line = Str::replace(["\n", "\r"], '', fgets(STDIN));
if ($line === '') {
break;
}
$lines[] = trim($line);
} while (true);
$tags[$tagName] = $lines;

$this->output->writeln(sprintf('Saving tag data to [%s]', $filename));
file_put_contents($filename, json_encode($tags, JSON_PRETTY_PRINT));

return Command::SUCCESS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Hyde\Console\Commands\Interfaces\CommandHandleInterface;
use Hyde\Console\Concerns\ValidatingCommand;
use Hyde\Framework\Actions\CreatesNewPublicationType;
use Hyde\Framework\Features\Publications\PublicationService;
use Illuminate\Support\Str;
use InvalidArgumentException;
use LaravelZero\Framework\Commands\Command;
Expand Down Expand Up @@ -76,14 +77,16 @@ public function handle(): int
);

$this->output->writeln('<bg=magenta;fg=white>Choose a canonical name field (the values of this field have to be unique!):</>');
$fieldNames = [];
foreach ($fields as $k => $v) {
if ($fields->first()->type != 'image') {
if ($v->type != 'image' && $v->type != 'tag') {
$fieldNames[] = $v->name;
$offset = $k + 1;
$this->line(" $offset: $v->name");
}
}
$selected = (int) $this->askWithValidation('selected', "Canonical field (1-$offset)", ['required', 'integer', "between:1,$offset"], 1);
$canonicalField = $fields[$selected - 1]['name'];
$canonicalField = $fieldNames[$selected - 1];

try {
$creator = new CreatesNewPublicationType($title, $fields, $canonicalField, $sortField, $sortDirection, $pageSize, $prevNextLinks, $this->output);
Expand All @@ -109,41 +112,64 @@ protected function captureFieldsDefinitions(): Collection
$this->output->writeln("<bg=cyan;fg=white>Field #$count:</>");

$field = Collection::create();
$field->name = $this->askWithValidation('name', 'Field name', ['required']);
do {
$field->name = trim($this->askWithValidation('name', 'Field name', ['required']));
$duplicate = $fields->where('name', $field->name)->count();
if ($duplicate) {
$this->error("Field name [$field->name] already exists!");
}
} while ($duplicate);

$this->line('Field type:');
$this->line(' 1 - String');
$this->line(' 2 - Boolean ');
$this->line(' 3 - Integer');
$this->line(' 4 - Float');
$this->line(' 5 - Datetime');
$this->line(' 5 - Datetime (YYYY-MM-DD (HH:MM:SS))');
$this->line(' 6 - URL');
$this->line(' 7 - Array');
$this->line(' 8 - Text');
$this->line(' 9 - Local Image');
$type = (int) $this->askWithValidation('type', 'Field type (1-9)', ['required', 'integer', 'between:1,9'], 1);
do {
// TODO This should only be done for types that can have length restrictions right?
$field->min = $this->askWithValidation('min', 'Min value (for strings, this refers to string length)', ['required', 'string'], 0);
$field->max = $this->askWithValidation('max', 'Max value (for strings, this refers to string length)', ['required', 'string'], 0);
$lengthsValid = true;
if ($field->max < $field->min) {
$lengthsValid = false;
$this->output->warning('Field length [max] must be [>=] than [min]');
$this->line(' 10 - Tag (select value from list)');
$type = (int) $this->askWithValidation('type', 'Field type (1-10)', ['required', 'integer', 'between:1,10'], 1);

if ($type < 10) {
do {
$field->min = trim($this->askWithValidation('min', 'Min value (for strings, this refers to string length)', ['required', 'string'], 0));
$field->max = trim($this->askWithValidation('max', 'Max value (for strings, this refers to string length)', ['required', 'string'], 0));
$lengthsValid = true;
if ($field->max < $field->min) {
$lengthsValid = false;
$this->output->warning('Field length [max] must be [>=] than [min]');
}
} while (! $lengthsValid);
} else {
$allTags = PublicationService::getAllTags();
$offset = 1;
foreach ($allTags as $k=>$v) {
$this->line(" $offset - $k");
$offset++;
}
} while (! $lengthsValid);
$addAnother = $this->askWithValidation('addAnother', 'Add another field (y/n)', ['required', 'string', 'in:y,n'], 'y');
$offset--; // The above loop overcounts by 1
$selected = $this->askWithValidation('tagGroup', 'Tag Group', ['required', 'integer', "between:1,$offset"], 0);
$field->tagGroup = $allTags->keys()->{$selected - 1};
$field->min = 0;
$field->max = 0;
}
$addAnother = $this->askWithValidation('addAnother', '<bg=magenta;fg=white>Add another field (y/n)</>', ['required', 'string', 'in:y,n'], 'y');

// map field choice to actual field type
$field->type = match ($type) {
1 => 'string',
2 => 'boolean',
3 => 'integer',
4 => 'float',
5 => 'datetime',
6 => 'url',
7 => 'array',
8 => 'text',
9 => 'image',
1 => 'string',
2 => 'boolean',
3 => 'integer',
4 => 'float',
5 => 'datetime',
6 => 'url',
7 => 'array',
8 => 'text',
9 => 'image',
10 => 'tag',
};

$fields->add($field);
Expand Down
18 changes: 8 additions & 10 deletions packages/framework/src/Console/Concerns/ValidatingCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,13 @@ public function askWithValidation(
array $rules = [],
mixed $default = null,
int $retryCount = 0
): mixed {
$answer = $this->ask(ucfirst($question), $default);
): string {
if ($retryCount >= self::MAX_RETRIES) {
// Prevent infinite loops that may happen, for example when testing. The retry count is high enough to not affect normal usage.
throw new RuntimeException(sprintf("Too many validation errors trying to validate '$name' with rules: [%s]", implode(', ', $rules)));
}

$answer = trim((string) $this->ask(ucfirst($question), $default));
$validator = Validator::make([$name => $answer], [$name => $rules]);

if ($validator->passes()) {
Expand All @@ -75,14 +80,7 @@ public function askWithValidation(
$this->error($this->translate($name, $error));
}

$retryCount++;

if ($retryCount >= self::MAX_RETRIES) {
// Prevent infinite loops that may happen, for example when testing. The retry count is high enough to not affect normal usage.
throw new RuntimeException(sprintf("Too many validation errors trying to validate '$name' with rules: [%s]", implode(', ', $rules)));
}

return $this->askWithValidation($name, $question, $rules, $default, $retryCount);
return $this->askWithValidation($name, $question, $rules, $default, $retryCount + 1);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function register(): void

Commands\MakePageCommand::class,
Commands\MakePostCommand::class,
Commands\MakePublicationTagCommand::class,
Commands\MakePublicationTypeCommand::class,
Commands\MakePublicationCommand::class,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class PublicationFieldType implements SerializableContract
{
use Serializable;

public final const TYPES = ['string', 'boolean', 'integer', 'float', 'datetime', 'url', 'array', 'text', 'image'];
public final const TYPES = ['string', 'boolean', 'integer', 'float', 'datetime', 'url', 'array', 'text', 'image', 'tag'];
public final const DEFAULT_RULES = [
'string' => ['required', 'string', 'between'],
'boolean' => ['required', 'boolean'],
Expand All @@ -29,27 +29,29 @@ class PublicationFieldType implements SerializableContract
];

public readonly string $type;
public readonly ?int $max;
public readonly ?int $min;
public readonly string $max;
public readonly string $min;
public readonly string $name;
public readonly ?string $tagGroup;

public static function fromArray(array $array): static
{
return new static(...$array);
}

public function __construct(string $type, string $name, int|string|null $min, int|string|null $max)
public function __construct(string $type, string $name, int|string|null $min, int|string|null $max, ?string $tagGroup = null)
{
$this->type = strtolower($type);
$this->name = Str::kebab($name);
$this->min = $this->parseInt($min);
$this->max = $this->parseInt($max);
$this->min = (string) $min;
$this->max = (string) $max;
$this->tagGroup = $tagGroup;

if (! in_array(strtolower($type), self::TYPES)) {
throw new InvalidArgumentException(sprintf("The type '$type' is not a valid type. Valid types are: %s.", implode(', ', self::TYPES)));
}

if (($min !== null) && ($max !== null) && $max < $min) {
if ($max < $min) {
throw new InvalidArgumentException("The 'max' value cannot be less than the 'min' value.");
}
}
Expand Down
Loading