Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 2 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,7 @@
"clear": "@php vendor/bin/testbench package:purge-skeleton --ansi",
"prepare": "@php vendor/bin/testbench package:discover --ansi",
"style": "vendor/bin/pint --parallel --ansi",
"test": [
"@clear",
"@php vendor/bin/pest --colors=always"
],
"test:update": [
"@clear",
"@php vendor/bin/pest --colors=always --update-snapshots"
]
"test": "@php vendor/bin/pest --colors=always",
"test:update": "@php vendor/bin/pest --colors=always --update-snapshots"
}
}
71 changes: 48 additions & 23 deletions docs/topics/generation.topic
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,64 @@

<show-structure depth="2" />

<snippet id="generate">
<chapter title="Generate all feeds" id="generate_all_feeds">
<snippet id="generate">
<p>
To generate feeds, create the classes of feeds and its element, add links to the file
<code>%config-filename%</code>, next call the console command:
</p>

<code-block lang="bash">
%command-generate%
</code-block>
</snippet>
</chapter>

<chapter title="Generate a single feed" id="generate_a_single_feed">
<note>
Please note that the specified feed will be executed even if it is disabled in the settings file.
</note>

<p>
To generate a specific feed class, specify the class reference or name relative to the
<code>App\Feeds</code> namespace.
</p>

<p>
To generate feeds, create the classes of feeds and its element, add links to the file
<code>%config-filename%</code>, next call the console command:
For example:
</p>

<code-block lang="bash">
%command-generate%
%command-generate% App\Feeds\UserFeed
%command-generate% UserFeed
%command-generate% User
</code-block>
</snippet>
</chapter>

<p>
Each feed can be created in a certain folder of a certain storage.
</p>
<chapter title="Change location" id="change_location">
<p>
Each feed can be created in a certain folder of a certain storage.
</p>

<p>
To indicate the storage, override the property of <code>$storage</code> in the feed class:
</p>
<p>
To indicate the storage, override the property of <code>$storage</code> in the feed class:
</p>

<code-block lang="php" src="generation-feed-storage.php" include-lines="5-" />
<code-block lang="php" src="generation-feed-storage.php" include-lines="5-" />

<p>
By default, storage is <code>public</code>.
</p>
<p>
By default, storage is <code>public</code>.
</p>

<p>
The path to the file inside the storage is indicated in the <code>filename</code> method:
</p>
<p>
The path to the file inside the storage is indicated in the <code>filename</code> method:
</p>

<code-block lang="php" src="generation-feed-filename.php" include-lines="5-" />
<code-block lang="php" src="generation-feed-filename.php" include-lines="5-" />

<p>
By default, the class name in <code>kebab-case</code> is used.
For example, <code>user-feed.xml</code> for <code>UserFeed</code> class.
</p>
<p>
By default, the class name in <code>kebab-case</code> is used.
For example, <code>user-feed.xml</code> for <code>UserFeed</code> class.
</p>
</chapter>
</topic>
18 changes: 14 additions & 4 deletions src/Console/Commands/FeedGenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace DragonCode\LaravelFeed\Console\Commands;

