Skip to content

Commit 5c315dc

Browse files
authored
Merge pull request #737 from hydephp/integrate-publications-seeder
Integrate publications seeder
2 parents 07b740a + d1b80b7 commit 5c315dc

File tree

6 files changed

+628
-4
lines changed

6 files changed

+628
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyde\Console\Commands;
6+
7+
use Hyde\Console\Concerns\ValidatingCommand;
8+
use Hyde\Framework\Actions\SeedsPublicationFiles;
9+
use Hyde\Framework\Features\Publications\Models\PublicationType;
10+
use Hyde\Framework\Features\Publications\PublicationService;
11+
use InvalidArgumentException;
12+
use LaravelZero\Framework\Commands\Command;
13+
use Rgasch\Collection\Collection;
14+
15+
/**
16+
* Hyde Command to seed publication files for a publication type.
17+
*
18+
* @see \Hyde\Framework\Actions\SeedsPublicationFiles
19+
* @see \Hyde\Framework\Testing\Feature\Commands\SeedPublicationCommandTest
20+
*
21+
* @todo Normalize command output style, maybe by hooking into the build actions?
22+
*/
23+
class SeedPublicationCommand extends ValidatingCommand
24+
{
25+
/** @var string */
26+
protected $signature = 'seed:publications
27+
{publicationType? : The name of the publication type to create publications for}
28+
{number? : The number of publications to generate}';
29+
30+
/** @var string */
31+
protected $description = 'Generate random publications for a publication type';
32+
33+
public function safeHandle(): int
34+
{
35+
$this->title('Seeding new publications!');
36+
37+
$pubType = $this->getPubTypeSelection($this->getPublicationTypes());
38+
$number = (int) ($this->argument('number') ?? $this->askWithValidation(
39+
'number',
40+
'How many publications would you like to generate',
41+
['required', 'integer', 'between:1,100000'], 1));
42+
43+
if ($number >= 10000) {
44+
$this->warn('Warning: Generating a large number of publications may take a while. <fg=gray>Expected time: '.($number / 1000).' seconds.</>');
45+
if (! $this->confirm('Are you sure you want to continue?')) {
46+
return parent::USER_EXIT;
47+
}
48+
}
49+
50+
$timeStart = microtime(true);
51+
$seeder = new SeedsPublicationFiles($pubType, $number);
52+
$seeder->create();
53+
54+
$ms = round((microtime(true) - $timeStart) * 1000);
55+
$each = round($ms / $number, 2);
56+
$this->info(sprintf("<comment>$number</comment> publication{$this->pluralize($number)} for <comment>$pubType->name</comment> created! <fg=gray>Took {$ms}ms%s",
57+
($number > 1) ? " ({$each}ms/each)</>" : ''));
58+
59+
return Command::SUCCESS;
60+
}
61+
62+
/**
63+
* @param \Rgasch\Collection\Collection<string, \Hyde\Framework\Features\Publications\Models\PublicationType> $pubTypes
64+
* @return \Hyde\Framework\Features\Publications\Models\PublicationType
65+
*/
66+
protected function getPubTypeSelection(Collection $pubTypes): PublicationType
67+
{
68+
$pubTypeSelection = $this->argument('publicationType') ?? $pubTypes->keys()->get(
69+
(int) $this->choice(
70+
'Which publication type would you like to seed?',
71+
$pubTypes->keys()->toArray()
72+
)
73+
);
74+
75+
if ($pubTypes->has($pubTypeSelection)) {
76+
if ($this->argument('number')) {
77+
$this->line("<info>Creating</info> [<comment>{$this->argument('number')}</comment>] <info>random publications for type</info> [<comment>$pubTypeSelection</comment>]");
78+
} else {
79+
$this->line("<info>Creating random publications for type</info> [<comment>$pubTypeSelection</comment>]");
80+
}
81+
82+
return $pubTypes->get($pubTypeSelection);
83+
}
84+
85+
throw new InvalidArgumentException("Unable to locate publication type [$pubTypeSelection]");
86+
}
87+
88+
/**
89+
* @return \Rgasch\Collection\Collection<string, PublicationType>
90+
*
91+
* @throws \InvalidArgumentException
92+
*/
93+
protected function getPublicationTypes(): Collection
94+
{
95+
$pubTypes = PublicationService::getPublicationTypes();
96+
if ($pubTypes->isEmpty()) {
97+
throw new InvalidArgumentException('Unable to locate any publication types. Did you create any?');
98+
}
99+
100+
return $pubTypes;
101+
}
102+
103+
protected function pluralize(int $count): string
104+
{
105+
return ($count === 1) ? '' : 's';
106+
}
107+
}

packages/framework/src/Console/HydeConsoleServiceProvider.php

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public function register(): void
4141
Commands\DebugCommand::class,
4242

