diff --git a/testbench.yaml b/testbench.yaml
index 4efff7d..69af6bc 100644
--- a/testbench.yaml
+++ b/testbench.yaml
@@ -9,7 +9,7 @@ migrations:
workbench:
install: false
discovers:
- web: false
+ web: true
api: false
config: true
commands: false
diff --git a/tests/.pest/snapshots/Feature/SitemapTest/export.snap b/tests/.pest/snapshots/Feature/SitemapTest/export.snap
new file mode 100644
index 0000000..715d2d3
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/SitemapTest/export.snap
@@ -0,0 +1,13 @@
+
+
+
+ http://localhost/products/GD-PRDCT-1
+ 2025-08-31T20:00:00+00:00
+ 0.9
+
+
+ http://localhost/products/GD-PRDCT-2
+ 2025-08-30T19:00:00+00:00
+ 0.9
+
+
diff --git a/tests/.pest/snapshots/Feature/YandexTest/export.snap b/tests/.pest/snapshots/Feature/YandexTest/export.snap
new file mode 100644
index 0000000..58988d2
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/YandexTest/export.snap
@@ -0,0 +1,51 @@
+
+
+
+ Laravel
+ Laravel
+ http://localhost
+ Laravel
+ test@example.com
+
+
+
+
+ Домашние майки
+ Велосипедки
+ Ремни
+
+
+
+ http://localhost/products/GD-PRDCT-1
+ GD-PRDCT-1
+ Some 1
+ Some description 1
+ true
+ 100
+ RUR
+ The Best
+
+
+ http://localhost/products/GD-PRDCT-2
+ GD-PRDCT-2
+ Some 2
+ Some description 2
+ true
+ 250
+ RUR
+ The Best
+
+
+ http://localhost/products/GD-PRDCT-3
+ GD-PRDCT-3
+ Some 3
+ Some description 3
+ true
+ 400
+ RUR
+ The Best
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Feature/FullTest.php b/tests/Feature/FullTest.php
index 4fd9723..a41d856 100644
--- a/tests/Feature/FullTest.php
+++ b/tests/Feature/FullTest.php
@@ -5,16 +5,13 @@
use DragonCode\LaravelFeed\Console\Commands\FeedGenerateCommand;
use Workbench\App\Data\NewsFakeData;
use Workbench\App\Feeds\FilledFeed;
-use Workbench\App\Models\News;
use function Pest\Laravel\artisan;
test('export', function (bool $pretty) {
setPrettyXml($pretty);
- News::factory()->count(3)->sequence(
- ...NewsFakeData::toArray()
- )->createMany();
+ createNews(...NewsFakeData::toArray());
$feed = app()->make(FilledFeed::class);
diff --git a/tests/Feature/PartialTest.php b/tests/Feature/PartialTest.php
index d9560c6..908e67e 100644
--- a/tests/Feature/PartialTest.php
+++ b/tests/Feature/PartialTest.php
@@ -5,20 +5,17 @@
use DragonCode\LaravelFeed\Console\Commands\FeedGenerateCommand;
use Workbench\App\Data\NewsFakeData;
use Workbench\App\Feeds\FilledFeed;
-use Workbench\App\Models\News;
use function Pest\Laravel\artisan;
test('export', function (bool $pretty) {
setPrettyXml($pretty);
- News::factory()->count(5)->sequence(fn () => [
+ createNews(static fn () => [
'updated_at' => fake()->dateTimeBetween(endDate: '-1 month'),
- ])->createMany();
+ ]);
- News::factory()->count(3)->sequence(
- ...NewsFakeData::toArray()
- )->createMany();
+ createNews(...NewsFakeData::toArray());
$feed = app()->make(FilledFeed::class);
diff --git a/tests/Feature/SitemapTest.php b/tests/Feature/SitemapTest.php
new file mode 100644
index 0000000..0547e85
--- /dev/null
+++ b/tests/Feature/SitemapTest.php
@@ -0,0 +1,19 @@
+make(SitemapFeed::class);
+
+ artisan(FeedGenerateCommand::class)->run();
+
+ expect($feed->path())->toBeReadableFile();
+ expect(file_get_contents($feed->path()))->toMatchSnapshot();
+});
diff --git a/tests/Feature/YandexTest.php b/tests/Feature/YandexTest.php
new file mode 100644
index 0000000..3e8461f
--- /dev/null
+++ b/tests/Feature/YandexTest.php
@@ -0,0 +1,19 @@
+make(YandexFeed::class);
+
+ artisan(FeedGenerateCommand::class)->run();
+
+ expect($feed->path())->toBeReadableFile();
+ expect(file_get_contents($feed->path()))->toMatchSnapshot();
+});
diff --git a/tests/Helpers/models.php b/tests/Helpers/models.php
new file mode 100644
index 0000000..aec4cb1
--- /dev/null
+++ b/tests/Helpers/models.php
@@ -0,0 +1,21 @@
+count(3)->sequence(
+ ...$sequence
+ )->createMany();
+}
+
+function createProducts(): void
+{
+ Product::factory()->count(3)->sequence(
+ ...ProductFakeData::toArray()
+ )->create();
+}
diff --git a/workbench/app/Data/ProductFakeData.php b/workbench/app/Data/ProductFakeData.php
new file mode 100644
index 0000000..5a3ba43
--- /dev/null
+++ b/workbench/app/Data/ProductFakeData.php
@@ -0,0 +1,73 @@
+ 'GD-PRDCT-1',
+ 'title' => 'Some 1',
+ 'description' => 'Some description 1',
+
+ 'price' => 100,
+ 'quantity' => 5,
+ 'currency' => 'USD',
+
+ 'brand' => 'The Best',
+
+ 'images' => json_encode([
+ 'https://via.placeholder.com/640x480.png/00ff55?text=s1',
+ ]),
+
+ 'created_at' => '2025-08-31 00:00:00',
+ 'updated_at' => '2025-08-31 20:00:00',
+ ],
+ [
+ 'article' => 'GD-PRDCT-2',
+ 'title' => 'Some 2',
+ 'description' => 'Some description 2',
+
+ 'price' => 250,
+ 'quantity' => 20,
+ 'currency' => 'USD',
+
+ 'brand' => 'The Best',
+
+ 'images' => json_encode([
+ 'https://via.placeholder.com/640x480.png/00ff55?text=s20',
+ 'https://via.placeholder.com/640x480.png/00ff55?text=s21',
+ 'https://via.placeholder.com/640x480.png/00ff55?text=s22',
+ ]),
+
+ 'created_at' => '2025-08-30 00:00:00',
+ 'updated_at' => '2025-08-30 19:00:00',
+ ],
+ [
+ 'article' => 'GD-PRDCT-3',
+ 'title' => 'Some 3',
+ 'description' => 'Some description 3',
+
+ 'price' => 400,
+ 'quantity' => 0,
+ 'currency' => 'USD',
+
+ 'brand' => 'The Best',
+
+ 'images' => json_encode([
+ 'https://via.placeholder.com/640x480.png/00ff55?text=s30',
+ 'https://via.placeholder.com/640x480.png/00ff55?text=s31',
+ ]),
+
+ 'created_at' => '2025-08-29 00:00:00',
+ 'updated_at' => '2025-08-29 18:00:00',
+ ],
+ ];
+ }
+}
diff --git a/workbench/app/Feeds/Items/SitemapFeedItem.php b/workbench/app/Feeds/Items/SitemapFeedItem.php
new file mode 100644
index 0000000..7443cd4
--- /dev/null
+++ b/workbench/app/Feeds/Items/SitemapFeedItem.php
@@ -0,0 +1,27 @@
+ route('products.show', $this->model->article),
+
+ 'lastmod' => $this->model->updated_at->toIso8601String(),
+
+ 'priority' => 0.9,
+ ];
+ }
+}
diff --git a/workbench/app/Feeds/Items/YandexFeedItem.php b/workbench/app/Feeds/Items/YandexFeedItem.php
new file mode 100644
index 0000000..2bcc15e
--- /dev/null
+++ b/workbench/app/Feeds/Items/YandexFeedItem.php
@@ -0,0 +1,46 @@
+ $this->model->id,
+
+ 'available' => ! empty($this->model->quantity) ? 'true' : 'false',
+
+ 'type' => 'vendor.model',
+ ];
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'url' => route('products.show', $this->model->article),
+
+ 'barcode' => $this->model->article,
+ 'name' => $this->model->title,
+ 'description' => $this->model->description,
+
+ 'delivery' => 'true',
+ 'price' => $this->model->price,
+
+ 'currencyId' => 'RUR',
+ 'vendor' => $this->model->brand,
+
+ // 'picture' => $this->model->images,
+ ];
+ }
+}
diff --git a/workbench/app/Feeds/SitemapFeed.php b/workbench/app/Feeds/SitemapFeed.php
new file mode 100644
index 0000000..486403c
--- /dev/null
+++ b/workbench/app/Feeds/SitemapFeed.php
@@ -0,0 +1,49 @@
+ 'http://www.sitemaps.org/schemas/sitemap/0.9',
+ 'xmlns:xhtml' => 'http://www.w3.org/1999/xhtml',
+ 'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1',
+ 'xmlns:video' => 'http://www.google.com/schemas/sitemap-video/1.1',
+ 'xmlns:news' => 'http://www.google.com/schemas/sitemap-news/0.9',
+ ];
+
+ public function builder(): Builder
+ {
+ return Product::query()->where('quantity', '>', 0);
+ }
+
+ public function root(): ElementData
+ {
+ return new ElementData(
+ $this->name,
+ $this->attributes
+ );
+ }
+
+ public function filename(): string
+ {
+ return 'sitemaps/products.xml';
+ }
+
+ public function item(Model $model): FeedItem
+ {
+ return new SitemapFeedItem($model);
+ }
+}
diff --git a/workbench/app/Feeds/YandexFeed.php b/workbench/app/Feeds/YandexFeed.php
new file mode 100644
index 0000000..4860433
--- /dev/null
+++ b/workbench/app/Feeds/YandexFeed.php
@@ -0,0 +1,67 @@
+
+
+
+ $name
+ $name
+ $url
+ $name
+ $email
+
+
+
+
+ Домашние майки
+ Велосипедки
+ Ремни
+
+ XML;
+ }
+
+ public function footer(): string
+ {
+ return "\n\n";
+ }
+
+ public function filename(): string
+ {
+ return 'yandex.xml';
+ }
+
+ public function item(Model $model): FeedItem
+ {
+ return new Items\YandexFeedItem($model);
+ }
+}
diff --git a/workbench/app/Models/News.php b/workbench/app/Models/News.php
index 229f023..f6e36b5 100644
--- a/workbench/app/Models/News.php
+++ b/workbench/app/Models/News.php
@@ -17,5 +17,6 @@ class News extends Model
protected $fillable = [
'title',
'content',
+ '',
];
}
diff --git a/workbench/app/Models/Product.php b/workbench/app/Models/Product.php
new file mode 100644
index 0000000..b8bb4c2
--- /dev/null
+++ b/workbench/app/Models/Product.php
@@ -0,0 +1,30 @@
+ 'test@example.com',
+];
diff --git a/workbench/config/feeds.php b/workbench/config/feeds.php
index 0311872..69b0c5a 100644
--- a/workbench/config/feeds.php
+++ b/workbench/config/feeds.php
@@ -4,13 +4,14 @@
use Workbench\App\Feeds\EmptyFeed;
use Workbench\App\Feeds\FilledFeed;
+use Workbench\App\Feeds\SitemapFeed;
+use Workbench\App\Feeds\YandexFeed;
return [
'channels' => [
- // App\Feeds\FooFeed::class => (bool) env('FEED_FOO_ENABLED', true),
- // App\Feeds\BarFeed::class => (bool) env('FEED_BAR_ENABLED', true),
- // App\Feeds\BazFeed::class => (bool) env('FEED_BAZ_ENABLED', false),
- EmptyFeed::class => true,
- FilledFeed::class => true,
+ EmptyFeed::class => true,
+ FilledFeed::class => true,
+ SitemapFeed::class => true,
+ YandexFeed::class => true,
],
];
diff --git a/workbench/database/factories/ProductFactory.php b/workbench/database/factories/ProductFactory.php
new file mode 100644
index 0000000..058714a
--- /dev/null
+++ b/workbench/database/factories/ProductFactory.php
@@ -0,0 +1,34 @@
+ Str::of(fake()->unique()->password(4, 8))->upper()->prepend('GD-')->toString(),
+
+ 'title' => fake()->unique()->words(4, true),
+ 'description' => fake()->text(),
+ 'brand' => fake()->word(),
+
+ 'price' => fake()->numberBetween(100, 1000),
+ 'quantity' => fake()->numberBetween(0, 10),
+ 'currency' => fake()->currencyCode(),
+
+ 'images' => json_encode([
+ fake()->imageUrl(),
+ fake()->imageUrl(),
+ fake()->imageUrl(),
+ ]),
+ ];
+ }
+}
diff --git a/workbench/database/migrations/2025_08_30_235243_create_products_table.php b/workbench/database/migrations/2025_08_30_235243_create_products_table.php
new file mode 100644
index 0000000..c9db018
--- /dev/null
+++ b/workbench/database/migrations/2025_08_30_235243_create_products_table.php
@@ -0,0 +1,30 @@
+id();
+
+ $table->string('article');
+
+ $table->string('title');
+ $table->text('description');
+ $table->string('brand');
+
+ $table->integer('price');
+ $table->integer('quantity');
+ $table->string('currency');
+
+ $table->json('images');
+
+ $table->timestamps();
+ });
+ }
+};
diff --git a/workbench/routes/web.php b/workbench/routes/web.php
new file mode 100644
index 0000000..6d0f1df
--- /dev/null
+++ b/workbench/routes/web.php
@@ -0,0 +1,7 @@
+name('products.show')
+ ->get('products/{product}', static fn (string $product) => $product);