diff --git a/README.md b/README.md
index 6c88717..6a5ce94 100644
--- a/README.md
+++ b/README.md
@@ -266,6 +266,32 @@ class UserFeedItem extends FeedItem
}
```
+#### Adding an array of elements
+
+In some cases, you need to place an array of elements with the same names. For example:
+
+```xml
+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
+```
+
+To do this, add a symbol of `@` to the beginning of the key name:
+
+```php
+use DragonCode\LaravelFeed\Feeds\Items\FeedItem;
+
+class UserFeedItem extends FeedItem
+{
+ public function toArray(): array
+ {
+ return [
+ '@picture' => $this->model->images,
+ ];
+ }
+}
+```
+
#### Header information
If it is necessary to change the file cap, override the `header` method in the feed class:
diff --git a/src/Services/ConvertToXml.php b/src/Services/ConvertToXml.php
index f1b82ef..75f6d9c 100644
--- a/src/Services/ConvertToXml.php
+++ b/src/Services/ConvertToXml.php
@@ -8,11 +8,14 @@
use DOMElement;
use DragonCode\LaravelFeed\Feeds\Items\FeedItem;
use Illuminate\Container\Attributes\Config;
+use Illuminate\Support\Str;
use function htmlspecialchars;
use function is_array;
+use function is_bool;
use function preg_replace;
use function str_replace;
+use function str_starts_with;
class ConvertToXml
{
@@ -54,6 +57,7 @@ protected function performItem(DOMElement $parent, array $items): void
$this->isAttributes($key) => $this->setAttributes($parent, $value),
$this->isCData($key) => $this->setCData($parent, $value),
$this->isMixed($key) => $this->setMixed($parent, $value),
+ $this->isArray($key) => $this->setItemsArray($parent, $value, $key),
default => $this->setItems($parent, $key, $value),
};
}
@@ -74,6 +78,11 @@ protected function isMixed(string $key): bool
return $key === '@mixed';
}
+ protected function isArray(string $key): bool
+ {
+ return str_starts_with($key, '@');
+ }
+
protected function createElement(string $name, bool|float|int|string|null $value = ''): DOMElement
{
return $this->document->createElement($name, $this->convertValue($value));
@@ -101,6 +110,15 @@ protected function setMixed(DOMElement $element, string $value): void
$element->appendChild($fragment);
}
+ protected function setItemsArray(DOMElement $parent, mixed $value, string $key): void
+ {
+ $key = Str::substr($key, 1);
+
+ foreach ($value as $item) {
+ $this->setItems($parent, $key, $item);
+ }
+ }
+
protected function setItems(DOMElement $parent, string $key, mixed $value): void
{
$element = $this->createElement($key, is_array($value) ? '' : $this->convertValue($value));
@@ -117,13 +135,17 @@ protected function toXml(DOMElement $item): string
return $this->document->saveXML($item);
}
- protected function convertKey(string $key): string
+ protected function convertKey(int|string $key): string
{
- return str_replace(' ', '_', $key);
+ return str_replace(' ', '_', (string) $key);
}
protected function convertValue(bool|float|int|string|null $value): string
{
+ if (is_bool($value)) {
+ return $value ? 'true' : 'false';
+ }
+
return $this->removeControlCharacters(
htmlspecialchars((string) $value)
);
diff --git a/tests/.pest/snapshots/Feature/YandexTest/export.snap b/tests/.pest/snapshots/Feature/YandexTest/export.snap
index 58988d2..77983d6 100644
--- a/tests/.pest/snapshots/Feature/YandexTest/export.snap
+++ b/tests/.pest/snapshots/Feature/YandexTest/export.snap
@@ -24,6 +24,7 @@
100
RUR
The Best
+ https://via.placeholder.com/640x480.png/008877?text=repudiandae
http://localhost/products/GD-PRDCT-2
@@ -34,6 +35,9 @@
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
http://localhost/products/GD-PRDCT-3
@@ -44,6 +48,8 @@
400
RUR
The Best
+ https://via.placeholder.com/640x480.png/000044?text=asperiores
+ https://via.placeholder.com/640x480.png/0055ff?text=expedita
diff --git a/workbench/app/Data/ProductFakeData.php b/workbench/app/Data/ProductFakeData.php
index 5a3ba43..963c5d1 100644
--- a/workbench/app/Data/ProductFakeData.php
+++ b/workbench/app/Data/ProductFakeData.php
@@ -4,8 +4,6 @@
namespace Workbench\App\Data;
-use function json_encode;
-
class ProductFakeData
{
public static function toArray(): array
@@ -22,9 +20,9 @@ public static function toArray(): array
'brand' => 'The Best',
- 'images' => json_encode([
- 'https://via.placeholder.com/640x480.png/00ff55?text=s1',
- ]),
+ 'images' => [
+ 'https://via.placeholder.com/640x480.png/008877?text=repudiandae',
+ ],
'created_at' => '2025-08-31 00:00:00',
'updated_at' => '2025-08-31 20:00:00',
@@ -40,11 +38,11 @@ public static function toArray(): array
'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',
- ]),
+ 'images' => [
+ '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',
+ ],
'created_at' => '2025-08-30 00:00:00',
'updated_at' => '2025-08-30 19:00:00',
@@ -60,10 +58,10 @@ public static function toArray(): array
'brand' => 'The Best',
- 'images' => json_encode([
- 'https://via.placeholder.com/640x480.png/00ff55?text=s30',
- 'https://via.placeholder.com/640x480.png/00ff55?text=s31',
- ]),
+ 'images' => [
+ 'https://via.placeholder.com/640x480.png/000044?text=asperiores',
+ 'https://via.placeholder.com/640x480.png/0055ff?text=expedita',
+ ],
'created_at' => '2025-08-29 00:00:00',
'updated_at' => '2025-08-29 18:00:00',
diff --git a/workbench/app/Feeds/Items/YandexFeedItem.php b/workbench/app/Feeds/Items/YandexFeedItem.php
index 2bcc15e..54bcc81 100644
--- a/workbench/app/Feeds/Items/YandexFeedItem.php
+++ b/workbench/app/Feeds/Items/YandexFeedItem.php
@@ -19,7 +19,7 @@ public function attributes(): array
return [
'id' => $this->model->id,
- 'available' => ! empty($this->model->quantity) ? 'true' : 'false',
+ 'available' => ! empty($this->model->quantity),
'type' => 'vendor.model',
];
@@ -40,7 +40,7 @@ public function toArray(): array
'currencyId' => 'RUR',
'vendor' => $this->model->brand,
- // 'picture' => $this->model->images,
+ '@picture' => $this->model->images,
];
}
}
diff --git a/workbench/app/Models/Product.php b/workbench/app/Models/Product.php
index b8bb4c2..b3aee87 100644
--- a/workbench/app/Models/Product.php
+++ b/workbench/app/Models/Product.php
@@ -27,4 +27,8 @@ class Product extends Model
'images',
];
+
+ protected $casts = [
+ 'images' => 'array',
+ ];
}
diff --git a/workbench/database/factories/ProductFactory.php b/workbench/database/factories/ProductFactory.php
index 058714a..9015532 100644
--- a/workbench/database/factories/ProductFactory.php
+++ b/workbench/database/factories/ProductFactory.php
@@ -7,8 +7,6 @@
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
-use function json_encode;
-
class ProductFactory extends Factory
{
public function definition(): array
@@ -24,11 +22,11 @@ public function definition(): array
'quantity' => fake()->numberBetween(0, 10),
'currency' => fake()->currencyCode(),
- 'images' => json_encode([
+ 'images' => [
fake()->imageUrl(),
fake()->imageUrl(),
fake()->imageUrl(),
- ]),
+ ],
];
}
}