4343
Commands\ValidatePublicationsCommand::class,
44+
Commands\SeedPublicationCommand::class,
4445
]
4546
);
4647
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyde\Framework\Actions;
6+
7+
use Hyde\Framework\Actions\Concerns\CreateAction;
8+
use Hyde\Framework\Actions\Contracts\CreateActionContract;
9+
use Hyde\Framework\Features\Publications\Models\PublicationField;
10+
use Hyde\Framework\Features\Publications\Models\PublicationType;
11+
use Hyde\Framework\Features\Publications\PublicationService;
12+
use Hyde\Pages\PublicationPage;
13+
use Illuminate\Support\Arr;
14+
use Illuminate\Support\Carbon;
15+
use Illuminate\Support\Str;
16+
use function in_array;
17+
use function rand;
18+
use function substr;
19+
use function time;
20+
use function trim;
21+
use function ucfirst;
22+
23+
/**
24+
* Seed publication files for a publication type.
25+
*
26+
* @see \Hyde\Console\Commands\SeedPublicationCommand
27+
* @see \Hyde\Framework\Testing\Feature\Actions\SeedsPublicationFilesTest
28+
*/
29+
class SeedsPublicationFiles extends CreateAction implements CreateActionContract
30+
{
31+
protected PublicationType $pubType;
32+
protected int $number = 1;
33+
34+
protected array $matter;
35+
protected string $canonicalValue;
36+
37+
public function __construct(PublicationType $pubType, int $number = 1)
38+
{
39+
$this->number = $number;
40+
$this->pubType = $pubType;
41+
}
42+
43+
protected function handleCreate(): void
44+
{
45+
$this->create();
46+
}
47+
48+
public function create(): void
49+
{
50+
for ($i = 0; $i < $this->number; $i++) {
51+
$this->matter = [];
52+
$this->canonicalValue = '';
53+
54+
$this->generatePublicationData();
55+
$identifier = Str::slug(substr($this->canonicalValue, 0, 64));
56+
57+
$page = new PublicationPage($identifier, $this->matter, '## Write something awesome.', $this->pubType);
58+
$page->save();
59+
}
60+
}
61+
62+
protected function generatePublicationData(): void
63+
{
64+
$this->matter['__createdAt'] = Carbon::today()->subDays(rand(1, 360))->addSeconds(rand(0, 86400));
65+
foreach ($this->pubType->getFields() as $field) {
66+
$this->matter[$field->name] = $this->generateFieldData($field);
67+
$this->getCanonicalFieldName($field);
68+
}
69+
70+
if (! $this->canonicalValue) {
71+
$this->canonicalValue = $this->fakeSentence(3);
72+
}
73+
}
74+
75+
protected function getDateTimeValue(): string
76+
{
77+
return date('Y-m-d H:i:s', rand(
78+
time() - 86400 + (rand(0, 86400)),
79+
time() - (86400 * 365) + (rand(0, 86400))
80+
));
81+
}
82+
83+
protected function getTextValue($lines): string
84+
{
85+
$value = '';
86+
87+
for ($i = 0; $i < $lines; $i++) {
88+
$value .= $this->fakeSentence(rand(5, 20))."\n";
89+
}
90+
91+
return $value;
92+
}
93+
94+
protected function generateFieldData(PublicationField $field): string|int|float|array|bool
95+
{
96+
return match ($field->type->value) {
97+
'array' => $this->getArrayItems(),
98+
'boolean' => rand(0, 100) < 50,
99+
'datetime' => $this->getDateTimeValue(),
100+
'float' => rand(-10000000, 10000000) / 100,
101+
'image' => 'https://picsum.photos/id/'.rand(1, 1000).'/400/400',
102+
'integer' => rand(-100000, 100000),
103+
'string' => substr($this->fakeSentence(10), 0, rand(0, 255)),
104+
'tag' => $this->getTags($field),
105+
'text' => $this->getTextValue(rand(3, 20)),
106+
'url' => $this->fakeUrl(),
107+
};
108+
}
109+
110+
protected function getCanonicalFieldName(PublicationField $field): void
111+
{
112+
if ($this->canFieldTypeCanBeCanonical($field->type->value)) {
113+
if ($field->name === $this->pubType->canonicalField) {
114+
$this->canonicalValue = $this->matter[$field->name];
115+
}
116+
}
117+
}
118+
119+
protected function canFieldTypeCanBeCanonical(string $value): bool
120+
{
121+
return in_array($value, ['url', 'text', 'string', 'integer', 'float', 'datetime', 'array']);
122+
}
123+
124+
protected function getArrayItems(): array
125+
{
126+
$arrayItems = [];
127+
for ($i = 0; $i < rand(3, 20); $i++) {
128+
$arrayItems[] = $this->fakeWord();
129+
}
130+
131+
return $arrayItems;
132+
}
133+
134+
protected function getTags(PublicationField $field): string
135+
{
136+
$tags = PublicationService::getValuesForTagName($field->tagGroup, false);
137+
138+
return $tags->isEmpty() ? '' : $tags->random();
139+
}
140+
141+
private const WORDS = [
142+
'lorem', 'ipsum', 'dolor', 'sit',
143+
'amet', 'consectetur', 'adipiscing', 'elit',
144+
'a', 'ac', 'accumsan', 'ad',
145+
'aenean', 'aliquam', 'aliquet', 'ante',
146+
'aptent', 'arcu', 'at', 'auctor',
147+
'augue', 'bibendum', 'blandit', 'class',
148+
'commodo', 'condimentum', 'congue', 'consequat',
149+
'conubia', 'convallis', 'cras', 'cubilia',
150+
'cum', 'curabitur', 'curae', 'cursus',
151+
'dapibus', 'diam', 'dictum', 'dictumst',
152+
'dignissim', 'dis', 'donec', 'dui',
153+
'duis', 'egestas', 'eget', 'eleifend',
154+
'elementum', 'enim', 'erat', 'eros',
155+
'est', 'et', 'etiam', 'eu',
156+
'euismod', 'facilisi', 'facilisis', 'fames',
157+
'faucibus', 'felis', 'fermentum', 'feugiat',
158+
'fringilla', 'fusce', 'gravida', 'habitant',
159+
'habitasse', 'hac', 'hendrerit', 'himenaeos',
160+
'iaculis', 'id', 'imperdiet', 'in',
161+
'inceptos', 'integer', 'interdum', 'justo',
162+
'lacinia', 'lacus', 'laoreet', 'lectus',
163+
'leo', 'libero', 'ligula', 'litora',
164+
'lobortis', 'luctus', 'maecenas', 'magna',
165+
'magnis', 'malesuada', 'massa', 'mattis',
166+
'mauris', 'metus', 'mi', 'molestie',
167+
'mollis', 'montes', 'morbi', 'mus',
168+
'nam', 'nascetur', 'natoque', 'nec',
169+
'neque', 'netus', 'nibh', 'nisi',
170+
'nisl', 'non', 'nostra', 'nulla',
171+
'nullam', 'nunc', 'odio', 'orci',
172+
'ornare', 'parturient', 'pellentesque', 'penatibus',
173+
'per', 'pharetra', 'phasellus', 'placerat',
174+
'platea', 'porta', 'porttitor', 'posuere',
175+
'potenti', 'praesent', 'pretium', 'primis',
176+
'proin', 'pulvinar', 'purus', 'quam',
177+
'quis', 'quisque', 'rhoncus', 'ridiculus',
178+
'risus', 'rutrum', 'sagittis', 'sapien',
179+
'scelerisque', 'sed', 'sem', 'semper',
180+
'senectus', 'sociis', 'sociosqu', 'sodales',
181+
'sollicitudin', 'suscipit', 'suspendisse', 'taciti',
182+
'tellus', 'tempor', 'tempus', 'tincidunt',
183+
'torquent', 'tortor', 'tristique', 'turpis',
184+
'ullamcorper', 'ultrices', 'ultricies', 'urna',
185+
'ut', 'varius', 'vehicula', 'vel',
186+
'velit', 'venenatis', 'vestibulum', 'vitae',
187+
'vivamus', 'viverra', 'volutpat', 'vulputate',
188+
];
189+
190+
private function fakeSentence(int $words): string
191+
{
192+
$sentence = '';
193+
for ($i = 0; $i < $words; $i++) {
194+
$sentence .= $this->fakeWord().' ';
195+
}
196+
197+
return ucfirst(trim($sentence)).'.';
198+
}
199+
200+
private function fakeWord(): string
201+
{
202+
return Arr::random(self::WORDS);
203+
}
204+
205+
private function fakeUrl(): string
206+
{
207+
return 'https://example.com/'.$this->fakeWord();
208+
}
209+
}

