From ec978036b7474be4aa6cef5593c1aaa546bcdd49 Mon Sep 17 00:00:00 2001 From: Dominik Pfaffenbauer Date: Wed, 14 Jul 2021 15:46:46 +0200 Subject: [PATCH 1/6] [Slug] default generate slugs and use instead of static routes for product and category --- .../product/view_product_via_link.feature | 2 +- .../Context/Ui/Domain/PaymentController.php | 4 - .../Context/Ui/Frontend/ProductContext.php | 2 - .../Behat/Page/Frontend/ProductPage.php | 5 +- .../Page/Frontend/SluggablePageTrait.php | 28 +++ .../Bundle/CoreBundle/Faker/Commerce.php | 192 ++++++++++++------ .../Data/Demo/AbstractProductFixture.php | 14 +- .../Fixtures/Data/Demo/CategoryFixture.php | 7 +- .../CoreShopCategory.json | 23 ++- .../CoreShopProduct.json | 23 ++- .../Controller/CategoryController.php | 56 +++-- .../Controller/ProductController.php | 59 +++--- .../install/pimcore/staticroutes.yml | 14 -- .../EventListener/SluggableListener.php | 102 ++++++++++ .../Resources/config/services.yml | 12 ++ .../install/pimcore/admin-translations.yml | 10 + .../pimcore/classes/CoreShopCategory.json | 24 ++- .../pimcore/classes/CoreShopProduct.json | 23 ++- .../Pimcore/Slug/SluggableInterface.php | 32 +++ .../Pimcore/Slug/SluggableLinkGenerator.php | 61 ++++++ .../Component/Product/Model/Category.php | 5 + .../Product/Model/CategoryInterface.php | 3 +- .../Component/Product/Model/Product.php | 5 + .../Product/Model/ProductInterface.php | 3 +- 24 files changed, 561 insertions(+), 148 deletions(-) create mode 100644 src/CoreShop/Behat/Page/Frontend/SluggablePageTrait.php create mode 100644 src/CoreShop/Bundle/PimcoreBundle/EventListener/SluggableListener.php create mode 100644 src/CoreShop/Component/Pimcore/Slug/SluggableInterface.php create mode 100644 src/CoreShop/Component/Pimcore/Slug/SluggableLinkGenerator.php diff --git a/features/ui/frontend/product/view_product_via_link.feature b/features/ui/frontend/product/view_product_via_link.feature index fb90f5d1c2..06e182eb08 100644 --- a/features/ui/frontend/product/view_product_via_link.feature +++ b/features/ui/frontend/product/view_product_via_link.feature @@ -8,7 +8,7 @@ Feature: Viewing a product details using it's link Scenario: View product via link Given the site has a product "T-Shirt" And the product is active and published and available for store "Austria" - When I open the page "en/shop/t-shirt~p%id%" for this product + When I open the page "en/t-shirt" for this product Then I should be on the product's detail page Then I should see the product name "T-Shirt" diff --git a/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php b/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php index c8a98bfb36..377607ac1a 100644 --- a/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php +++ b/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php @@ -86,10 +86,6 @@ public function iOpenCartSummaryPage(PaymentInterface $payment) * @var Response $result */ foreach ($responses as $index => $result) { - echo "Request:" . $requests[$index]->getUri() . PHP_EOL; - echo (string)$result->getBody(); - echo PHP_EOL; -// Assert::eq( $result->getStatusCode(), 200, diff --git a/src/CoreShop/Behat/Context/Ui/Frontend/ProductContext.php b/src/CoreShop/Behat/Context/Ui/Frontend/ProductContext.php index 301e0a8d18..983b067154 100644 --- a/src/CoreShop/Behat/Context/Ui/Frontend/ProductContext.php +++ b/src/CoreShop/Behat/Context/Ui/Frontend/ProductContext.php @@ -45,8 +45,6 @@ public function __construct( */ public function iOpenPage($url, ProductInterface $product) { - $url = str_replace('%id%', (string)$product->getId(), $url); - $this->productPage->tryToOpenWithUri($url); } diff --git a/src/CoreShop/Behat/Page/Frontend/ProductPage.php b/src/CoreShop/Behat/Page/Frontend/ProductPage.php index d191d75f40..3d7c51d786 100644 --- a/src/CoreShop/Behat/Page/Frontend/ProductPage.php +++ b/src/CoreShop/Behat/Page/Frontend/ProductPage.php @@ -20,10 +20,7 @@ class ProductPage extends AbstractFrontendPage implements ProductPageInterface { - public function getRouteName(): string - { - return 'coreshop_product_detail'; - } + use SluggablePageTrait; public function getContent(): string { diff --git a/src/CoreShop/Behat/Page/Frontend/SluggablePageTrait.php b/src/CoreShop/Behat/Page/Frontend/SluggablePageTrait.php new file mode 100644 index 0000000000..f6b1fa693e --- /dev/null +++ b/src/CoreShop/Behat/Page/Frontend/SluggablePageTrait.php @@ -0,0 +1,28 @@ + [ - 'Small', - 'Ergonomic', - 'Rustic', - 'Intelligent', - 'Gorgeous', - 'Incredible', - 'Fantastic', - 'Practical', - 'Sleek', - 'Awesome', - 'Enormous', - 'Mediocre', - 'Synergistic', - 'Heavy Duty', - 'Lightweight', - 'Aerodynamic', - 'Durable', - ], - 'material' => [ - 'Steel', - 'Wooden', - 'Concrete', - 'Plastic', - 'Cotton', - 'Granite', - 'Rubber', - 'Leather', - 'Silk', - 'Wool', - 'Linen', - 'Marble', - 'Iron', - 'Bronze', - 'Copper', - 'Aluminum', - 'Paper', + 'en' => [ + 'adjective' => [ + 'Small', + 'Ergonomic', + 'Rustic', + 'Intelligent', + 'Gorgeous', + 'Incredible', + 'Fantastic', + 'Practical', + 'Sleek', + 'Awesome', + 'Enormous', + 'Mediocre', + 'Synergistic', + 'Heavy Duty', + 'Lightweight', + 'Aerodynamic', + 'Durable', + ], + 'material' => [ + 'Steel', + 'Wooden', + 'Concrete', + 'Plastic', + 'Cotton', + 'Granite', + 'Rubber', + 'Leather', + 'Silk', + 'Wool', + 'Linen', + 'Marble', + 'Iron', + 'Bronze', + 'Copper', + 'Aluminum', + 'Paper', + ], + 'product' => [ + 'Chair', + 'Car', + 'Computer', + 'Gloves', + 'Pants', + 'Shirt', + 'Table', + 'Shoes', + 'Hat', + 'Plate', + 'Knife', + 'Bottle', + 'Coat', + 'Lamp', + 'Keyboard', + 'Bag', + 'Bench', + 'Clock', + 'Watch', + 'Wallet', + ], ], - 'product' => [ - 'Chair', - 'Car', - 'Computer', - 'Gloves', - 'Pants', - 'Shirt', - 'Table', - 'Shoes', - 'Hat', - 'Plate', - 'Knife', - 'Bottle', - 'Coat', - 'Lamp', - 'Keyboard', - 'Bag', - 'Bench', - 'Clock', - 'Watch', - 'Wallet', + 'de' => [ + 'adjective' => [ + 'Klein', + 'Ergonomisch', + 'Rustikal', + 'Intelligent', + 'Herrlich', + 'Unglaublich', + 'Fantastisch', + 'Praktisch', + 'Glatt', + 'Genial', + 'Enorm', + 'Mittelmäßig', + 'Synergistisch', + 'Schwerlast', + 'Leicht', + 'Aerodynamisch', + 'Dauerhaft', + ], + 'material' => [ + 'Stahl', + 'Hölzern', + 'Beton', + 'Plastik', + 'Baumwolle', + 'Granit', + 'Gummi', + 'Leder', + 'Die Seide', + 'Wolle', + 'Leinen', + 'Marmor', + 'Eisen', + 'Bronze', + 'Kupfer', + 'Aluminium', + 'Papier', + ], + 'product' => [ + 'Stuhl', + 'Wagen', + 'Computer', + 'Handschuhe', + 'Hose', + 'Hemd', + 'Tabelle', + 'Schuhe', + 'Hut', + 'Teller', + 'Messer', + 'Flasche', + 'Mantel', + 'Lampe', + 'Tastatur', + 'Tasche', + 'Bank', + 'Uhr', + 'Uhr', + 'Brieftasche', + ], ], ]; protected static $promotionCode = [ @@ -124,8 +188,8 @@ class Commerce extends Base public function promotionCode(int $digits = 6): string { return static::randomElement(static::$promotionCode['adjective']) - . static::randomElement(static::$promotionCode['noun']) - . $this->generator->randomNumber($digits, true); + .static::randomElement(static::$promotionCode['noun']) + .$this->generator->randomNumber($digits, true); } public function department(int $max = 3, bool $fixedAmount = false): string @@ -154,10 +218,10 @@ public function category(): string return static::randomElement(static::$department); } - public function productName(): string + public function productName(?string $locale = null): string { - return static::randomElement(static::$productName['adjective']) - . ' ' . static::randomElement(static::$productName['material']) - . ' ' . static::randomElement(static::$productName['product']); + return static::randomElement(static::$productName[$locale ?? 'en']['adjective']) + .' '.static::randomElement(static::$productName[$locale ?? 'en']['material']) + .' '.static::randomElement(static::$productName[$locale ?? 'en']['product']); } } diff --git a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php index b53fd569b3..94a327d203 100644 --- a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php +++ b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php @@ -23,9 +23,11 @@ use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Faker\Factory; use Faker\Provider\Barcode; +use Faker\Provider\Base; use Faker\Provider\Lorem; use Pimcore\Model\Asset; use Pimcore\Model\DataObject\Service; +use Pimcore\Tool; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\HttpKernel\KernelInterface; @@ -49,6 +51,11 @@ public function getDependencies(): array protected function createProduct(string $parentPath): ProductInterface { + $fakerDE = Factory::create('de_DE'); + $fakerDE->addProvider(new Lorem($fakerDE)); + $fakerDE->addProvider(new Barcode($fakerDE)); + $fakerDE->addProvider(new Commerce($fakerDE)); + $faker = Factory::create(); $faker->addProvider(new Lorem($faker)); $faker->addProvider(new Barcode($faker)); @@ -99,9 +106,12 @@ protected function createProduct(string $parentPath): ProductInterface * @var ProductInterface $product */ $product = $this->container->get('coreshop.factory.product')->createNew(); - $product->setName($faker->productName); + foreach (Tool::getValidLanguages() as $language) { + $product->setName($faker->productName($language), $language); + + $product->setShortDescription($language === 'de' ? $fakerDE->text() : $faker->text(), $language); + } $product->setSku($faker->ean13); - $product->setShortDescription($faker->text()); $product->setDescription(implode('
', $faker->paragraphs(3))); $product->setEan($faker->ean13); $product->setActive(true); diff --git a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php index 2d8b6ce054..7c7fe5234e 100644 --- a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php +++ b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php @@ -22,6 +22,7 @@ use Doctrine\Persistence\ObjectManager; use Faker\Factory; use Pimcore\Model\DataObject\Service; +use Pimcore\Tool; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -51,7 +52,11 @@ public function load(ObjectManager $manager): void * @var CategoryInterface $category */ $category = $this->container->get('coreshop.factory.category')->createNew(); - $category->setName($faker->department); + + foreach (Tool::getValidLanguages() as $language) { + $category->setName($faker->department, $language); + } + $category->setParent($this->container->get(ObjectServiceInterface::class)->createFolderByPath('/demo/categories')); $category->setStores([$this->container->get('coreshop.repository.store')->findStandard()->getId()]); $category->setPublished(true); diff --git a/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopCategory.json b/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopCategory.json index 514bf0f253..74e02a59b9 100644 --- a/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopCategory.json +++ b/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopCategory.json @@ -7,7 +7,7 @@ "allowVariants": false, "showVariants": false, "generateTypeDeclarations": true, - "linkGeneratorReference": "@coreshop.object.link_generator.category", + "linkGeneratorReference": "@CoreShop\\Component\\Pimcore\\Slug\\SluggableLinkGenerator", "layoutDefinitions": { "fieldtype": "panel", "labelWidth": 100, @@ -96,6 +96,27 @@ "invisible": false, "visibleGridView": false, "visibleSearch": false + }, + { + "fieldtype": "urlSlug", + "width": "", + "domainLabelWidth": null, + "action": "CoreShop\\Bundle\\FrontendBundle\\Controller\\CategoryController:detailSlugAction", + "availableSites": [], + "name": "slug", + "title": "coreshop.category.slug", + "tooltip": "", + "mandatory": false, + "noteditable": false, + "index": false, + "locked": false, + "style": "", + "permissions": null, + "datatype": "data", + "relationType": false, + "invisible": false, + "visibleGridView": false, + "visibleSearch": false } ], "name": "localizedfields", diff --git a/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopProduct.json b/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopProduct.json index 85915d27b3..7af8cb7179 100644 --- a/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopProduct.json +++ b/src/CoreShop/Bundle/CoreBundle/Resources/install/pimcore/classes/CoreShopProductBundle/CoreShopProduct.json @@ -7,7 +7,7 @@ "allowVariants": false, "showVariants": false, "generateTypeDeclarations": true, - "linkGeneratorReference": "@coreshop.object.link_generator.product", + "linkGeneratorReference": "@CoreShop\\Component\\Pimcore\\Slug\\SluggableLinkGenerator", "layoutDefinitions": { "fieldtype": "panel", "labelWidth": 100, @@ -110,6 +110,27 @@ "invisible": false, "visibleGridView": true, "visibleSearch": true + }, + { + "fieldtype": "urlSlug", + "width": "", + "domainLabelWidth": null, + "action": "CoreShop\\Bundle\\FrontendBundle\\Controller\\ProductController:detailSlugAction", + "availableSites": [], + "name": "slug", + "title": "coreshop.product.slug", + "tooltip": "", + "mandatory": false, + "noteditable": false, + "index": false, + "locked": false, + "style": "", + "permissions": null, + "datatype": "data", + "relationType": false, + "invisible": false, + "visibleGridView": false, + "visibleSearch": false } ], "name": "localizedfields", diff --git a/src/CoreShop/Bundle/FrontendBundle/Controller/CategoryController.php b/src/CoreShop/Bundle/FrontendBundle/Controller/CategoryController.php index f914ccd890..036cf68ece 100644 --- a/src/CoreShop/Bundle/FrontendBundle/Controller/CategoryController.php +++ b/src/CoreShop/Bundle/FrontendBundle/Controller/CategoryController.php @@ -29,6 +29,7 @@ use CoreShop\Component\Tracking\Tracker\TrackerInterface; use Knp\Component\Pager\PaginatorInterface; use Pimcore\Http\RequestHelper; +use Pimcore\Model\DataObject\Data\UrlSlug; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -68,7 +69,23 @@ public function menuLeftAction(Request $request): Response ]); } + public function detailSlugAction(Request $request, CategoryInterface $object, UrlSlug $urlSlug) + { + return $this->detail($request, $object); + } + public function indexAction(Request $request): Response + { + $category = $this->getRepository()->findOneBy([$this->repositoryIdentifier => $request->get($this->requestIdentifier), 'pimcore_unpublished' => true]); + + if (!$category instanceof CategoryInterface) { + throw new NotFoundHttpException(sprintf(sprintf('category with identifier "%s" (%s) not found', $this->repositoryIdentifier, $request->get($this->requestIdentifier)))); + } + + return $this->detail($request, $category); + } + + public function detail(Request $request, CategoryInterface $category): Response { $listModeDefault = $this->getConfigurationService()->getForStore('system.category.list.mode'); $gridPerPageAllowed = $this->getConfigurationService()->getForStore('system.category.grid.per_page'); @@ -85,28 +102,8 @@ public function indexAction(Request $request): Response $allowedPerPage = $type === 'list' ? $listPerPageAllowed : $gridPerPageAllowed; $perPage = $request->get('perPage', $defaultPerPage); - $isFrontendRequestByAdmin = false; - $category = $this->getRepository()->findOneBy([$this->repositoryIdentifier => $request->get($this->requestIdentifier), 'pimcore_unpublished' => true]); - - if (!$category instanceof CategoryInterface) { - throw new NotFoundHttpException(sprintf(sprintf('category with identifier "%s" (%s) not found', $this->repositoryIdentifier, $request->get($this->requestIdentifier)))); - } - if ($this->get(RequestHelper::class)->isFrontendRequestByAdmin($request)) { - $isFrontendRequestByAdmin = true; - } - - if ($isFrontendRequestByAdmin === false && !$category->isPublished()) { - throw new NotFoundHttpException('category not found'); - } - - if (!in_array($this->getContext()->getStore()->getId(), array_values($category->getStores()))) { - throw new NotFoundHttpException(sprintf(sprintf('store (id %s) not available in category', $this->getContext()->getStore()->getId()))); - } - - if (!in_array($perPage, $allowedPerPage)) { - $perPage = $defaultPerPage; - } + $this->validateCategory($request, $category); $viewParameters = []; @@ -193,6 +190,23 @@ public function indexAction(Request $request): Response return $this->render($this->templateConfigurator->findTemplate('Category/index.html'), $viewParameters); } + protected function validateCategory(Request $request, CategoryInterface $category) + { + $isFrontendRequestByAdmin = false; + + if ($this->get(RequestHelper::class)->isFrontendRequestByAdmin($request)) { + $isFrontendRequestByAdmin = true; + } + + if ($isFrontendRequestByAdmin === false && !$category->isPublished()) { + throw new NotFoundHttpException('category not found'); + } + + if (!in_array($this->getContext()->getStore()->getId(), array_values($category->getStores()))) { + throw new NotFoundHttpException(sprintf(sprintf('store (id %s) not available in category', $this->getContext()->getStore()->getId()))); + } + } + protected function parseSorting(string $sortString): array { $sort = [ diff --git a/src/CoreShop/Bundle/FrontendBundle/Controller/ProductController.php b/src/CoreShop/Bundle/FrontendBundle/Controller/ProductController.php index 83df0493f0..191e14bd77 100644 --- a/src/CoreShop/Bundle/FrontendBundle/Controller/ProductController.php +++ b/src/CoreShop/Bundle/FrontendBundle/Controller/ProductController.php @@ -15,22 +15,18 @@ namespace CoreShop\Bundle\FrontendBundle\Controller; use CoreShop\Component\Core\Model\ProductInterface; +use CoreShop\Component\Order\Model\PurchasableInterface; use CoreShop\Component\SEO\SEOPresentationInterface; use CoreShop\Component\Store\Context\StoreContextInterface; use CoreShop\Component\Tracking\Tracker\TrackerInterface; use Pimcore\Http\RequestHelper; -use Pimcore\Model\DataObject; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class ProductController extends FrontendController { - /** - * @param Request $request - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function latestAction(Request $request) + public function latestAction(Request $request): Response { $productRepository = $this->get('coreshop.repository.product'); @@ -39,21 +35,40 @@ public function latestAction(Request $request) ]); } - /** - * @param Request $request - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function detailAction(Request $request) + public function detailSlugAction(Request $request, ProductInterface $object) { - $product = $this->getProductByRequest($request); + $this->validateProduct($request, $object); - $isFrontendRequestByAdmin = false; + $this->get(SEOPresentationInterface::class)->updateSeoMetadata($object); + $this->get(TrackerInterface::class)->trackProduct($object); + + return $this->render($this->templateConfigurator->findTemplate('Product/detail.html'), [ + 'product' => $object, + ]); + } + + public function detailAction(Request $request): Response + { + $product = $this->getProductByRequest($request); if (!$product instanceof ProductInterface) { throw new NotFoundHttpException('product not found'); } + $this->validateProduct($request, $product); + + $this->get(SEOPresentationInterface::class)->updateSeoMetadata($product); + $this->get(TrackerInterface::class)->trackProduct($product); + + return $this->render($this->templateConfigurator->findTemplate('Product/detail.html'), [ + 'product' => $product, + ]); + } + + protected function validateProduct(Request $request, ProductInterface $product): void + { + $isFrontendRequestByAdmin = false; + if ($this->get(RequestHelper::class)->isFrontendRequestByAdmin($request)) { $isFrontendRequestByAdmin = true; } @@ -65,21 +80,9 @@ public function detailAction(Request $request) if (!in_array($this->get(StoreContextInterface::class)->getStore()->getId(), $product->getStores())) { throw new NotFoundHttpException('product not found'); } - - $this->get(SEOPresentationInterface::class)->updateSeoMetadata($product); - $this->get(TrackerInterface::class)->trackProduct($product); - - return $this->render($this->templateConfigurator->findTemplate('Product/detail.html'), [ - 'product' => $product, - ]); } - /** - * @param Request $request - * - * @return DataObject\Concrete - */ - protected function getProductByRequest(Request $request) + protected function getProductByRequest(Request $request): ?PurchasableInterface { return $this->get('coreshop.repository.stack.purchasable')->find($request->get('product')); } diff --git a/src/CoreShop/Bundle/FrontendBundle/Resources/install/pimcore/staticroutes.yml b/src/CoreShop/Bundle/FrontendBundle/Resources/install/pimcore/staticroutes.yml index 5ec2eb4adf..2d04713b5e 100644 --- a/src/CoreShop/Bundle/FrontendBundle/Resources/install/pimcore/staticroutes.yml +++ b/src/CoreShop/Bundle/FrontendBundle/Resources/install/pimcore/staticroutes.yml @@ -6,20 +6,6 @@ routes: variables: _locale priority: 1 - coreshop_category_list: - pattern: '/(\w+)\/shop\/(.*?)\~c([0-9]+)$/' - reverse: '/%_locale/shop/%name~c%category' - controller: 'CoreShop\Bundle\FrontendBundle\Controller\CategoryController:indexAction' - variables: _locale,name,category - priority: 2 - - coreshop_product_detail: - pattern: '/(\w+)\/shop\/(.*?)\~p([0-9]+)$/' - reverse: '/%_locale/shop/%name~p%product' - controller: 'CoreShop\Bundle\FrontendBundle\Controller\ProductController:detailAction' - variables: _locale,name,product - priority: 2 - coreshop_cart_summary: pattern: '/(\w+)\/shop\/cart$/' reverse: '/%_locale/shop/cart' diff --git a/src/CoreShop/Bundle/PimcoreBundle/EventListener/SluggableListener.php b/src/CoreShop/Bundle/PimcoreBundle/EventListener/SluggableListener.php new file mode 100644 index 0000000000..0fa28eec73 --- /dev/null +++ b/src/CoreShop/Bundle/PimcoreBundle/EventListener/SluggableListener.php @@ -0,0 +1,102 @@ +slugger = $slugger; + } + + public static function getSubscribedEvents() + { + return [ + DataObjectEvents::PRE_UPDATE => 'preUpdate', + DataObjectEvents::PRE_ADD => 'preUpdate', + ]; + } + + public function preUpdate(DataObjectEvent $dataObjectEvent) + { + $object = $dataObjectEvent->getObject(); + + if (!$object instanceof SluggableInterface) { + return; + } + + $sites = new Site\Listing(); + + foreach (Tool::getValidLanguages() as $language) { + $newSlugs = []; + $actualSlugs = []; + $name = $object->getNameForSlug($language); + + if (!$name) { + continue; + } + + $slug = sprintf( + '/%s/%s', + $language, + strtolower($this->slugger->slug($name, '-', $language)->toString()) + ); + + $newSlugs[] = new UrlSlug($slug, 0); + + foreach ($sites->getSites() as $site) { + $newSlugs[] = new UrlSlug($slug, $site->getId()); + } + + $existingSlugs = $object->getSlug($language); + + foreach ($newSlugs as $newSlug) { + $found = false; + + foreach ($existingSlugs as $existingSlug) { + if ($existingSlug->getSiteId() === $newSlug->getSiteId()) { + if ($existingSlug->getSlug() === $newSlug->getSlug()) { + $actualSlugs[] = $existingSlug; + } + else { + $newSlug->setPreviousSlug($existingSlug->getSlug()); + $actualSlugs[] = $newSlug; + + } + $found = true; + break; + } + } + + if (!$found) { + $actualSlugs[] = $newSlug; + } + } + + $object->setSlug($actualSlugs, $language); + } + } +} diff --git a/src/CoreShop/Bundle/PimcoreBundle/Resources/config/services.yml b/src/CoreShop/Bundle/PimcoreBundle/Resources/config/services.yml index f4314f87ca..81e11902ad 100755 --- a/src/CoreShop/Bundle/PimcoreBundle/Resources/config/services.yml +++ b/src/CoreShop/Bundle/PimcoreBundle/Resources/config/services.yml @@ -70,3 +70,15 @@ services: - '%coreshop.all.pimcore.admin.editmode_css%' tags: - { name: kernel.event_subscriber } + + CoreShop\Bundle\PimcoreBundle\EventListener\SluggableListener: + arguments: + - '@Symfony\Component\String\Slugger\SluggerInterface' + tags: + - { name: kernel.event_subscriber } + + CoreShop\Component\Pimcore\Slug\SluggableLinkGenerator: + public: true + arguments: + - '@Pimcore\Http\Request\Resolver\SiteResolver' + - '@Symfony\Component\HttpFoundation\RequestStack' diff --git a/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/admin-translations.yml b/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/admin-translations.yml index f7bc4749af..589aae259e 100644 --- a/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/admin-translations.yml +++ b/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/admin-translations.yml @@ -158,3 +158,13 @@ translations: languages: en: 'Depth' de: 'Tiefe' + + coreshop.product.slug: + languages: + en: 'Slug' + de: 'Slug' + + coreshop.category.slug: + languages: + en: 'Slug' + de: 'Slug' diff --git a/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopCategory.json b/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopCategory.json index d555ec8164..6d51ae0ab1 100644 --- a/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopCategory.json +++ b/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopCategory.json @@ -7,6 +7,7 @@ "allowVariants": false, "showVariants": false, "generateTypeDeclarations": true, + "linkGeneratorReference": "@CoreShop\\Component\\Pimcore\\Slug\\SluggableLinkGenerator", "layoutDefinitions": { "fieldtype": "panel", "labelWidth": 100, @@ -80,6 +81,27 @@ "invisible": false, "visibleGridView": false, "visibleSearch": false + }, + { + "fieldtype": "urlSlug", + "width": "", + "domainLabelWidth": null, + "action": "CoreShop\\Bundle\\FrontendBundle\\Controller\\CategoryController:detailSlugAction", + "availableSites": [], + "name": "slug", + "title": "coreshop.category.slug", + "tooltip": "", + "mandatory": false, + "noteditable": false, + "index": false, + "locked": false, + "style": "", + "permissions": null, + "datatype": "data", + "relationType": false, + "invisible": false, + "visibleGridView": false, + "visibleSearch": false } ], "name": "localizedfields", @@ -133,8 +155,6 @@ "icon": null, "previewUrl": null, "group": "CoreShop", - "generateTypeDeclarations": true, - "propertyVisibility": { "grid": { "id": true, diff --git a/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopProduct.json b/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopProduct.json index 65aed511dc..6c70e3c302 100644 --- a/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopProduct.json +++ b/src/CoreShop/Bundle/ProductBundle/Resources/install/pimcore/classes/CoreShopProduct.json @@ -7,6 +7,7 @@ "allowVariants": false, "showVariants": false, "generateTypeDeclarations": true, + "linkGeneratorReference": "@CoreShop\\Component\\Pimcore\\Slug\\SluggableLinkGenerator", "layoutDefinitions": { "fieldtype": "panel", "labelWidth": 100, @@ -109,6 +110,27 @@ "invisible": false, "visibleGridView": false, "visibleSearch": false + }, + { + "fieldtype": "urlSlug", + "width": "", + "domainLabelWidth": null, + "action": "CoreShop\\Bundle\\FrontendBundle\\Controller\\ProductController:detailSlugAction", + "availableSites": [], + "name": "slug", + "title": "coreshop.product.slug", + "tooltip": "", + "mandatory": false, + "noteditable": false, + "index": false, + "locked": false, + "style": "", + "permissions": null, + "datatype": "data", + "relationType": false, + "invisible": false, + "visibleGridView": false, + "visibleSearch": false } ], "name": "localizedfields", @@ -659,7 +681,6 @@ "icon": null, "previewUrl": null, "group": "CoreShop", - "generateTypeDeclarations": true, "propertyVisibility": { "grid": { diff --git a/src/CoreShop/Component/Pimcore/Slug/SluggableInterface.php b/src/CoreShop/Component/Pimcore/Slug/SluggableInterface.php new file mode 100644 index 0000000000..40d72fdcd2 --- /dev/null +++ b/src/CoreShop/Component/Pimcore/Slug/SluggableInterface.php @@ -0,0 +1,32 @@ +siteResolver = $siteResolver; + $this->requestStack = $requestStack; + } + + public function generate(Concrete $object, array $params = []): string + { + if (!$object instanceof SluggableInterface) { + throw new \InvalidArgumentException(sprintf('Object with Path "%s" must implement %s', + $object->getFullPath(), SluggableInterface::class)); + } + + $slugs = $object->getSlug($params['_locale'] ?? null); + $slug = null; + $site = $params['site'] ?? ( + $this->requestStack->getMainRequest() ? + $this->siteResolver->getSite($this->requestStack->getMainRequest()) : + null + ); + + foreach ($slugs as $possibleSlug) { + if ($possibleSlug->getSiteId() === ($site ? $site->getId() : 0)) { + $slug = $possibleSlug; + break; + } + } + + if (null === $slug) { + throw new \InvalidArgumentException(sprintf('No Valid Slug found for object "%s"', $object->getFullPath())); + } + + return $slug->getSlug(); + } +} diff --git a/src/CoreShop/Component/Product/Model/Category.php b/src/CoreShop/Component/Product/Model/Category.php index 8a74c0cb28..019864c2e9 100644 --- a/src/CoreShop/Component/Product/Model/Category.php +++ b/src/CoreShop/Component/Product/Model/Category.php @@ -34,6 +34,11 @@ public function hasChildCategories(): bool return count($this->getChildren()) > 0; } + public function getNameForSlug($language = null): ?string + { + return $this->getName($language); + } + public function getHierarchy(): array { $hierarchy = []; diff --git a/src/CoreShop/Component/Product/Model/CategoryInterface.php b/src/CoreShop/Component/Product/Model/CategoryInterface.php index b09159f34e..32754590b7 100644 --- a/src/CoreShop/Component/Product/Model/CategoryInterface.php +++ b/src/CoreShop/Component/Product/Model/CategoryInterface.php @@ -14,9 +14,10 @@ namespace CoreShop\Component\Product\Model; +use CoreShop\Component\Pimcore\Slug\SluggableInterface; use CoreShop\Component\Resource\Pimcore\Model\PimcoreModelInterface; -interface CategoryInterface extends PimcoreModelInterface +interface CategoryInterface extends PimcoreModelInterface, SluggableInterface { public function getName($language = null): ?string; diff --git a/src/CoreShop/Component/Product/Model/Product.php b/src/CoreShop/Component/Product/Model/Product.php index 521b4fb698..caae883f28 100644 --- a/src/CoreShop/Component/Product/Model/Product.php +++ b/src/CoreShop/Component/Product/Model/Product.php @@ -45,4 +45,9 @@ public function hasAdditionalUnitDefinitions(): bool { return $this->hasUnitDefinitions() && $this->getUnitDefinitions()->getAdditionalUnitDefinitions()->count() > 0; } + + public function getNameForSlug($language = null): ?string + { + return $this->getName($language); + } } diff --git a/src/CoreShop/Component/Product/Model/ProductInterface.php b/src/CoreShop/Component/Product/Model/ProductInterface.php index 9d5b392dd9..07a52c8fee 100644 --- a/src/CoreShop/Component/Product/Model/ProductInterface.php +++ b/src/CoreShop/Component/Product/Model/ProductInterface.php @@ -14,11 +14,12 @@ namespace CoreShop\Component\Product\Model; +use CoreShop\Component\Pimcore\Slug\SluggableInterface; use CoreShop\Component\Resource\Model\ToggleableInterface; use CoreShop\Component\Resource\Pimcore\Model\PimcoreModelInterface; use Pimcore\Model\Asset\Image; -interface ProductInterface extends PimcoreModelInterface, ToggleableInterface +interface ProductInterface extends PimcoreModelInterface, ToggleableInterface, SluggableInterface { public function getSku(): ?string; From 58f5413211257d6923215679a623b632364b7924 Mon Sep 17 00:00:00 2001 From: Dominik Pfaffenbauer Date: Wed, 14 Jul 2021 15:52:59 +0200 Subject: [PATCH 2/6] [Slug] default generate slugs and use instead of static routes for product and category - linter --- src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php | 2 +- .../Component/Pimcore/Slug/SluggableLinkGenerator.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php b/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php index 377607ac1a..5679105aff 100644 --- a/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php +++ b/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php @@ -85,7 +85,7 @@ public function iOpenCartSummaryPage(PaymentInterface $payment) /** * @var Response $result */ - foreach ($responses as $index => $result) { + foreach ($responses as $index => $result) {SluggableLinkGenerator Assert::eq( $result->getStatusCode(), 200, diff --git a/src/CoreShop/Component/Pimcore/Slug/SluggableLinkGenerator.php b/src/CoreShop/Component/Pimcore/Slug/SluggableLinkGenerator.php index a886345504..63be5accb9 100644 --- a/src/CoreShop/Component/Pimcore/Slug/SluggableLinkGenerator.php +++ b/src/CoreShop/Component/Pimcore/Slug/SluggableLinkGenerator.php @@ -40,8 +40,8 @@ public function generate(Concrete $object, array $params = []): string $slugs = $object->getSlug($params['_locale'] ?? null); $slug = null; $site = $params['site'] ?? ( - $this->requestStack->getMainRequest() ? - $this->siteResolver->getSite($this->requestStack->getMainRequest()) : + $this->requestStack->getMasterRequest() ? + $this->siteResolver->getSite($this->requestStack->getMasterRequest()) : null ); From 0679b2ab2cbb5f301f46c92ef723e92e8ed09698 Mon Sep 17 00:00:00 2001 From: Dominik Pfaffenbauer Date: Wed, 14 Jul 2021 15:55:56 +0200 Subject: [PATCH 3/6] [Slug] default generate slugs and use instead of static routes for product and category - revert --- src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php b/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php index 5679105aff..377607ac1a 100644 --- a/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php +++ b/src/CoreShop/Behat/Context/Ui/Domain/PaymentController.php @@ -85,7 +85,7 @@ public function iOpenCartSummaryPage(PaymentInterface $payment) /** * @var Response $result */ - foreach ($responses as $index => $result) {SluggableLinkGenerator + foreach ($responses as $index => $result) { Assert::eq( $result->getStatusCode(), 200, From dda487ac1430024a36ca866995e01d3b5aff3ea6 Mon Sep 17 00:00:00 2001 From: Dominik Pfaffenbauer Date: Thu, 15 Jul 2021 08:45:41 +0200 Subject: [PATCH 4/6] [Slug] default generate slugs and use instead of static routes for product and category - fixes for behat tests --- features/domain/category/category_link.feature | 2 +- features/domain/product/product_link.feature | 2 +- src/CoreShop/Behat/Context/Domain/LinkGeneratorContext.php | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/features/domain/category/category_link.feature b/features/domain/category/category_link.feature index 771f75a965..6c92377957 100644 --- a/features/domain/category/category_link.feature +++ b/features/domain/category/category_link.feature @@ -7,4 +7,4 @@ Feature: In order that a customer can visit the category page And the site has a category "Shirts" Scenario: Create URL for product - Then the generated url for object should be "/en/shop/shirts~c%id" + Then the generated url for object should be "/en/shirts" diff --git a/features/domain/product/product_link.feature b/features/domain/product/product_link.feature index d8bc6f096d..c0eae0ee09 100644 --- a/features/domain/product/product_link.feature +++ b/features/domain/product/product_link.feature @@ -8,4 +8,4 @@ Feature: In order that a customer can visit the product page Then the product "Shoe" should be priced at "100" Scenario: Create URL for product - Then the generated url for object should be "/en/shop/shoe~p%id" + Then the generated url for object should be "/en/shoe" diff --git a/src/CoreShop/Behat/Context/Domain/LinkGeneratorContext.php b/src/CoreShop/Behat/Context/Domain/LinkGeneratorContext.php index 3cd0d7eeab..85c0f4bd9d 100644 --- a/src/CoreShop/Behat/Context/Domain/LinkGeneratorContext.php +++ b/src/CoreShop/Behat/Context/Domain/LinkGeneratorContext.php @@ -37,7 +37,6 @@ public function __construct(SharedStorageInterface $sharedStorage, LinkGenerator public function theGeneratedUrlForObjectShouldBe(Concrete $object, $url) { $generatedUrl = $this->linkGenerator->generate($object, null, ['_locale' => 'en']); - $url = str_replace('%id', (string)$object->getId(), $url); Assert::eq( $generatedUrl, @@ -56,7 +55,6 @@ public function theGeneratedUrlForObjectShouldBe(Concrete $object, $url) public function theGeneratedUrlForObjectWithRouteShouldBe(Concrete $object, $routeName, $url) { $generatedUrl = $this->linkGenerator->generate($object, $routeName, ['_locale' => 'en']); - $url = str_replace('%id', (string)$object->getId(), $url); Assert::eq( $generatedUrl, From 48b99f6d7c72d0243c0424b063b7ffb286b749e3 Mon Sep 17 00:00:00 2001 From: Dominik Pfaffenbauer Date: Thu, 15 Jul 2021 12:11:44 +0200 Subject: [PATCH 5/6] [Slug] default generate slugs and use instead of static routes for product and category - try fixing command --- src/CoreShop/Behat/Context/Cli/InstallerContext.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/CoreShop/Behat/Context/Cli/InstallerContext.php b/src/CoreShop/Behat/Context/Cli/InstallerContext.php index ff817a490a..5390b0972c 100644 --- a/src/CoreShop/Behat/Context/Cli/InstallerContext.php +++ b/src/CoreShop/Behat/Context/Cli/InstallerContext.php @@ -107,10 +107,7 @@ public function commandSuccess() */ private function iExecuteCommandAndConfirm($name) { - try { - $this->tester->setInputs(['y']); - $this->tester->execute(['command' => $name]); - } catch (\Exception $e) { - } + $this->tester->setInputs(['y']); + $this->tester->execute(['command' => $name]); } } From 2bd57b26cd3701ae280e05902355d0a4e3efc40f Mon Sep 17 00:00:00 2001 From: Dominik Pfaffenbauer Date: Thu, 15 Jul 2021 13:22:05 +0200 Subject: [PATCH 6/6] [Slug] default generate slugs and use instead of static routes for product and category - change fixtures to more predictable create category and product names --- .../Bundle/CoreBundle/Faker/Commerce.php | 227 -------- .../Data/Demo/AbstractProductFixture.php | 539 +++++++++++++++++- .../Fixtures/Data/Demo/CategoryFixture.php | 29 +- 3 files changed, 547 insertions(+), 248 deletions(-) delete mode 100644 src/CoreShop/Bundle/CoreBundle/Faker/Commerce.php diff --git a/src/CoreShop/Bundle/CoreBundle/Faker/Commerce.php b/src/CoreShop/Bundle/CoreBundle/Faker/Commerce.php deleted file mode 100644 index dae7794d0e..0000000000 --- a/src/CoreShop/Bundle/CoreBundle/Faker/Commerce.php +++ /dev/null @@ -1,227 +0,0 @@ - [ - 'adjective' => [ - 'Small', - 'Ergonomic', - 'Rustic', - 'Intelligent', - 'Gorgeous', - 'Incredible', - 'Fantastic', - 'Practical', - 'Sleek', - 'Awesome', - 'Enormous', - 'Mediocre', - 'Synergistic', - 'Heavy Duty', - 'Lightweight', - 'Aerodynamic', - 'Durable', - ], - 'material' => [ - 'Steel', - 'Wooden', - 'Concrete', - 'Plastic', - 'Cotton', - 'Granite', - 'Rubber', - 'Leather', - 'Silk', - 'Wool', - 'Linen', - 'Marble', - 'Iron', - 'Bronze', - 'Copper', - 'Aluminum', - 'Paper', - ], - 'product' => [ - 'Chair', - 'Car', - 'Computer', - 'Gloves', - 'Pants', - 'Shirt', - 'Table', - 'Shoes', - 'Hat', - 'Plate', - 'Knife', - 'Bottle', - 'Coat', - 'Lamp', - 'Keyboard', - 'Bag', - 'Bench', - 'Clock', - 'Watch', - 'Wallet', - ], - ], - 'de' => [ - 'adjective' => [ - 'Klein', - 'Ergonomisch', - 'Rustikal', - 'Intelligent', - 'Herrlich', - 'Unglaublich', - 'Fantastisch', - 'Praktisch', - 'Glatt', - 'Genial', - 'Enorm', - 'Mittelmäßig', - 'Synergistisch', - 'Schwerlast', - 'Leicht', - 'Aerodynamisch', - 'Dauerhaft', - ], - 'material' => [ - 'Stahl', - 'Hölzern', - 'Beton', - 'Plastik', - 'Baumwolle', - 'Granit', - 'Gummi', - 'Leder', - 'Die Seide', - 'Wolle', - 'Leinen', - 'Marmor', - 'Eisen', - 'Bronze', - 'Kupfer', - 'Aluminium', - 'Papier', - ], - 'product' => [ - 'Stuhl', - 'Wagen', - 'Computer', - 'Handschuhe', - 'Hose', - 'Hemd', - 'Tabelle', - 'Schuhe', - 'Hut', - 'Teller', - 'Messer', - 'Flasche', - 'Mantel', - 'Lampe', - 'Tastatur', - 'Tasche', - 'Bank', - 'Uhr', - 'Uhr', - 'Brieftasche', - ], - ], - ]; - protected static $promotionCode = [ - 'adjective' => [ - 'Amazing', - 'Awesome', - 'Cool', - 'Good', - 'Great', - 'Incredible', - 'Killer', - 'Premium', - 'Special', - 'Stellar', - 'Sweet', - ], - 'noun' => ['Code', 'Deal', 'Discount', 'Price', 'Promo', 'Promotion', 'Sale', 'Savings'], - ]; - - public function promotionCode(int $digits = 6): string - { - return static::randomElement(static::$promotionCode['adjective']) - .static::randomElement(static::$promotionCode['noun']) - .$this->generator->randomNumber($digits, true); - } - - public function department(int $max = 3, bool $fixedAmount = false): string - { - $categories = []; - if (!$fixedAmount) { - $max = mt_rand(1, $max); - } - $uniqueGenerator = $this->generator->unique(true); - for ($i = 0; $i < $max; $i++) { - $categories[] = $uniqueGenerator->category; - } - if (count($categories) >= 2) { - $commaSeparatedCategories = implode(', ', array_slice($categories, 0, -1)); - $categories = [ - $commaSeparatedCategories, - end($categories), - ]; - } - - return implode(' & ', $categories); - } - - public function category(): string - { - return static::randomElement(static::$department); - } - - public function productName(?string $locale = null): string - { - return static::randomElement(static::$productName[$locale ?? 'en']['adjective']) - .' '.static::randomElement(static::$productName[$locale ?? 'en']['material']) - .' '.static::randomElement(static::$productName[$locale ?? 'en']['product']); - } -} diff --git a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php index 94a327d203..0b0ea690ff 100644 --- a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php +++ b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/AbstractProductFixture.php @@ -14,7 +14,6 @@ namespace CoreShop\Bundle\CoreBundle\Fixtures\Data\Demo; -use CoreShop\Bundle\CoreBundle\Faker\Commerce; use CoreShop\Bundle\FixtureBundle\Fixture\VersionedFixtureInterface; use CoreShop\Component\Core\Model\CategoryInterface; use CoreShop\Component\Core\Model\ProductInterface; @@ -23,7 +22,6 @@ use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Faker\Factory; use Faker\Provider\Barcode; -use Faker\Provider\Base; use Faker\Provider\Lorem; use Pimcore\Model\Asset; use Pimcore\Model\DataObject\Service; @@ -36,6 +34,509 @@ abstract class AbstractProductFixture extends AbstractFixture implements Contain { use ContainerAwareTrait; + protected static array $names = [ + [ + "de" => [ + "name" => "Schwerlast Leder Brieftasche", + "description" => "Minus voluptatem asperiores quod excepturi quis. Quod non amet explicabo praesentium similique minima numquam corporis.", + ], + "en" => [ + "name" => "Heavy Duty Leather Wallet", + "description" => "Debitis deserunt sunt voluptas neque voluptas molestias aut. Eligendi pariatur voluptatem voluptatum autem vel in. Quo labore modi facere qui earum aut. Nemo possimus est debitis.", + ], + ], + [ + "de" => [ + "name" => "Leicht Baumwolle Hose", + "description" => "Labore itaque et quia. Ad rerum reiciendis consequuntur. Incidunt totam aut qui voluptas. Nemo culpa dolorem ut eos suscipit.", + ], + "en" => [ + "name" => "Lightweight Cotton Pants", + "description" => "Voluptas minus eos eveniet beatae est repudiandae ut. Deleniti exercitationem id incidunt voluptas. Aut facere voluptatem aut possimus sed est. Sint quia voluptas quisquam earum.", + ], + ], + [ + "de" => [ + "name" => "Fantastisch Gummi Wagen", + "description" => "Sint id harum autem nisi optio sunt. Quis voluptatem et iure reiciendis dolores pariatur. Itaque dolore vel dolorum id sunt quasi aperiam. Quia id porro eveniet consequatur.", + ], + "en" => [ + "name" => "Fantastic Rubber Car", + "description" => "Sint doloremque itaque aut voluptate maiores ut. Odit delectus enim aut quis. Omnis expedita assumenda velit possimus recusandae animi deserunt. Placeat qui possimus rerum architecto architecto ut.", + ], + ], + [ + "de" => [ + "name" => "Enorm Papier Teller", + "description" => "Saepe vel earum soluta sit. Nam voluptatem non aliquam molestias explicabo suscipit qui. Dolorem aliquid et aut. Dicta similique totam culpa.", + ], + "en" => [ + "name" => "Enormous Paper Plate", + "description" => "Porro delectus rem labore beatae dicta ullam aut. Alias modi amet deleniti neque optio libero. Repellendus mollitia quod pariatur quod iure.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Aluminium Mantel", + "description" => "Dolores doloremque repudiandae et ducimus aspernatur ut. Illo eum expedita quibusdam minima vel. Sint iste ratione rerum voluptatem voluptatum et fuga aut. Qui in aspernatur aut est numquam non et.", + ], + "en" => [ + "name" => "Aerodynamic Aluminum Coat", + "description" => "Voluptatibus sit et quas eos. Eaque tenetur enim reprehenderit maxime aut consequatur velit ducimus. Ut eum qui esse commodi.", + ], + ], + [ + "de" => [ + "name" => "Genial Papier Schuhe", + "description" => "Inventore distinctio maiores ea animi non. Quasi tempora tenetur non. Non voluptatem libero possimus asperiores. Itaque consequatur autem consectetur sequi aut.", + ], + "en" => [ + "name" => "Awesome Paper Shoes", + "description" => "Aut quasi minus et aut incidunt. Ratione recusandae ipsa temporibus magnam quis molestiae. Placeat nihil aperiam alias et.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Beton Messer", + "description" => "Est iste suscipit est vero. Qui nam aut voluptate voluptatem error enim et. Ut dolores facilis reiciendis autem. Dicta commodi impedit non consequuntur quia aut accusamus.", + ], + "en" => [ + "name" => "Aerodynamic Concrete Knife", + "description" => "Dolores deleniti aut quisquam est aut. Vero ut omnis eligendi itaque consequuntur cum. Itaque repellat qui cum minus soluta consequatur.", + ], + ], + [ + "de" => [ + "name" => "Praktisch Baumwolle Tasche", + "description" => "Non asperiores aliquid ut est omnis. Eligendi vel eum aut aliquid tenetur. Quam amet facilis provident deserunt beatae maiores dolorum. Earum aut ut eligendi et in.", + ], + "en" => [ + "name" => "Practical Cotton Bag", + "description" => "At est totam debitis sit quo natus nulla voluptatibus. Magnam nisi atque corrupti. Voluptas est culpa eligendi eius eum.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Stahl Brieftasche", + "description" => "Aliquam consequuntur sed omnis tempore ut consequatur. Sed laboriosam ex nostrum nemo ad at aliquid consequatur.", + ], + "en" => [ + "name" => "Aerodynamic Steel Wallet", + "description" => "Sit corporis totam eos rerum qui. Enim cupiditate soluta reiciendis possimus dolor non. Reiciendis eos et error maxime dolorem quia et eaque.", + ], + ], + [ + "de" => [ + "name" => "Ergonomisch Papier Hut", + "description" => "Maiores tempora quo enim repellendus. Qui porro voluptates et quod. Et magni totam est est quos nobis ea. Nihil ducimus voluptas id nihil soluta.", + ], + "en" => [ + "name" => "Ergonomic Paper Hat", + "description" => "Similique pariatur perferendis illo voluptatibus velit cum. Et dolor natus ab modi ipsum magnam enim. Aliquam asperiores doloremque vero aut corporis quis.", + ], + ], + [ + "de" => [ + "name" => "Praktisch Eisen Handschuhe", + "description" => "Incidunt temporibus voluptatem et qui eos. Dolores placeat similique provident in quasi repellendus. Consequatur similique dolore vel repudiandae accusantium dolores error.", + ], + "en" => [ + "name" => "Practical Iron Gloves", + "description" => "Dolor vitae aut cum sequi voluptatem debitis vitae saepe. Est sint rerum facere vel. Amet ab voluptatem beatae consequatur libero et eligendi. Ut eos non pariatur.", + ], + ], + [ + "de" => [ + "name" => "Leicht Papier Messer", + "description" => "Reiciendis quo vel tempora facere unde ut et. A aut earum harum quasi animi. Architecto sunt eos dolores.", + ], + "en" => [ + "name" => "Lightweight Paper Knife", + "description" => "Et aut ut commodi vero aliquam. Voluptatem asperiores eaque iste quia praesentium est asperiores. Dolor et maxime illum quo error est et. Aut non qui unde eos.", + ], + ], + [ + "de" => [ + "name" => "Glatt Stahl Mantel", + "description" => "Sunt non voluptas rerum qui tenetur dolor repellat. Ipsam harum quod consequatur et qui non et. Ut consequatur fugiat error voluptas vero totam in nihil.", + ], + "en" => [ + "name" => "Sleek Steel Coat", + "description" => "Et commodi dolores animi quia et incidunt et. Animi qui amet eaque et. Repudiandae aut enim vel et facilis praesentium. Sit minima eaque distinctio alias. Omnis eum dolorem quis.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Beton Hemd", + "description" => "Nesciunt eos qui consectetur sint et accusamus possimus. Alias quis quasi qui vel est corporis consequuntur. Aspernatur facilis est ullam quia in et.", + ], + "en" => [ + "name" => "Aerodynamic Concrete Shirt", + "description" => "Saepe autem quia dicta nemo suscipit. Repellat labore sit quas quo. Itaque qui vitae sunt quis quasi qui.", + ], + ], + [ + "de" => [ + "name" => "Genial Gummi Flasche", + "description" => "Laudantium hic in et nobis. Culpa illum tenetur aliquid iure adipisci exercitationem a. Perspiciatis vero ut accusamus minus incidunt numquam error.", + ], + "en" => [ + "name" => "Awesome Rubber Bottle", + "description" => "Aut maxime sint similique quia. At maxime ea ab laudantium. Cum saepe odit sint eius earum est. Qui laudantium omnis est eligendi dolores ut.", + ], + ], + [ + "de" => [ + "name" => "Mittelm\u00e4\u00dfig Granit Wagen", + "description" => "Molestiae ut ut quo qui atque a placeat. Asperiores vel deserunt qui natus maxime. Esse ut hic autem et. Porro ducimus ducimus debitis et quo quam.", + ], + "en" => [ + "name" => "Mediocre Granite Car", + "description" => "Deleniti ab saepe voluptatem rerum amet sed. Et dolores aut est enim. Ea eaque eum suscipit et eius exercitationem. Dicta sapiente et eaque labore. Soluta laboriosam magni veniam quia est.", + ], + ], + [ + "de" => [ + "name" => "Herrlich Leder Uhr", + "description" => "Voluptas assumenda ratione impedit optio dolorem ea rerum. Qui in quidem cum consequatur natus eos quis.", + ], + "en" => [ + "name" => "Gorgeous Leather Clock", + "description" => "Cum sed in doloremque repellendus velit veniam accusantium fugiat. Aut ea est eos iusto sunt voluptate. Provident possimus ipsa alias earum velit dolor ipsam.", + ], + ], + [ + "de" => [ + "name" => "Synergistisch Stahl Lampe", + "description" => "Est maiores qui vel. Aut aut labore rerum qui architecto tempora. Consectetur rerum veritatis magnam in numquam quidem ex. Et et molestiae quaerat rerum.", + ], + "en" => [ + "name" => "Synergistic Steel Lamp", + "description" => "Et molestiae unde quo aliquid officiis omnis est. Veritatis voluptatem corporis accusantium qui molestiae sunt deleniti. Fuga tenetur molestias facere est et. Repellendus et ad omnis sunt sequi.", + ], + ], + [ + "de" => [ + "name" => "Intelligent Stahl Teller", + "description" => "Eius perspiciatis quia ipsam eum deserunt. Doloribus et quas architecto quia. Aut blanditiis sed nulla nesciunt.", + ], + "en" => [ + "name" => "Intelligent Steel Plate", + "description" => "Sunt et quia molestias aut impedit quisquam provident. Vel nisi id odio harum ducimus iusto autem. Veniam ut et tenetur cumque animi nisi accusantium.", + ], + ], + [ + "de" => [ + "name" => "Genial Die Seide Tastatur", + "description" => "Distinctio voluptatem amet enim accusamus possimus nihil atque. Aspernatur fugit ad excepturi perferendis corrupti nihil.", + ], + "en" => [ + "name" => "Awesome Silk Keyboard", + "description" => "Fuga quo excepturi iure dolor et eum assumenda. Omnis eum odio non tempora autem consequatur. Sed cum ut praesentium ut. Id est dignissimos magnam in dolores blanditiis.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Bronze Lampe", + "description" => "Impedit quisquam cumque voluptatum velit id. At maxime ipsam repellendus doloribus dolorem. Sit fugit sequi quis. Et veritatis animi culpa voluptates. Quibusdam hic id nemo quia veritatis dolorem.", + ], + "en" => [ + "name" => "Aerodynamic Bronze Lamp", + "description" => "Voluptatem sequi corrupti rerum nihil blanditiis repudiandae totam. Qui quia incidunt magni inventore voluptatem quisquam. Ipsam odio odio nemo fugiat tempora minus nisi.", + ], + ], + [ + "de" => [ + "name" => "Leicht Baumwolle Flasche", + "description" => "Velit et ea natus delectus dolore. Qui odio earum a at quisquam delectus. Autem excepturi magnam iusto rerum in. Voluptatem et ullam minus minus odit quos voluptas.", + ], + "en" => [ + "name" => "Lightweight Cotton Bottle", + "description" => "Aut voluptatibus voluptatem deserunt praesentium molestiae eveniet. Ex sed asperiores non tempore nihil. Rerum aut eos numquam mollitia enim provident.", + ], + ], + [ + "de" => [ + "name" => "Glatt Leder Bank", + "description" => "Consequatur saepe eius id labore. Accusamus nihil in excepturi aperiam est rerum. Earum voluptatem non saepe et consequuntur non eveniet. Eum qui earum dolores voluptas cum.", + ], + "en" => [ + "name" => "Sleek Leather Bench", + "description" => "Eum autem at cumque non. In nihil et vitae. Error sit fugiat impedit explicabo vero consequuntur quia. Numquam quos aut molestiae iure.", + ], + ], + [ + "de" => [ + "name" => "Mittelm\u00e4\u00dfig Leinen Mantel", + "description" => "Dignissimos magni adipisci cumque quia voluptatem ut. Pariatur consequatur labore aut omnis sit. Non dolorum ut officia qui.", + ], + "en" => [ + "name" => "Mediocre Linen Coat", + "description" => "Facere velit aut eum vel dolor. Animi deleniti cum quia sit et voluptatem. Excepturi inventore quia voluptas voluptatem accusantium labore iusto. Quo fugit eaque sunt error qui sed.", + ], + ], + [ + "de" => [ + "name" => "Dauerhaft Eisen Hose", + "description" => "Accusamus quam sit sequi quo rerum ipsam. Ex enim molestiae dolores repudiandae quas dolor. Inventore odio ipsam quo quibusdam perferendis similique eos maiores.", + ], + "en" => [ + "name" => "Durable Iron Pants", + "description" => "Autem quis laudantium odit sed repellat aut. Deleniti ratione itaque quaerat eum vel alias. Esse dicta odit facilis eius.", + ], + ], + [ + "de" => [ + "name" => "Herrlich Marmor Brieftasche", + "description" => "Delectus labore sit alias voluptas. Quo doloribus quod mollitia deleniti laboriosam. Culpa laudantium nulla accusamus id qui ut. A omnis voluptatem omnis qui.", + ], + "en" => [ + "name" => "Gorgeous Marble Wallet", + "description" => "Neque dolores qui ut officiis explicabo voluptatum qui. Fugit tenetur id sapiente esse aut eligendi et facere. Sed quos quia molestiae odit.", + ], + ], + [ + "de" => [ + "name" => "Dauerhaft Aluminium Bank", + "description" => "Facere architecto ullam aut. Sapiente qui mollitia minus iure omnis impedit praesentium. Debitis ea vero est doloribus laboriosam ex tempora. Natus odio ipsam magni voluptas id voluptatibus.", + ], + "en" => [ + "name" => "Durable Aluminum Bench", + "description" => "Aut perspiciatis occaecati dolores quia nostrum sint. Harum natus est saepe laboriosam accusamus dolorem.", + ], + ], + [ + "de" => [ + "name" => "Intelligent H\u00f6lzern Computer", + "description" => "Nobis modi aspernatur non id esse accusantium. Qui impedit delectus quia eum eligendi quas.", + ], + "en" => [ + "name" => "Intelligent Wooden Computer", + "description" => "Ab sunt est voluptatibus veniam. Est possimus quia fugiat id perferendis repellat non soluta. Eaque sequi velit sint ipsum.", + ], + ], + [ + "de" => [ + "name" => "Dauerhaft Leder Computer", + "description" => "Earum laudantium voluptates culpa ut delectus est doloribus dolores. Ullam iure et iure impedit voluptate sit error.", + ], + "en" => [ + "name" => "Durable Leather Computer", + "description" => "Corporis quibusdam autem necessitatibus dolorem unde totam cum temporibus. Ipsa quam qui et asperiores autem quam consequatur. Est eos dolores est laboriosam sit.", + ], + ], + [ + "de" => [ + "name" => "Ergonomisch Baumwolle Wagen", + "description" => "Consectetur labore est consectetur beatae cumque dignissimos. Velit tenetur error maiores et veniam. Quia impedit quasi doloribus quos omnis delectus sit quia.", + ], + "en" => [ + "name" => "Ergonomic Cotton Car", + "description" => "Voluptatem quos eaque esse doloremque. Neque rerum eum mollitia nam. Quo facere vitae et odio molestiae.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Aluminium Uhr", + "description" => "Magnam natus non eos optio perspiciatis nisi. Nihil voluptates et iste eius vel possimus. Quasi fugit fugiat illum labore debitis. Quae possimus provident non recusandae sint.", + ], + "en" => [ + "name" => "Aerodynamic Aluminum Clock", + "description" => "Beatae voluptatem quisquam aut omnis perspiciatis. Maiores ut et omnis et est. Nostrum dolorum molestias perspiciatis porro sit porro perspiciatis.", + ], + ], + [ + "de" => [ + "name" => "Enorm H\u00f6lzern Mantel", + "description" => "Temporibus consequuntur vero debitis omnis numquam est. Qui et blanditiis perferendis facere eaque sapiente voluptas. Sed iste tempora nulla minus ipsa.", + ], + "en" => [ + "name" => "Enormous Wooden Coat", + "description" => "Cumque voluptas nemo voluptatem non ut repudiandae esse. Doloribus molestiae laudantium quidem voluptas. Et numquam suscipit aliquid nihil enim corrupti.", + ], + ], + [ + "de" => [ + "name" => "Mittelm\u00e4\u00dfig Marmor Uhr", + "description" => "Culpa et dolor qui recusandae sint deserunt dolorem. Mollitia qui nisi dolores earum pariatur quo. Sint eligendi accusamus eius in.", + ], + "en" => [ + "name" => "Mediocre Marble Watch", + "description" => "Sed odio autem inventore sint. Est temporibus minima odit et nulla ipsam et molestias. Quas ut et dolore et officiis ullam.", + ], + ], + [ + "de" => [ + "name" => "Glatt Papier Wagen", + "description" => "Hic perferendis et sunt consequatur. Corrupti est quo eum sunt aut aut. Quia provident doloribus aliquid vero quod adipisci molestias minima.", + ], + "en" => [ + "name" => "Sleek Paper Car", + "description" => "Quis rerum eligendi dolor neque. A commodi illum consequuntur. Repellendus aliquam molestias et expedita quia.", + ], + ], + [ + "de" => [ + "name" => "Unglaublich Aluminium Hut", + "description" => "Incidunt laudantium reiciendis et consequuntur officiis. Iste sint consequatur rem qui qui officia. Voluptas et dolorem quia omnis est officiis voluptates.", + ], + "en" => [ + "name" => "Incredible Aluminum Hat", + "description" => "Ut itaque alias aspernatur aliquam. Dolor ullam porro magni doloribus corporis illo molestiae. Aliquam est possimus nihil eos reiciendis ex. Laborum harum tempora et autem possimus aut eum id.", + ], + ], + [ + "de" => [ + "name" => "Ergonomisch Eisen Lampe", + "description" => "Et qui soluta et velit et iste. Sapiente ut pariatur neque tempore libero placeat. Adipisci dolor consequatur architecto ut in optio. Voluptatem numquam id suscipit eum consequuntur voluptatem.", + ], + "en" => [ + "name" => "Ergonomic Iron Lamp", + "description" => "Ipsum consectetur aut perspiciatis eum. Nulla at id eveniet ipsa sapiente. Dicta temporibus enim in fugit quia qui molestias. Id aut rem omnis similique at est fugiat fuga.", + ], + ], + [ + "de" => [ + "name" => "Intelligent H\u00f6lzern Handschuhe", + "description" => "Veritatis quia unde ab omnis est dolorem qui beatae. Expedita sit qui eveniet aliquid. Officiis cumque similique nulla perspiciatis. Quaerat exercitationem rerum cumque consequuntur et quasi.", + ], + "en" => [ + "name" => "Intelligent Wooden Gloves", + "description" => "Cumque ex voluptatem fugit esse. Et illo enim odit. Minima sit molestiae autem omnis soluta. Commodi vitae porro dolor nemo.", + ], + ], + [ + "de" => [ + "name" => "Klein Papier Hemd", + "description" => "Ratione quia eum cum esse non et. Asperiores ratione pariatur adipisci in laudantium. Quos est quia dicta velit soluta quidem. Recusandae qui nisi in deserunt non.", + ], + "en" => [ + "name" => "Small Paper Shirt", + "description" => "Esse nemo maiores ea deserunt ut quasi. Ut similique voluptatum sit accusamus. Reiciendis sint hic ut consequatur dolorem. Voluptates tempore placeat optio quia soluta expedita repellat.", + ], + ], + [ + "de" => [ + "name" => "Genial Gummi Uhr", + "description" => "Omnis blanditiis itaque ipsa ut. Eum rerum magni facere non laudantium recusandae dolorem. Ipsa illo voluptatem quis.", + ], + "en" => [ + "name" => "Awesome Rubber Watch", + "description" => "Sint voluptatem accusamus inventore repellendus voluptatem nam. Aspernatur sit veritatis beatae et. Praesentium perspiciatis sunt quia eum sed nesciunt. Eaque beatae quibusdam sit natus.", + ], + ], + [ + "de" => [ + "name" => "Synergistisch Bronze Flasche", + "description" => "Tempora atque sint ipsa vero et quidem quis. Quia asperiores veritatis optio. Ea est sed quia placeat culpa labore doloribus.", + ], + "en" => [ + "name" => "Synergistic Bronze Bottle", + "description" => "Et repellat eveniet fuga veniam. Similique magnam quod unde velit perspiciatis. Aspernatur ea iure aut quas id velit. Atque eaque consectetur et non aliquid repellat sunt.", + ], + ], + [ + "de" => [ + "name" => "Fantastisch Baumwolle Messer", + "description" => "Pariatur nesciunt sint voluptatem. Itaque non cumque voluptas ea rerum quibusdam vel. Quo dolor rem nobis cum. Blanditiis temporibus ad eos quasi.", + ], + "en" => [ + "name" => "Fantastic Cotton Knife", + "description" => "Praesentium sunt qui nihil laboriosam rerum. Quis et nemo dolor ipsa. Sapiente quasi aut provident voluptas qui ipsam quae ut. Quo ducimus est dolorum ratione eos.", + ], + ], + [ + "de" => [ + "name" => "Dauerhaft Aluminium Stuhl", + "description" => "Ea sequi tenetur aut quasi necessitatibus nemo. Qui sequi ut ducimus officiis quae. Eos sit accusamus modi eligendi id ut id sint.", + ], + "en" => [ + "name" => "Durable Aluminum Chair", + "description" => "Quibusdam distinctio exercitationem nobis corrupti et eius. Rem commodi enim qui commodi. Laboriosam quis vel dolorem adipisci. Rerum incidunt earum omnis quia. Laudantium in necessitatibus illum.", + ], + ], + [ + "de" => [ + "name" => "Dauerhaft Kupfer Computer", + "description" => "Perspiciatis rerum aut eos quibusdam distinctio aut. Corrupti fugiat animi a eos non quia quisquam. Dolorem consequuntur et sit ut.", + ], + "en" => [ + "name" => "Durable Copper Computer", + "description" => "Quia ut nihil ipsum quo sint vel. Nemo nemo sit illum eum accusamus molestias dolor autem.", + ], + ], + [ + "de" => [ + "name" => "Leicht Stahl Computer", + "description" => "Tempore expedita beatae at adipisci. Quam qui aut blanditiis iure exercitationem et.", + ], + "en" => [ + "name" => "Lightweight Steel Computer", + "description" => "Mollitia tenetur iure error soluta adipisci vitae praesentium. Iure minus veritatis sed doloremque ut maiores necessitatibus. Quia non id possimus eum reprehenderit.", + ], + ], + [ + "de" => [ + "name" => "Genial Marmor Uhr", + "description" => "Deserunt a molestiae hic itaque est cumque. Iusto aspernatur atque laborum doloribus. Rerum repudiandae quidem sit et blanditiis odio velit.", + ], + "en" => [ + "name" => "Awesome Marble Clock", + "description" => "Et esse dolores aliquid suscipit autem. Repudiandae laborum sapiente odio excepturi voluptatem temporibus. Quia vel sed quia dolorem. Vel ut ut reprehenderit accusamus dolorum ducimus numquam.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Leinen Brieftasche", + "description" => "Tenetur dolor est iusto voluptatem. Praesentium explicabo occaecati asperiores et. Eligendi aperiam qui sapiente corporis quia. Velit aut dolore repellat officia ipsum.", + ], + "en" => [ + "name" => "Aerodynamic Linen Wallet", + "description" => "Voluptatem rem et sunt dicta esse. Dolorem reprehenderit iusto velit vel suscipit deserunt aut. Neque eum beatae assumenda. Labore omnis vero sed exercitationem in.", + ], + ], + [ + "de" => [ + "name" => "Unglaublich Leinen Schuhe", + "description" => "Vel consequuntur dolor laborum non. Et alias architecto ut iure repellat laudantium dolorum. Quo consequuntur eos corporis vel. Saepe dolor minus nesciunt. Ut ducimus fugit a.", + ], + "en" => [ + "name" => "Incredible Linen Shoes", + "description" => "Quidem tenetur ullam rerum quia et totam. Dolorum sed ut sit ex molestiae ut sit molestiae. Repudiandae velit illo dicta fugit vel necessitatibus.", + ], + ], + [ + "de" => [ + "name" => "Schwerlast H\u00f6lzern Mantel", + "description" => "Nostrum aperiam adipisci debitis ut in error. Dolore nulla sunt sint dolor modi. Atque in facilis mollitia. Mollitia praesentium sit ea.", + ], + "en" => [ + "name" => "Heavy Duty Wooden Coat", + "description" => "Sed non voluptatem alias vitae. Voluptatibus corrupti voluptate perferendis iure ut voluptatem aut iusto. Rerum quia deleniti fugiat in vero.", + ], + ], + [ + "de" => [ + "name" => "Klein Papier Hose", + "description" => "Ut pariatur et voluptatem fugit. Vitae rerum ut sapiente dolorem. Quaerat ullam libero est. Qui quidem vel et totam. Et distinctio autem perferendis.", + ], + "en" => [ + "name" => "Small Paper Pants", + "description" => "Ut officiis et voluptas similique. Eligendi modi expedita quis corrupti. Nemo itaque voluptas possimus nesciunt quia. Et nihil animi quo enim velit aut nemo.", + ], + ], + [ + "de" => [ + "name" => "Aerodynamisch Die Seide Brieftasche", + "description" => "Unde perferendis aperiam nihil. Necessitatibus perferendis nihil ut quo voluptatem. A laborum sed neque. Corrupti blanditiis architecto ratione numquam aut dolorem laudantium quo.", + ], + "en" => [ + "name" => "Aerodynamic Silk Wallet", + "description" => "Sit eligendi totam sunt laudantium libero consectetur. Et error et veniam ea culpa provident pariatur.", + ], + ], + ]; + public function getVersion(): string { return '2.0'; @@ -51,15 +552,14 @@ public function getDependencies(): array protected function createProduct(string $parentPath): ProductInterface { - $fakerDE = Factory::create('de_DE'); - $fakerDE->addProvider(new Lorem($fakerDE)); - $fakerDE->addProvider(new Barcode($fakerDE)); - $fakerDE->addProvider(new Commerce($fakerDE)); + $index = array_rand(static::$names); + $name = static::$names[$index]; + + unset(static::$names[$index]); $faker = Factory::create(); $faker->addProvider(new Lorem($faker)); $faker->addProvider(new Barcode($faker)); - $faker->addProvider(new Commerce($faker)); $decimalFactor = $this->container->getParameter('coreshop.currency.decimal_factor'); @@ -75,16 +575,22 @@ protected function createProduct(string $parentPath): ProductInterface /** * @var CategoryInterface $usedCategory */ - $usedCategory = $categories[rand(0, count($categories) - 1)]; - $folder = \Pimcore\Model\Asset\Service::createFolderByPath(sprintf('/demo/%s/%s', $parentPath, Service::getValidKey($usedCategory->getName(), 'asset'))); + $usedCategory = $categories[random_int(0, count($categories) - 1)]; + $folder = \Pimcore\Model\Asset\Service::createFolderByPath(sprintf('/demo/%s/%s', $parentPath, + Service::getValidKey($usedCategory->getName(), 'asset'))); $images = []; for ($j = 0; $j < 3; $j++) { - $imagePath = $kernel->locateResource(sprintf('@CoreShopCoreBundle/Resources/fixtures/image%s.jpeg', rand(1, 3))); + $imagePath = $kernel->locateResource( + sprintf( + '@CoreShopCoreBundle/Resources/fixtures/image%s.jpeg', + random_int(1, 3) + ) + ); $fileName = sprintf('image_%s.jpg', uniqid()); - $fullPath = $folder->getFullPath() . '/' . $fileName; + $fullPath = $folder->getFullPath().'/'.$fileName; $existingImage = Asset::getByPath($fullPath); @@ -107,12 +613,12 @@ protected function createProduct(string $parentPath): ProductInterface */ $product = $this->container->get('coreshop.factory.product')->createNew(); foreach (Tool::getValidLanguages() as $language) { - $product->setName($faker->productName($language), $language); + $product->setName($name[$language]['name'] ?? $name['en']['name'], $language); - $product->setShortDescription($language === 'de' ? $fakerDE->text() : $faker->text(), $language); + $product->setShortDescription($name[$language]['description'] ?? $name['en']['description'], $language); + $product->setDescription(implode('
', $faker->paragraphs(3)), $language); } $product->setSku($faker->ean13); - $product->setDescription(implode('
', $faker->paragraphs(3))); $product->setEan($faker->ean13); $product->setActive(true); $product->setCategories([$usedCategory]); @@ -120,7 +626,7 @@ protected function createProduct(string $parentPath): ProductInterface // $product->setWholesalePrice((int)($faker->randomFloat(2, 100, 200) * $decimalFactor)); foreach ($stores as $store) { - $product->setStoreValuesOfType('price', (int) ($faker->randomFloat(2, 200, 400) * $decimalFactor), $store); + $product->setStoreValuesOfType('price', (int)($faker->randomFloat(2, 200, 400) * $decimalFactor), $store); } $product->setTaxRule($this->getReference('taxRule')); @@ -130,7 +636,8 @@ protected function createProduct(string $parentPath): ProductInterface $product->setWeight($faker->numberBetween(5, 10)); $product->setImages($images); $product->setStores([$defaultStore]); - $product->setParent($this->container->get(ObjectServiceInterface::class)->createFolderByPath(sprintf('/demo/%s/%s', $parentPath, Service::getValidKey($usedCategory->getName(), 'object')))); + $product->setParent($this->container->get(ObjectServiceInterface::class)->createFolderByPath(sprintf('/demo/%s/%s', + $parentPath, Service::getValidKey($usedCategory->getName(), 'object')))); $product->setPublished(true); $product->setKey($product->getName()); $product->setKey(Service::getUniqueKey($product)); diff --git a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php index 7c7fe5234e..ff2cecd48d 100644 --- a/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php +++ b/src/CoreShop/Bundle/CoreBundle/Fixtures/Data/Demo/CategoryFixture.php @@ -14,13 +14,11 @@ namespace CoreShop\Bundle\CoreBundle\Fixtures\Data\Demo; -use CoreShop\Bundle\CoreBundle\Faker\Commerce; use CoreShop\Bundle\FixtureBundle\Fixture\VersionedFixtureInterface; use CoreShop\Component\Core\Model\CategoryInterface; use CoreShop\Component\Pimcore\DataObject\ObjectServiceInterface; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Persistence\ObjectManager; -use Faker\Factory; use Pimcore\Model\DataObject\Service; use Pimcore\Tool; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -42,10 +40,31 @@ public function setContainer(ContainerInterface $container = null): void public function load(ObjectManager $manager): void { + $names = [ + [ + 'de' => 'Bücher', + 'en' => 'Books', + ], + [ + 'de' => 'Computer', + 'en' => 'Computer', + ], + [ + 'de' => 'Filme', + 'en' => 'Movies', + ], + [ + 'de' => 'Kleidung', + 'en' => 'Clothing', + ], + [ + 'de' => 'Schuhe', + 'en' => 'Shoes', + ], + ]; + if (!count($this->container->get('coreshop.repository.category')->findAll())) { $categoriesCount = 5; - $faker = Factory::create(); - $faker->addProvider(new Commerce($faker)); for ($i = 0; $i < $categoriesCount; $i++) { /** @@ -54,7 +73,7 @@ public function load(ObjectManager $manager): void $category = $this->container->get('coreshop.factory.category')->createNew(); foreach (Tool::getValidLanguages() as $language) { - $category->setName($faker->department, $language); + $category->setName($names[$i][$language] ?? $names[$i]['en'], $language); } $category->setParent($this->container->get(ObjectServiceInterface::class)->createFolderByPath('/demo/categories'));