diff --git a/composer.json b/composer.json index 6bef483..a39f4b9 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "issues": "https://github.com/Bukashk0zzz/YmlGenerator/issues" }, "require": { - "php": ">=5.6.1" + "php": ">=5.6.1", + "ext-simplexml": "*" }, "require-dev": { "ext-dom": "*", diff --git a/src/Model/Offer/AbstractOffer.php b/src/Model/Offer/AbstractOffer.php index 509ac2c..be4c294 100644 --- a/src/Model/Offer/AbstractOffer.php +++ b/src/Model/Offer/AbstractOffer.php @@ -111,7 +111,9 @@ abstract class AbstractOffer implements OfferInterface */ private $cpa; - /** @var string[] */ + /** + * @var string[] + */ private $barcodes; /** @@ -129,6 +131,13 @@ abstract class AbstractOffer implements OfferInterface */ private $store; + /** + * Array of custom elements (element types are keys) of arrays of element values + * There may be multiple elements of the same type + * @var array[] + */ + private $customElements; + /** * @return array */ @@ -635,6 +644,68 @@ public function addBarcode($barcode) return $this; } + /** + * Sets list of custom elements + * + * @param array $customElements Array (keys are element types) of arrays (element values) + * + * @return $this + */ + public function setCustomElements(array $customElements = []) + { + $this->customElements = $customElements; + + return $this; + } + + /** + * Add a custom element with given type and value + * Multiple elements of the same type are supported + * + * @param string $elementType + * @param mixed $value + * + * @return $this + */ + public function addCustomElement($elementType, $value) + { + if ($value !== null) { + // Add value to the list of values of the given element type creating array when needed + $this->customElements[$elementType][] = $value; + } + + return $this; + } + + /** + * Returns a list of custom elements + * Always returns an array even if no custom elements were added + * + * @return array + */ + public function getCustomElements() + { + return $this->customElements ?: []; + } + + /** + * Returns a list of values for the specified custom element type + * Always returns an array + * + * @param string $elementType + * + * @return array + */ + public function getCustomElementByType($elementType) + { + // TODO: Use ?? operator when support for PHP 5.6 is no longer needed + if (isset($this->customElements[$elementType])) { + return $this->customElements[$elementType]; + } + + return []; + } + /** * @return array */ @@ -658,7 +729,7 @@ private function getHeaderOptions() 'delivery' => $this->isDelivery(), 'weight' => $this->getWeight(), 'local_delivery_cost' => $this->getLocalDeliveryCost(), - ]; + ] + $this->getCustomElements(); } /** diff --git a/tests/AbstractGeneratorTest.php b/tests/AbstractGeneratorTest.php index 37cbe1e..0aba3e8 100644 --- a/tests/AbstractGeneratorTest.php +++ b/tests/AbstractGeneratorTest.php @@ -79,9 +79,9 @@ public function setUp() abstract protected function createOffer(); /** - * Test generation + * Produces an XML file and writes to $this->settings->getOutputFile() */ - protected function runGeneratorTest() + protected function generateFile() { static::assertTrue((new Generator($this->settings))->generate( $this->shopInfo, @@ -90,7 +90,14 @@ protected function runGeneratorTest() $this->createOffers(), $this->deliveries )); + } + /** + * Test generation + */ + protected function runGeneratorTest() + { + $this->generateFile(); $this->validateFileWithDtd(); } diff --git a/tests/OfferCustomElementsGeneratorTest.php b/tests/OfferCustomElementsGeneratorTest.php new file mode 100755 index 0000000..4f6ef92 --- /dev/null +++ b/tests/OfferCustomElementsGeneratorTest.php @@ -0,0 +1,145 @@ +Simple HTML

validateFileWithDtd() here because custom elements are not included into the default DTD + $this->generateFile(); + $this->checkCustomElements(); + } + + /** + * Need to override parent::createOffers() in order to avoid setting description + * after calling self::createOffer() + * + * {@inheritDoc} + * @see \Bukashk0zzz\YmlGenerator\Tests\AbstractGeneratorTest::createOffers() + */ + protected function createOffers() + { + $offers = []; + foreach (\range(1, self::OFFER_COUNT) as $id) { + $offers[] = + $this->createOffer() + ->setId($id) + ->setCategoryId($id) + ; + } + + return $offers; + } + + /** + * Set the test description with CDATA here + * + * {@inheritDoc} + * @see \Bukashk0zzz\YmlGenerator\Tests\AbstractGeneratorTest::createOffer() + */ + protected function createOffer() + { + $offer = (new OfferSimple()) + ->setAvailable($this->faker->boolean) + ->setUrl($this->faker->url) + ->setPrice($this->faker->numberBetween(1, 9999)) + ->setOldPrice($this->faker->numberBetween(1, 9999)) + ->setWeight($this->faker->numberBetween(1, 9999)) + ->setCurrencyId('UAH') + ->setDelivery($this->faker->boolean) + ->setLocalDeliveryCost($this->faker->numberBetween(1, 9999)) + ->setSalesNotes($this->faker->text(45)) + ->setManufacturerWarranty($this->faker->boolean) + ->setCountryOfOrigin('Украина') + ->setDownloadable($this->faker->boolean) + ->setAdult($this->faker->boolean) + ->setMarketCategory($this->faker->word) + ->setCpa($this->faker->numberBetween(0, 1)) + ->setBarcodes([$this->faker->ean13, $this->faker->ean13]) + + ->setName($this->faker->name) + ->setVendor($this->faker->company) + ->setDescription($this->faker->sentence) + ->setVendorCode(null) + ->setPickup(true) + ->setGroupId($this->faker->numberBetween()) + ->addPicture('http://example.com/example.jpeg') + ->addBarcode($this->faker->ean13) + + ->setCustomElements(['custom_element' => [100500, 'string value']]) + ->addCustomElement('custom_element', true) + ->addCustomElement('custom_element', false) + ->addCustomElement('custom_element', null) // Should not be written + ->addCustomElement('custom_element', $cdata = new Cdata(self::CDATA_TEST_STRING)) + ->addCustomElement('stock_quantity', 100) // https://rozetka.com.ua/sellerinfo/pricelist/ + ; + + $this->assertSame([100500, 'string value', true, false, $cdata], $offer->getCustomElementByType('custom_element')); + $this->assertSame([100], $offer->getCustomElementByType('stock_quantity')); + $this->assertSame([], $offer->getCustomElementByType('non_existent_element')); + + return $offer; + } + + /** + * Load generated XML file and check custom elements + */ + private function checkCustomElements() + { + // Much easier to test with SimpleXML tahn with DOM + $yml = \simplexml_load_file($this->settings->getOutputFile()); + + $offers = $yml->shop->offers->offer; + $this->assertNotEmpty($offers); + $this->assertEquals(self::OFFER_COUNT, count($offers)); + + foreach ($offers as $offer) { + $prop = 'stock_quantity'; + $this->assertSame(100, (int) $offer->$prop); // Can't use $offer->stock_quantity because of CS rules + + $prop = 'custom_element'; + $multipleElements = $offer->$prop; // Can't use $offer->custom_element because of CS rules + $this->assertNotEmpty($multipleElements); + + // Verity each added value + $this->assertSame(100500, (int) $multipleElements[0]); + $this->assertSame('string value', (string) $multipleElements[1]); + $this->assertSame('true', (string) $multipleElements[2]); + $this->assertSame('false', (string) $multipleElements[3]); + + // ->addCustomElement('custom_element', null) must not produce an element + + $this->assertSame(self::CDATA_TEST_STRING, (string) $multipleElements[4]); + } + } + + /** + * Create instance of Cdata class with a predefined test string + * + * @return \Bukashk0zzz\YmlGenerator\Cdata + */ + private function makeDescription() + { + return new Cdata(self::CDATA_TEST_STRING); + } +}