packages/framework/src/Framework/Features/Publications/PublicationService.php

+11-4
Original file line numberDiff line numberDiff line change
@@ -58,33 +58,40 @@ public static function getMediaForPubType(PublicationType $pubType): Collection
5858
/**
5959
* Get all available tags.
6060
*
61+
* @param bool $reload Reload the tags from the filesystem
6162
* @return \Rgasch\Collection\Collection
6263
*
6364
* @throws \Safe\Exceptions\FilesystemException
6465
* @throws \Safe\Exceptions\JsonException
6566
*/
66-
public static function getAllTags(): Collection
67+
public static function getAllTags(bool $reload = true): Collection
6768
{
6869
$filename = Hyde::path('tags.json');
6970
if (! file_exists($filename)) {
7071
return Collection::create();
7172
}
7273

73-
return Collection::create(json_decode(file_get_contents($filename), true))->sortKeys();
74+
static $tags = null;
75+
if (! $tags || $reload) {
76+
$tags = Collection::create(json_decode(file_get_contents($filename), true))->sortKeys();
77+
}
78+
79+
return $tags;
7480
}
7581

7682
/**
7783
* Get all values for a given tag name.
7884
*
7985
* @param string $tagName
86+
* @param bool $reload Reload the tags from the filesystem
8087
* @return \Rgasch\Collection\Collection
8188
*
8289
* @throws \Safe\Exceptions\FilesystemException
8390
* @throws \Safe\Exceptions\JsonException
8491
*/
85-
public static function getValuesForTagName(string $tagName): Collection
92+
public static function getValuesForTagName(string $tagName, bool $reload = true): Collection
8693
{
87-
$tags = static::getAllTags();
94+
$tags = self::getAllTags($reload);
8895
if (! $tags->get($tagName)) {
8996
return Collection::create();
9097
}

0 commit comments

Comments
 (0)