use DragonCode\LaravelFeed\Helpers\FeedHelper;
use DragonCode\LaravelFeed\Services\Generator;
use Illuminate\Console\Command;
use Laravel\Prompts\Concerns\Colors;
Expand All @@ -18,24 +19,33 @@ class FeedGenerateCommand extends Command
{
use Colors;

public function handle(Generator $generator): void
public function handle(Generator $generator, FeedHelper $helper): void
{
foreach ($this->feedable() as $feed => $enabled) {
foreach ($this->feedable($helper) as $feed => $enabled) {
$enabled
? $this->components->task($feed, fn () => $generator->feed(app($feed)))
: $this->components->twoColumnDetail($feed, $this->messageYellow('SKIP'));
}
}

protected function feedable(): array
protected function feedable(FeedHelper $helper): array
{
if ($feed = $this->argument('class')) {
if ($feed = $this->resolveFeedClass($helper)) {
return [$feed => true];
}

return config('feeds.channels');
}

protected function resolveFeedClass(FeedHelper $helper): ?string
{
if (! $class = $this->argument('class')) {
return null;
}

return $helper->find((string) $class);
}

protected function messageYellow(string $message): string
{
if ($this->option('no-ansi')) {
Expand Down
15 changes: 15 additions & 0 deletions src/Exceptions/FeedNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace DragonCode\LaravelFeed\Exceptions;

use InvalidArgumentException;

class FeedNotFoundException extends InvalidArgumentException
{
public function __construct(string $class)
{
parent::__construct("Feed [$class] not found.");
}
}
18 changes: 18 additions & 0 deletions src/Exceptions/UnexpectedFeedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace DragonCode\LaravelFeed\Exceptions;

use DragonCode\LaravelFeed\Feeds\Feed;
use UnexpectedValueException;

class UnexpectedFeedException extends UnexpectedValueException
{
public function __construct(string $class)
{
parent::__construct(
sprintf('The [%s] class must implement %s.', $class, Feed::class)
);
}
}
58 changes: 58 additions & 0 deletions src/Helpers/FeedHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace DragonCode\LaravelFeed\Helpers;

use DragonCode\LaravelFeed\Exceptions\FeedNotFoundException;
use DragonCode\LaravelFeed\Exceptions\UnexpectedFeedException;
use DragonCode\LaravelFeed\Feeds\Feed;
use Illuminate\Foundation\Application;
use Illuminate\Support\Str;

use function class_exists;
use function is_a;

class FeedHelper
{
public function __construct(
protected Application $laravel
) {}

public function find(string $class): string
{
if (class_exists($class)) {
return $this->ensure($class);
}

if (class_exists($class = $this->resolve($class))) {
return $this->ensure($class);
}

throw new FeedNotFoundException($class);
}

protected function resolve(string $class): string
{
return Str::of($class)
->replace('/', '\\')
->ltrim('\\')
->start($this->rootNamespace() . 'Feeds\\')
->finish('Feed')
->toString();
}

protected function ensure(string $class): string
{
if (! is_a($class, Feed::class, true)) {
throw new UnexpectedFeedException($class);
}

return $class;
}

protected function rootNamespace(): string
{
return $this->laravel->getNamespace();
}
}
1 change: 1 addition & 0 deletions testbench.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ laravel: '@testbench'

providers:
- DragonCode\LaravelFeed\LaravelFeedServiceProvider
- Workbench\App\Providers\WorkbenchServiceProvider

migrations:
- workbench/database/migrations
Expand Down
23 changes: 23 additions & 0 deletions tests/Feature/Console/Generation/DefaultTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

use DragonCode\LaravelFeed\Console\Commands\FeedGenerateCommand;

use function Pest\Laravel\artisan;

test('generate', function () {
$command = artisan(FeedGenerateCommand::class);

config()
?->collection('feeds.channels')
?->keys()
?->each(fn (string $feed) => $command->expectsOutputToContain($feed));

$command->assertSuccessful()->run();

config()
?->collection('feeds.channels')
?->keys()
?->each(fn (string $feed) => expect(app($feed)->path())->toBeReadableFile());
});
32 changes: 32 additions & 0 deletions tests/Feature/Console/Generation/DisabledTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

use DragonCode\LaravelFeed\Console\Commands\FeedGenerateCommand;
use Workbench\App\Feeds\SitemapFeed;
use Workbench\App\Feeds\YandexFeed;

use function Pest\Laravel\artisan;

test('generate', function () {
config()?->set('feeds.channels.' . SitemapFeed::class, false);
config()?->set('feeds.channels.' . YandexFeed::class, false);

$command = artisan(FeedGenerateCommand::class);

config()
?->collection('feeds.channels')
?->keys()
?->each(fn (string $feed) => $command->expectsOutputToContain($feed));

$command->assertSuccessful()->run();

config()
?->collection('feeds.channels')
?->keys()
?->each(fn (string $feed) => match ($feed) {
SitemapFeed::class,
YandexFeed::class => expect(app($feed)->path())->not->toBeReadableFile(),
default => expect(app($feed)->path())->toBeReadableFile()
});
});
44 changes: 44 additions & 0 deletions tests/Feature/Console/Generation/IncorrectParameterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

use DragonCode\LaravelFeed\Console\Commands\FeedGenerateCommand;
use DragonCode\LaravelFeed\Exceptions\FeedNotFoundException;

use function Pest\Laravel\artisan;

test('incorrect', function (mixed $name) {
artisan(FeedGenerateCommand::class, [
'class' => $name,
])->run();
})
->throws(FeedNotFoundException::class)
->with([
'foo=bar',
'foo+bar',
'foo bar',
'123',
123,
]);

test('may be correct', function (mixed $name) {
$command = artisan(FeedGenerateCommand::class, [
'class' => $name,
]);

config()
?->collection('feeds.channels')
?->keys()
?->each(fn (string $feed) => $command->expectsOutputToContain($feed));

$command->assertSuccessful()->run();

config()
?->collection('feeds.channels')
?->keys()
?->each(fn (string $feed) => expect(app($feed)->path())->toBeReadableFile());
})->with([
'',
0,
null,
]);
34 changes: 34 additions & 0 deletions tests/Feature/Console/Generation/SpecifiedTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

use DragonCode\LaravelFeed\Console\Commands\FeedGenerateCommand;
use Workbench\App\Feeds\SitemapFeed;

use function Pest\Laravel\artisan;

test('generate', function () {
$command = artisan(FeedGenerateCommand::class, [
'class' => SitemapFeed::class,
]);

config()
?->collection('feeds.channels')
?->keys()
?->each(
fn (string $feed) => $feed === SitemapFeed::class
? $command->expectsOutputToContain($feed)
: $command->doesntExpectOutputToContain($feed)
);

$command->assertSuccessful()->run();

config()
?->collection('feeds.channels')
?->keys()
?->each(
fn (string $feed) => $feed === SitemapFeed::class
? expect(app($feed)->path())->toBeReadableFile()
: expect(app($feed)->path())->not->toBeReadableFile()
);
});
Loading