diff --git a/docs/snippets/advanced-directive-array-1.xml b/docs/snippets/advanced-directive-array-1.xml
new file mode 100644
index 0000000..b250e82
--- /dev/null
+++ b/docs/snippets/advanced-directive-array-1.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ Cedrick Stoltenberg
+ https://via.placeholder.com/640x480.png/006611?text=sapiente
+ https://via.placeholder.com/640x480.png/0099aa?text=voluptates
+ https://via.placeholder.com/640x480.png/008833?text=facere
+ https://via.placeholder.com/640x480.png/005500?text=illo
+
+
+ Cale Murazik
+ https://via.placeholder.com/640x480.png/00bb22?text=nisi
+ https://via.placeholder.com/640x480.png/0022aa?text=illo
+ https://via.placeholder.com/640x480.png/0066ee?text=omnis
+ https://via.placeholder.com/640x480.png/0099bb?text=maxime
+
+
+
diff --git a/docs/snippets/advanced-directive-array.xml b/docs/snippets/advanced-directive-array.xml
index 87fed3b..ed42be1 100644
--- a/docs/snippets/advanced-directive-array.xml
+++ b/docs/snippets/advanced-directive-array.xml
@@ -2,18 +2,18 @@
- Dr. Mozelle Hand V
- https://via.placeholder.com/640x480.png/00ddee?text=iste
- https://via.placeholder.com/640x480.png/00aaaa?text=dolorem
- https://via.placeholder.com/640x480.png/002288?text=quasi
- https://via.placeholder.com/640x480.png/00aa66?text=explicabo
+ Cyril Kunze
+ https://via.placeholder.com/640x480.png/0044bb?text=sit
+ https://via.placeholder.com/640x480.png/0055ee?text=accusantium
+ https://via.placeholder.com/640x480.png/00dd22?text=qui
+ https://via.placeholder.com/640x480.png/0011dd?text=molestias
- Lori Gislason
- https://via.placeholder.com/640x480.png/001144?text=nostrum
- https://via.placeholder.com/640x480.png/002200?text=numquam
- https://via.placeholder.com/640x480.png/005544?text=consequatur
- https://via.placeholder.com/640x480.png/0011dd?text=maiores
+ Norberto Cassin I
+ https://via.placeholder.com/640x480.png/006622?text=in
+ https://via.placeholder.com/640x480.png/009988?text=qui
+ https://via.placeholder.com/640x480.png/003300?text=ipsam
+ https://via.placeholder.com/640x480.png/00dd88?text=similique
diff --git a/docs/snippets/advanced-directive-attributes-1.xml b/docs/snippets/advanced-directive-attributes-1.xml
new file mode 100644
index 0000000..9854d56
--- /dev/null
+++ b/docs/snippets/advanced-directive-attributes-1.xml
@@ -0,0 +1,16 @@
+
+
+
+
+https://example.com
+
+
+ Mr. Cameron Nader
+
+
+
+ Prof. Ernestine Murphy MD
+
+
+
+
diff --git a/docs/snippets/advanced-directive-attributes.xml b/docs/snippets/advanced-directive-attributes.xml
index ff4fc6c..6f8f833 100644
--- a/docs/snippets/advanced-directive-attributes.xml
+++ b/docs/snippets/advanced-directive-attributes.xml
@@ -1,16 +1,16 @@
-
+
https://example.com
- Doyle Donnelly
-
+ Ada Klocko
+
- Sally Pagac Sr.
-
+ Garrett Yost
+
diff --git a/docs/snippets/advanced-directive-cdata-1.xml b/docs/snippets/advanced-directive-cdata-1.xml
new file mode 100644
index 0000000..9b52be0
--- /dev/null
+++ b/docs/snippets/advanced-directive-cdata-1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ Oral Lang]]>
+ margie.bartell@example.net
+
+
+ Prof. Kellen Schroeder DDS]]>
+ awindler@example.net
+
+
+
diff --git a/docs/snippets/advanced-directive-cdata.xml b/docs/snippets/advanced-directive-cdata.xml
index edf02ce..4aa67c2 100644
--- a/docs/snippets/advanced-directive-cdata.xml
+++ b/docs/snippets/advanced-directive-cdata.xml
@@ -2,12 +2,12 @@
- Sallie Price]]>
- ssporer@example.net
+ Thalia Baumbach]]>
+ fermin.crooks@example.com
- Dr. Nedra Weimann]]>
- beau.feest@example.net
+ Pauline Dicki]]>
+ bkuhic@example.org
diff --git a/docs/snippets/advanced-directive-mixed-1.xml b/docs/snippets/advanced-directive-mixed-1.xml
new file mode 100644
index 0000000..9e466c0
--- /dev/null
+++ b/docs/snippets/advanced-directive-mixed-1.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ Rod Mraz III
+
+ Foo
+ winona.fay@example.com
+
+
+
+ Chelsey Wintheiser
+
+ Foo
+ else71@example.com
+
+
+
+
diff --git a/docs/snippets/advanced-directive-mixed.xml b/docs/snippets/advanced-directive-mixed.xml
index 7b97f98..f50f87d 100644
--- a/docs/snippets/advanced-directive-mixed.xml
+++ b/docs/snippets/advanced-directive-mixed.xml
@@ -2,17 +2,17 @@
- Bradley Krajcik
+ Dewitt Brown
Foo
- alexander73@example.net
+ lakin.augusta@example.com
- Jamil Hilpert PhD
+ Bessie DuBuque
Foo
- vwalsh@example.net
+ ali52@example.org
diff --git a/docs/snippets/advanced-directive-value-1.xml b/docs/snippets/advanced-directive-value-1.xml
new file mode 100644
index 0000000..272cd5c
--- /dev/null
+++ b/docs/snippets/advanced-directive-value-1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ Onie Hahn
+ mozelle.eichmann@example.com
+
+
+ Heaven Hauck
+ grady.rocky@example.org
+
+
+
diff --git a/docs/snippets/advanced-directive-value.xml b/docs/snippets/advanced-directive-value.xml
index eec4b5d..8950115 100644
--- a/docs/snippets/advanced-directive-value.xml
+++ b/docs/snippets/advanced-directive-value.xml
@@ -2,12 +2,12 @@
- Mr. Cyril Douglas
- grant.kassulke@example.com
+ Johnson Schoen
+ hamill.myrtie@example.com
- Prof. Camille Veum DVM
- hfranecki@example.com
+ Morton Abernathy
+ randall.corkery@example.com
diff --git a/docs/snippets/advanced-element-attribute-1.xml b/docs/snippets/advanced-element-attribute-1.xml
new file mode 100644
index 0000000..953531a
--- /dev/null
+++ b/docs/snippets/advanced-element-attribute-1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ 1
+ Elody Durgan
+
+
+ 2
+ Cierra Bauch
+
+
+
diff --git a/docs/snippets/advanced-element-attribute.xml b/docs/snippets/advanced-element-attribute.xml
index e1c5321..098d7a4 100644
--- a/docs/snippets/advanced-element-attribute.xml
+++ b/docs/snippets/advanced-element-attribute.xml
@@ -1,13 +1,13 @@
-
+
1
- Efren Prosacco
+ Mrs. Janet Kuhn
-
+
2
- Elwyn Kuphal DDS
+ Kristin Gleason
diff --git a/docs/snippets/advanced-element-header-footer-1.xml b/docs/snippets/advanced-element-header-footer-1.xml
new file mode 100644
index 0000000..d48e863
--- /dev/null
+++ b/docs/snippets/advanced-element-header-footer-1.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ 1
+ Paxton Bradtke
+
+
+ 2
+ Riley Grant III
+
+
+
+
+This is a custom footer element
\ No newline at end of file
diff --git a/docs/snippets/advanced-element-header-footer.xml b/docs/snippets/advanced-element-header-footer.xml
index 50010f8..8d2065a 100644
--- a/docs/snippets/advanced-element-header-footer.xml
+++ b/docs/snippets/advanced-element-header-footer.xml
@@ -3,11 +3,11 @@
1
- Arnaldo Mohr
+ Dr. Jordyn Hintz
2
- Dillan Beahan
+ Garland Mraz IV
diff --git a/docs/snippets/advanced-element-info-1.xml b/docs/snippets/advanced-element-info-1.xml
new file mode 100644
index 0000000..bb7e763
--- /dev/null
+++ b/docs/snippets/advanced-element-info-1.xml
@@ -0,0 +1,16 @@
+
+
+
+Laravel
+https://example.com
+
+
+ 1
+ Mr. Odell Keeling MD
+
+
+ 2
+ Cesar Swaniawski
+
+
+
diff --git a/docs/snippets/advanced-element-info-before-false-1.xml b/docs/snippets/advanced-element-info-before-false-1.xml
new file mode 100644
index 0000000..4270321
--- /dev/null
+++ b/docs/snippets/advanced-element-info-before-false-1.xml
@@ -0,0 +1,16 @@
+
+Laravel
+https://example.com
+
+
+
+
+ 1
+ Mr. Grayce Borer IV
+
+
+ 2
+ Melissa Windler PhD
+
+
+
diff --git a/docs/snippets/advanced-element-info-before-false.xml b/docs/snippets/advanced-element-info-before-false.xml
index 1ce5b52..11e53cb 100644
--- a/docs/snippets/advanced-element-info-before-false.xml
+++ b/docs/snippets/advanced-element-info-before-false.xml
@@ -6,11 +6,11 @@
1
- Hilton Rath
+ Willy Wilkinson
2
- Prof. Juanita Oberbrunner
+ Delphine Mohr
diff --git a/docs/snippets/advanced-element-info.xml b/docs/snippets/advanced-element-info.xml
index 091c75b..4a9b800 100644
--- a/docs/snippets/advanced-element-info.xml
+++ b/docs/snippets/advanced-element-info.xml
@@ -6,11 +6,11 @@
1
- Sigurd Mueller
+ Neha Pfannerstill V
2
- Kiera Hansen
+ Quincy Walter
diff --git a/docs/snippets/advanced-element-root-1.xml b/docs/snippets/advanced-element-root-1.xml
new file mode 100644
index 0000000..17d5472
--- /dev/null
+++ b/docs/snippets/advanced-element-root-1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ 1
+ Trenton Larson
+
+
+ 2
+ Xzavier Spinka Jr.
+
+
+
diff --git a/docs/snippets/advanced-element-root.xml b/docs/snippets/advanced-element-root.xml
index 15edaaf..780dfb6 100644
--- a/docs/snippets/advanced-element-root.xml
+++ b/docs/snippets/advanced-element-root.xml
@@ -3,11 +3,11 @@
1
- Darby Davis MD
+ Verna Goldner DVM
2
- Dr. Mattie Rippin
+ Dr. Samara Ziemann II
diff --git a/docs/snippets/receipt-instagram-feed-1.xml b/docs/snippets/receipt-instagram-feed-1.xml
new file mode 100644
index 0000000..cea1bd6
--- /dev/null
+++ b/docs/snippets/receipt-instagram-feed-1.xml
@@ -0,0 +1,59 @@
+
+
+ Laravel
+ https://example.com
+
+
+-
+ 1
+
+
+ https://example.com/products/in-illum-dolores-officiis-ea
+ https://via.placeholder.com/640x480.png/008877?text=repudiandae
+ https://via.placeholder.com/640x480.png/008877?text=repudiandae
+ The Best
+ new
+ in stock
+ 100
+ 100
+ 12345
+ active
+ 123
+ 456
+ Some foo
+ Some bar
+ Some baz
+ a
+ b
+ c
+
+-
+ 2
+
+
+ https://example.com/products/ut-adipisci-consectetur-non-et
+ https://via.placeholder.com/640x480.png/009966?text=beatae
+ https://via.placeholder.com/640x480.png/009966?text=beatae
+ https://via.placeholder.com/640x480.png/000011?text=deleniti
+ https://via.placeholder.com/640x480.png/009999?text=voluptates
+ The Best
+ new
+ in stock
+ 250
+ 250
+ 12345
+ active
+ 123
+ 456
+ Some foo
+ Some bar
+ Some baz
+ a
+ b
+ c
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/snippets/receipt-instagram-feed.xml b/docs/snippets/receipt-instagram-feed.xml
index 680585c..b8dfa84 100644
--- a/docs/snippets/receipt-instagram-feed.xml
+++ b/docs/snippets/receipt-instagram-feed.xml
@@ -8,7 +8,7 @@
1
- https://example.com/products/ratione-minima-officia-adipisci-ratione-consectetur
+ https://example.com/products/qui-ut-ratione-sed-et-ratione
https://via.placeholder.com/640x480.png/008877?text=repudiandae
https://via.placeholder.com/640x480.png/008877?text=repudiandae
The Best
@@ -31,7 +31,7 @@
2
- https://example.com/products/accusamus-animi-animi-earum-quis
+ https://example.com/products/sunt-magnam-dolores-a-omnis
https://via.placeholder.com/640x480.png/009966?text=beatae
https://via.placeholder.com/640x480.png/009966?text=beatae
https://via.placeholder.com/640x480.png/000011?text=deleniti
diff --git a/docs/snippets/receipt-rss-feed-1.xml b/docs/snippets/receipt-rss-feed-1.xml
new file mode 100644
index 0000000..8836509
--- /dev/null
+++ b/docs/snippets/receipt-rss-feed-1.xml
@@ -0,0 +1,34 @@
+
+
+
+
+-
+ Some 1
+ https://example.com/news/some-1
+ 1
+
+ Some category 1
+ Wed, 03 Sep 2025 18:52:32 +0000
+ bar
+
+-
+ Some 2
+ https://example.com/news/some-2
+ 2
+
+ Some category 2
+ Wed, 03 Sep 2025 13:19:41 +0000
+ bar
+
+-
+ Some 3
+ https://example.com/news/some-3
+ 3
+
+ Some category 3
+ Thu, 04 Sep 2025 03:28:15 +0000
+ bar
+
+
+
+
\ No newline at end of file
diff --git a/docs/snippets/receipt-rss-feed.xml b/docs/snippets/receipt-rss-feed.xml
index 0890dff..2f3e4e5 100644
--- a/docs/snippets/receipt-rss-feed.xml
+++ b/docs/snippets/receipt-rss-feed.xml
@@ -8,7 +8,7 @@
1
Some category 1
- Wed, 03 Sep 2025 13:26:59 +0000
+ Wed, 03 Sep 2025 11:09:19 +0000
bar
-
@@ -17,7 +17,16 @@
2
Some category 2
- Wed, 03 Sep 2025 14:17:03 +0000
+ Wed, 03 Sep 2025 22:21:20 +0000
+ bar
+
+-
+ Some 3
+ https://example.com/news/some-3
+ 3
+
+ Some category 3
+ Wed, 03 Sep 2025 23:36:51 +0000
bar
diff --git a/docs/snippets/receipt-sitemap-feed-1.xml b/docs/snippets/receipt-sitemap-feed-1.xml
new file mode 100644
index 0000000..62a4bee
--- /dev/null
+++ b/docs/snippets/receipt-sitemap-feed-1.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ https://example.com/products/reiciendis-animi-ut-voluptatem-quaerat-odit-suscipit
+ 2025-08-31T20:00:00+00:00
+ 0.9
+
+
+ https://example.com/products/consequatur-beatae-non-sint-totam-voluptatem
+ 2025-08-30T19:00:00+00:00
+ 0.9
+
+
+
diff --git a/docs/snippets/receipt-sitemap-feed.xml b/docs/snippets/receipt-sitemap-feed.xml
index 2a28a0f..491d3a4 100644
--- a/docs/snippets/receipt-sitemap-feed.xml
+++ b/docs/snippets/receipt-sitemap-feed.xml
@@ -2,12 +2,12 @@
- https://example.com/products/dolor-culpa-reiciendis-illo-magnam-nisi-quisquam-labore-aspernatur
+ https://example.com/products/voluptatem-dolores-iure-sint-autem-dolores-quo-itaque
2025-08-31T20:00:00+00:00
0.9
- https://example.com/products/quas-tempora-quia-animi-veniam-tempore-et-at
+ https://example.com/products/veritatis-voluptates-officiis-aperiam-voluptas-vel-non
2025-08-30T19:00:00+00:00
0.9
diff --git a/docs/snippets/receipt-yandex-feed-1.xml b/docs/snippets/receipt-yandex-feed-1.xml
new file mode 100644
index 0000000..f3db52c
--- /dev/null
+++ b/docs/snippets/receipt-yandex-feed-1.xml
@@ -0,0 +1,47 @@
+
+
+
+My App
+My Company
+My Platform
+https://example.com
+feeds@example.com
+
+
+
+
+ Foo
+ Bar
+
+bar
+
+
+
+
+ https://example.com/products/qui-sunt-nihil-placeat-numquam-rerum-laboriosam-dolores-aliquid
+ GD-PRDCT-1
+ Some 1
+ Some description 1
+ 100
+ RUR
+ The Best
+ https://via.placeholder.com/640x480.png/008877?text=repudiandae
+ bar
+
+
+ https://example.com/products/laudantium-perferendis-error-ad-explicabo-eos-aspernatur
+ GD-PRDCT-2
+ Some 2
+ Some description 2
+ 250
+ RUR
+ The Best
+ https://via.placeholder.com/640x480.png/009966?text=beatae
+ https://via.placeholder.com/640x480.png/000011?text=deleniti
+ https://via.placeholder.com/640x480.png/009999?text=voluptates
+ bar
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/snippets/receipt-yandex-feed.xml b/docs/snippets/receipt-yandex-feed.xml
index 0f47c65..d66edda 100644
--- a/docs/snippets/receipt-yandex-feed.xml
+++ b/docs/snippets/receipt-yandex-feed.xml
@@ -18,7 +18,7 @@
- https://example.com/products/ipsa-maiores-odit-dicta-temporibus-et-rerum
+ https://example.com/products/a-doloremque-et-nihil
GD-PRDCT-1
Some 1
Some description 1
@@ -29,7 +29,7 @@
bar
- https://example.com/products/quo-rerum-qui-eos-eius-quaerat-voluptatem-et
+ https://example.com/products/pariatur-molestiae-vitae-odit-qui
GD-PRDCT-2
Some 2
Some description 2
diff --git a/src/Feeds/Feed.php b/src/Feeds/Feed.php
index 7a0e465..69ebb0a 100644
--- a/src/Feeds/Feed.php
+++ b/src/Feeds/Feed.php
@@ -46,6 +46,16 @@ public function chunkSize(): int
return 1000;
}
+ public function perFile(): int
+ {
+ return 0;
+ }
+
+ public function maxFiles(): int
+ {
+ return 0;
+ }
+
public function header(): string
{
return match ($this->format()) {
@@ -87,10 +97,26 @@ public function filename(): string
->toString();
}
- public function path(): string
+ public function path(int|string $suffix = ''): string
{
+ if (empty($suffix)) {
+ return $this->storage()->path(
+ $this->filename()
+ );
+ }
+
+ $filename = $this->filename();
+
+ $directory = pathinfo($filename, PATHINFO_DIRNAME);
+ $basename = pathinfo($filename, PATHINFO_FILENAME);
+ $extension = pathinfo($filename, PATHINFO_EXTENSION);
+
+ if ($suffix) {
+ $suffix = '-' . $suffix;
+ }
+
return $this->storage()->path(
- $this->filename()
+ "$directory/$basename$suffix.$extension"
);
}
diff --git a/src/Services/ExportService.php b/src/Services/ExportService.php
new file mode 100644
index 0000000..670cb57
--- /dev/null
+++ b/src/Services/ExportService.php
@@ -0,0 +1,199 @@
+perFile = $this->perFile($this->feed);
+ $this->maxFiles = $this->maxFiles($this->feed);
+ $this->total = $this->total();
+
+ $this->progressBar = $this->createProgressBar(
+ $this->total
+ );
+ }
+
+ public function chunk(int $chunk): static
+ {
+ $this->chunk = $chunk;
+
+ return $this;
+ }
+
+ public function file(Closure $create, Closure $close): static
+ {
+ $this->createFile = $create;
+ $this->closeFile = $close;
+
+ return $this;
+ }
+
+ public function item(Closure $callback): static
+ {
+ $this->item = $callback;
+
+ return $this;
+ }
+
+ public function export(): void
+ {
+ $this->feed->builder()
+ ->lazyById($this->chunk)
+ ->each(function (Model $model) {
+ $this->line++;
+ $this->records++;
+ $this->total--;
+
+ $this->content[] = value($this->item, $model, $this->isLastItem());
+
+ $this->store();
+
+ if ($this->total <= 0) {
+ return false;
+ }
+
+ if ($this->maxFiles && $this->file >= $this->maxFiles) {
+ return false;
+ }
+ });
+
+ $this->store(true);
+
+ $this->progressBar?->finish();
+ }
+
+ protected function store(bool $force = false): void
+ {
+ if ($force || $this->records >= $this->perFile || $this->line >= $this->chunk) {
+ $this->line = 0;
+
+ $this->append();
+
+ $this->content = [];
+ }
+
+ if ($force || $this->records >= $this->perFile) {
+ $this->records = 0;
+
+ $this->releaseFile();
+ }
+ }
+
+ protected function isLastItem(): bool
+ {
+ return $this->line >= min($this->perFile, $this->total);
+ }
+
+ protected function getFile() // @pest-ignore-type
+ {
+ if (! empty($this->resource)) {
+ return $this->resource;
+ }
+
+ return $this->resource ??= value($this->createFile);
+ }
+
+ protected function releaseFile(): void
+ {
+ if ($this->resource === null) {
+ return;
+ }
+
+ $index = $this->maxFiles ? $this->file : 0;
+
+ value($this->closeFile, $this->resource, $index);
+
+ $this->resource = null;
+
+ $this->file++;
+ }
+
+ protected function append(): void
+ {
+ if (blank($this->content)) {
+ return;
+ }
+
+ $this->filesystem->append($this->getFile(), implode(PHP_EOL, $this->content), $this->feed->path());
+ }
+
+ protected function perFile(Feed $feed): int
+ {
+ if ($count = max($feed->perFile(), 0)) {
+ return $count;
+ }
+
+ return $this->modelCount();
+ }
+
+ protected function maxFiles(Feed $feed): int
+ {
+ return max($feed->maxFiles(), 0);
+ }
+
+ protected function total(): int
+ {
+ if ($this->maxFiles <= 0) {
+ return $this->modelCount();
+ }
+
+ return $this->perFile * $this->maxFiles;
+ }
+
+ protected function modelCount(): int
+ {
+ return $this->modelCount ??= $this->feed->builder()->count();
+ }
+
+ protected function createProgressBar(int $total): ?ProgressBar
+ {
+ return $this->output?->createProgressBar($total);
+ }
+}
diff --git a/src/Services/FilesystemService.php b/src/Services/FilesystemService.php
index 37ea3b0..3ac08dd 100644
--- a/src/Services/FilesystemService.php
+++ b/src/Services/FilesystemService.php
@@ -16,10 +16,7 @@
use function dirname;
use function fclose;
-use function fflush;
-use function flock;
use function fopen;
-use function ftruncate;
use function fwrite;
use function is_resource;
use function microtime;
@@ -47,8 +44,6 @@ public function createDraft(string $filename) // @pest-ignore-type
// @codeCoverageIgnoreEnd
}
- $this->lock($resource);
-
return $resource;
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
@@ -77,7 +72,6 @@ public function release($resource, string $path): void // @pest-ignore-type
try {
$temp = $this->getMetaPath($resource);
- $this->unlock($resource);
$this->close($resource);
if ($this->file->exists($path)) {
@@ -93,7 +87,7 @@ public function release($resource, string $path): void // @pest-ignore-type
$this->cleanTemporaryDirectory($temp);
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
- throw new CloseFeedException($temp, $e);
+ throw new CloseFeedException($path, $e);
}
// @codeCoverageIgnoreEnd
}
@@ -145,27 +139,4 @@ protected function getMetaPath($file): string // @pest-ignore-type
return $meta['uri'] ?? throw new ResourceMetaException;
}
-
- /**
- * @param resource $resource
- */
- protected function lock($resource): void // @pest-ignore-type
- {
- if (! flock($resource, LOCK_EX)) {
- // @codeCoverageIgnoreStart
- throw new RuntimeException('Resource lock error. The resource may be in use by another process.');
- // @codeCoverageIgnoreEnd
- }
-
- ftruncate($resource, 0);
- }
-
- /**
- * @param resource $resource
- */
- protected function unlock($resource): void // @pest-ignore-type
- {
- fflush($resource);
- flock($resource, LOCK_UN);
- }
}
diff --git a/src/Services/GeneratorService.php b/src/Services/GeneratorService.php
index 7243947..cb46b9b 100644
--- a/src/Services/GeneratorService.php
+++ b/src/Services/GeneratorService.php
@@ -4,6 +4,7 @@
namespace DragonCode\LaravelFeed\Services;
+use Closure;
use DragonCode\LaravelFeed\Converters\Converter;
use DragonCode\LaravelFeed\Events\FeedFinishedEvent;
use DragonCode\LaravelFeed\Events\FeedStartingEvent;
@@ -12,14 +13,12 @@
use DragonCode\LaravelFeed\Helpers\ConverterHelper;
use DragonCode\LaravelFeed\Queries\FeedQuery;
use Illuminate\Console\OutputStyle;
-use Illuminate\Database\Eloquent\Collection;
-use Symfony\Component\Console\Helper\ProgressBar;
+use Illuminate\Database\Eloquent\Model;
use Throwable;
use function blank;
use function event;
use function get_class;
-use function implode;
class GeneratorService
{
@@ -34,18 +33,7 @@ public function feed(Feed $feed, ?OutputStyle $output = null): void
try {
$this->started($feed);
- $file = $this->createDraft(
- $feed->filename()
- );
-
- $this->performHeader($file, $feed);
- $this->performRoot($file, $feed, true);
- $this->performInfo($file, $feed);
- $this->performRoot($file, $feed, false);
- $this->performItem($file, $feed, $output);
- $this->performFooter($file, $feed);
-
- $this->release($file, $feed->path());
+ $this->export($feed, $output, $this->filesystem);
$this->setLastActivity($feed);
@@ -55,37 +43,42 @@ public function feed(Feed $feed, ?OutputStyle $output = null): void
}
}
- protected function performItem($file, Feed $feed, ?OutputStyle $output): void // @pest-ignore-type
+ protected function export(Feed $feed, ?OutputStyle $output, FilesystemService $filesystem): void
{
- $count = $feed->builder()->count();
-
- // @codeCoverageIgnoreStart
- $bar = $this->progressBar($count, $output);
- // @codeCoverageIgnoreEnd
-
- $progress = $count;
+ (new ExportService($feed, $filesystem, $output))
+ ->file(
+ create: $this->createFile($feed),
+ close : $this->closeFile($feed)
+ )
+ ->item(fn (Model $model, bool $last) => $this->converter($feed)->item(
+ item : $feed->item($model),
+ isLast: $last
+ ))
+ ->chunk($feed->chunkSize())
+ ->export();
+ }
- $feed->builder()->chunkById(
- $feed->chunkSize(),
- function (Collection $models) use ($file, $feed, $bar, &$progress) {
- $content = [];
+ protected function createFile(Feed $feed): Closure
+ {
+ return function () use ($feed) {
+ $file = $this->createDraft($feed->filename());
- foreach ($models as $model) {
- $content[] = $this->converter($feed)->item(
- item: $feed->item($model),
- isLast: $progress <= 1
- );
+ $this->performHeader($file, $feed);
+ $this->performRoot($file, $feed, true);
+ $this->performInfo($file, $feed);
+ $this->performRoot($file, $feed, false);
- $bar?->advance();
- $progress--;
- }
+ return $file;
+ };
+ }
- $this->append($file, implode(PHP_EOL, $content), $feed->path());
- }
- );
+ protected function closeFile(Feed $feed): Closure
+ {
+ return function ($file, int $index) use ($feed) {
+ $this->performFooter($file, $feed);
- $bar?->finish();
- $output?->newLine();
+ $this->release($file, $feed->path($index));
+ };
}
protected function performHeader($file, Feed $feed): void // @pest-ignore-type
@@ -161,11 +154,6 @@ protected function converter(Feed $feed): Converter
);
}
- protected function progressBar(int $count, ?OutputStyle $output): ?ProgressBar
- {
- return $output?->createProgressBar($count);
- }
-
protected function started(Feed $feed): void
{
event(new FeedStartingEvent(get_class($feed)));
diff --git a/tests/.pest/snapshots/Feature/Feeds/Defaults/EmptyTest/export_with_data_set____false__.snap b/tests/.pest/snapshots/Feature/Feeds/Defaults/EmptyTest/export_with_data_set____false__.snap
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/.pest/snapshots/Feature/Feeds/Defaults/EmptyTest/export_with_data_set____true__.snap b/tests/.pest/snapshots/Feature/Feeds/Defaults/EmptyTest/export_with_data_set____true__.snap
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/DefaultTest/export_with_data_set____true__.snap b/tests/.pest/snapshots/Feature/Feeds/Formats/Json/DefaultTest/export_with_data_set____true__.snap
deleted file mode 100644
index 8e62410..0000000
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/DefaultTest/export_with_data_set____true__.snap
+++ /dev/null
@@ -1,26 +0,0 @@
-[
-{
- "id": 1,
- "title": "Some 1",
- "content": "Some content 1",
- "category": "Some category 1",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 2,
- "title": "Some 2",
- "content": "Some content 2",
- "category": "Some category 2",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 3,
- "title": "Some 3",
- "content": "Some content 3",
- "category": "Some category 3",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-}
-]
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/InfoTest/export_with_data_set____false__.snap b/tests/.pest/snapshots/Feature/Feeds/Formats/Json/InfoTest/export_with_data_set____false__.snap
deleted file mode 100644
index 0194d9c..0000000
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/InfoTest/export_with_data_set____false__.snap
+++ /dev/null
@@ -1,6 +0,0 @@
-[
-{"name":"Laravel","company":"Laravel","platform":"Laravel","url":"https://example.com","email":"test@example.com","currencies":{"@currency":[{"@attributes":{"id":"RUR","rate":"1"}}]},"categories":{"@category":[{"@attributes":{"id":41},"@value":"Домашние майки"},{"@attributes":{"id":539},"@value":"Велосипедки"},{"@attributes":{"id":44},"@value":"Ремни"}]}},
-{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
-]
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/InfoTest/export_with_data_set____true__.snap b/tests/.pest/snapshots/Feature/Feeds/Formats/Json/InfoTest/export_with_data_set____true__.snap
deleted file mode 100644
index f7359a7..0000000
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/InfoTest/export_with_data_set____true__.snap
+++ /dev/null
@@ -1,65 +0,0 @@
-[
-{
- "name": "Laravel",
- "company": "Laravel",
- "platform": "Laravel",
- "url": "https://example.com",
- "email": "test@example.com",
- "currencies": {
- "@currency": [
- {
- "@attributes": {
- "id": "RUR",
- "rate": "1"
- }
- }
- ]
- },
- "categories": {
- "@category": [
- {
- "@attributes": {
- "id": 41
- },
- "@value": "Домашние майки"
- },
- {
- "@attributes": {
- "id": 539
- },
- "@value": "Велосипедки"
- },
- {
- "@attributes": {
- "id": 44
- },
- "@value": "Ремни"
- }
- ]
- }
-},
-{
- "id": 1,
- "title": "Some 1",
- "content": "Some content 1",
- "category": "Some category 1",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 2,
- "title": "Some 2",
- "content": "Some content 2",
- "category": "Some category 2",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 3,
- "title": "Some 3",
- "content": "Some content 3",
- "category": "Some category 3",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-}
-]
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootInfoTest/export_with_data_set____false__.snap b/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootInfoTest/export_with_data_set____false__.snap
deleted file mode 100644
index 37f763e..0000000
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootInfoTest/export_with_data_set____false__.snap
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-"name":"Laravel","company":"Laravel","platform":"Laravel","url":"https://example.com","email":"test@example.com","currencies":{"@currency":[{"@attributes":{"id":"RUR","rate":"1"}}]},"categories":{"@category":[{"@attributes":{"id":41},"@value":"Домашние майки"},{"@attributes":{"id":539},"@value":"Велосипедки"},{"@attributes":{"id":44},"@value":"Ремни"}]},
-"items": [
-{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
-]
-}
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootInfoTest/export_with_data_set____true__.snap b/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootInfoTest/export_with_data_set____true__.snap
deleted file mode 100644
index 6beb376..0000000
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootInfoTest/export_with_data_set____true__.snap
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-
- "name": "Laravel",
- "company": "Laravel",
- "platform": "Laravel",
- "url": "https://example.com",
- "email": "test@example.com",
- "currencies": {
- "@currency": [
- {
- "@attributes": {
- "id": "RUR",
- "rate": "1"
- }
- }
- ]
- },
- "categories": {
- "@category": [
- {
- "@attributes": {
- "id": 41
- },
- "@value": "Домашние майки"
- },
- {
- "@attributes": {
- "id": 539
- },
- "@value": "Велосипедки"
- },
- {
- "@attributes": {
- "id": 44
- },
- "@value": "Ремни"
- }
- ]
- }
-,
-"items": [
-{
- "id": 1,
- "title": "Some 1",
- "content": "Some content 1",
- "category": "Some category 1",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 2,
- "title": "Some 2",
- "content": "Some content 2",
- "category": "Some category 2",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 3,
- "title": "Some 3",
- "content": "Some content 3",
- "category": "Some category 3",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-}
-]
-}
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootTest/export_with_data_set____true__.snap b/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootTest/export_with_data_set____true__.snap
deleted file mode 100644
index 77e1f56..0000000
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootTest/export_with_data_set____true__.snap
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-"items": [
-{
- "id": 1,
- "title": "Some 1",
- "content": "Some content 1",
- "category": "Some category 1",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 2,
- "title": "Some 2",
- "content": "Some content 2",
- "category": "Some category 2",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-},
-{
- "id": 3,
- "title": "Some 3",
- "content": "Some content 3",
- "category": "Some category 3",
- "created_at": "2025-09-04T04:08:12.000000Z",
- "updated_at": "2025-09-04T04:08:12.000000Z"
-}
-]
-}
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/CsvTest/export.snap b/tests/.pest/snapshots/Feature/Feeds/Split/CsvTest/export.snap
new file mode 100644
index 0000000..2b0dc4b
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/CsvTest/export.snap
@@ -0,0 +1,2 @@
+1;Some 1;Some content 1;Some category 1;2025-09-04T04:08:12.000000Z;2025-09-04T04:08:12.000000Z
+2;Some 2;Some content 2;Some category 2;2025-09-04T04:08:12.000000Z;2025-09-04T04:08:12.000000Z
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/CsvTest/export__2.snap b/tests/.pest/snapshots/Feature/Feeds/Split/CsvTest/export__2.snap
new file mode 100644
index 0000000..36e59ef
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/CsvTest/export__2.snap
@@ -0,0 +1 @@
+3;Some 3;Some content 3;Some category 3;2025-09-04T04:08:12.000000Z;2025-09-04T04:08:12.000000Z
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootTest/export_with_data_set____false__.snap b/tests/.pest/snapshots/Feature/Feeds/Split/JsonLinesTest/export.snap
similarity index 56%
rename from tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootTest/export_with_data_set____false__.snap
rename to tests/.pest/snapshots/Feature/Feeds/Split/JsonLinesTest/export.snap
index 1a8e82e..0385f70 100644
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/RootTest/export_with_data_set____false__.snap
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/JsonLinesTest/export.snap
@@ -1,7 +1,2 @@
-{
-"items": [
-{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
-]
-}
+{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
+{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/JsonLinesTest/export__2.snap b/tests/.pest/snapshots/Feature/Feeds/Split/JsonLinesTest/export__2.snap
new file mode 100644
index 0000000..df5d32a
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/JsonLinesTest/export__2.snap
@@ -0,0 +1 @@
+{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/DefaultTest/export_with_data_set____false__.snap b/tests/.pest/snapshots/Feature/Feeds/Split/JsonTest/export.snap
similarity index 66%
rename from tests/.pest/snapshots/Feature/Feeds/Formats/Json/DefaultTest/export_with_data_set____false__.snap
rename to tests/.pest/snapshots/Feature/Feeds/Split/JsonTest/export.snap
index e2ca44d..971a0b0 100644
--- a/tests/.pest/snapshots/Feature/Feeds/Formats/Json/DefaultTest/export_with_data_set____false__.snap
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/JsonTest/export.snap
@@ -1,5 +1,4 @@
[
{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"},
-{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
+{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
]
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/JsonTest/export__2.snap b/tests/.pest/snapshots/Feature/Feeds/Split/JsonTest/export__2.snap
new file mode 100644
index 0000000..df1c334
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/JsonTest/export__2.snap
@@ -0,0 +1,3 @@
+[
+{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
+]
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/MaxFilesTest/export.snap b/tests/.pest/snapshots/Feature/Feeds/Split/MaxFilesTest/export.snap
new file mode 100644
index 0000000..e5706ae
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/MaxFilesTest/export.snap
@@ -0,0 +1 @@
+{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/PerFileTest/export.snap b/tests/.pest/snapshots/Feature/Feeds/Split/PerFileTest/export.snap
new file mode 100644
index 0000000..0385f70
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/PerFileTest/export.snap
@@ -0,0 +1,2 @@
+{"id":1,"title":"Some 1","content":"Some content 1","category":"Some category 1","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
+{"id":2,"title":"Some 2","content":"Some content 2","category":"Some category 2","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/PerFileTest/export__2.snap b/tests/.pest/snapshots/Feature/Feeds/Split/PerFileTest/export__2.snap
new file mode 100644
index 0000000..df5d32a
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/PerFileTest/export__2.snap
@@ -0,0 +1 @@
+{"id":3,"title":"Some 3","content":"Some content 3","category":"Some category 3","created_at":"2025-09-04T04:08:12.000000Z","updated_at":"2025-09-04T04:08:12.000000Z"}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/RssTest/export.snap b/tests/.pest/snapshots/Feature/Feeds/Split/RssTest/export.snap
new file mode 100644
index 0000000..8631673
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/RssTest/export.snap
@@ -0,0 +1,23 @@
+
+
+
+
+-
+ Some 1
+ https://example.com/news/some-1
+ https://example.com/news/some-1
+
+ Some category 1
+ Thu, 04 Sep 2025 04:08:12 +0000
+
+-
+ Some 2
+ https://example.com/news/some-2
+ https://example.com/news/some-2
+
+ Some category 2
+ Thu, 04 Sep 2025 04:08:12 +0000
+
+
+
+
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/RssTest/export__2.snap b/tests/.pest/snapshots/Feature/Feeds/Split/RssTest/export__2.snap
new file mode 100644
index 0000000..45af7ca
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/RssTest/export__2.snap
@@ -0,0 +1,15 @@
+
+
+
+
+-
+ Some 3
+ https://example.com/news/some-3
+ https://example.com/news/some-3
+
+ Some category 3
+ Thu, 04 Sep 2025 04:08:12 +0000
+
+
+
+
\ No newline at end of file
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/XmlTest/export.snap b/tests/.pest/snapshots/Feature/Feeds/Split/XmlTest/export.snap
new file mode 100644
index 0000000..1d8cbba
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/XmlTest/export.snap
@@ -0,0 +1,41 @@
+
+
+
+
+ [NEWS]:Some 1
+ Some content 1
+ Some extra data
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Sauron]]>
+ Evil Eye
+
+
+ line
+line with some html/xml tag
+line with & symbol
+
+
+ [NEWS]:Some 2
+ Some content 2
+ Some extra data
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Sauron]]>
+ Evil Eye
+
+
+ line
+line with some html/xml tag
+line with & symbol
+
+
+
diff --git a/tests/.pest/snapshots/Feature/Feeds/Split/XmlTest/export__2.snap b/tests/.pest/snapshots/Feature/Feeds/Split/XmlTest/export__2.snap
new file mode 100644
index 0000000..fca5fbb
--- /dev/null
+++ b/tests/.pest/snapshots/Feature/Feeds/Split/XmlTest/export__2.snap
@@ -0,0 +1,23 @@
+
+
+
+
+ [NEWS]:Some 3
+ Some content 3
+ Some extra data
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Sauron]]>
+ Evil Eye
+
+
+ line
+line with some html/xml tag
+line with & symbol
+
+
+
diff --git a/tests/Feature/Feeds/Split/CsvTest.php b/tests/Feature/Feeds/Split/CsvTest.php
new file mode 100644
index 0000000..b0b8309
--- /dev/null
+++ b/tests/Feature/Feeds/Split/CsvTest.php
@@ -0,0 +1,13 @@
+ $feed->id,
])->assertSuccessful()->run();
- expect($instance->path())->toBeFile();
+ foreach ($indexes as $index) {
+ expect($instance->path($index))->toBeFile();
- $content = file_get_contents($instance->path());
+ $content = file_get_contents($instance->path($index));
- match ($format) {
- FeedFormatEnum::Json => expect($content)->toBeJson(),
- FeedFormatEnum::JsonLines => expect($content)->toBeJsonLines(),
- FeedFormatEnum::Csv => expect($content)->toBeCsv(),
- FeedFormatEnum::Rss => expect($content)->toBeRss(),
- default => null
- };
+ match ($format) {
+ FeedFormatEnum::Json => expect($content)->toBeJson(),
+ FeedFormatEnum::JsonLines => expect($content)->toBeJsonLines(),
+ FeedFormatEnum::Csv => expect($content)->toBeCsv(),
+ FeedFormatEnum::Rss => expect($content)->toBeRss(),
+ default => null
+ };
- expect($content)->toMatchSnapshot();
+ expect($content)->toMatchSnapshot();
+ }
}
diff --git a/tests/Helpers/models.php b/tests/Helpers/models.php
index 20db89c..c54af03 100644
--- a/tests/Helpers/models.php
+++ b/tests/Helpers/models.php
@@ -8,7 +8,7 @@
function createNews(...$sequence): void
{
- News::factory()->count(3)->sequence(
+ News::factory()->count(count($sequence))->sequence(
...$sequence
)->createMany();
}
diff --git a/workbench/app/Feeds/SplitCsvFeed.php b/workbench/app/Feeds/SplitCsvFeed.php
new file mode 100644
index 0000000..96bbfa5
--- /dev/null
+++ b/workbench/app/Feeds/SplitCsvFeed.php
@@ -0,0 +1,33 @@
+',
+ '',
+ ]);
+ }
+
+ public function footer(): string
+ {
+ return '';
+ }
+}
diff --git a/workbench/app/Feeds/SplitXmlFeed.php b/workbench/app/Feeds/SplitXmlFeed.php
new file mode 100644
index 0000000..eeaecb1
--- /dev/null
+++ b/workbench/app/Feeds/SplitXmlFeed.php
@@ -0,0 +1,40 @@
+