diff --git a/.docker/php@7.2/Dockerfile b/.docker/php@7.2/Dockerfile new file mode 100644 index 0000000..6841e58 --- /dev/null +++ b/.docker/php@7.2/Dockerfile @@ -0,0 +1,88 @@ +FROM php:7.2-cli-alpine + +LABEL maintainer="Grégory Planchat " + +ARG APP_UID=1000 +ARG APP_GID=1000 +ARG APP_USERNAME=docker +ARG APP_GROUPNAME=docker + +RUN set -ex\ + && apk update \ + && apk upgrade \ + && echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ + && apk add \ + shadow@testing \ + ca-certificates \ + wget \ + autoconf \ + bash \ + binutils \ + expat \ + file \ + g++ \ + gcc \ + m4 \ + make \ + git \ + nodejs \ + npm \ + && update-ca-certificates + +RUN docker-php-ext-install opcache + +RUN apk add --update icu-dev icu \ + && docker-php-ext-configure intl \ + && docker-php-ext-install intl \ + && apk del icu-dev + +RUN apk del \ + autoconf \ + bash \ + binutils \ + expat \ + file \ + g++ \ + gcc \ + gdbm \ + gmp \ + isl \ + libatomic \ + libbz2 \ + libc-dev \ + libffi \ + libgcc \ + libgomp \ + libldap \ + libltdl \ + libmagic \ + libstdc++ \ + libtool \ + m4 \ + make \ + mpc1 \ + mpfr3 \ + musl-dev \ + perl \ + pkgconf \ + pkgconfig \ + python \ + re2c \ + readline \ + sqlite-libs \ + && rm -rf /tmp/* /var/cache/apk/* + +RUN addgroup -g ${APP_GID} ${APP_USERNAME} \ + && adduser -u ${APP_UID} -h /opt/${APP_USERNAME} -H -G ${APP_GROUPNAME} -s /sbin/nologin -D ${APP_USERNAME} + +COPY config/memory.ini /usr/local/etc/php/conf.d/memory.ini + +RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ + && php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \ + && php composer-setup.php --install-dir /usr/local/bin --filename composer\ + && php -r "unlink('composer-setup.php');" + +RUN mkdir -p /opt/docker/.npm \ + && chown docker:docker /opt/docker/.npm + +WORKDIR /app diff --git a/.docker/php@7.2/config/memory.ini b/.docker/php@7.2/config/memory.ini new file mode 100644 index 0000000..b0fe7fe --- /dev/null +++ b/.docker/php@7.2/config/memory.ini @@ -0,0 +1 @@ +memory_limit=-1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..90152ca --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +Akeneo Fixtures Generation Toolbox +================================== + +This classes and templates toolbox helps generating CSV fixtues from Magento 1.9CE or 1.14EE catalog. + +Example +------- + +```php +#!/ur/bin/env php +addExtension(new TwigExtension()); + +$locales = [ + $fr_FR = new Locale\Locale('fr_FR', new MagentoStore(1)), + $de_DE = new Locale\Locale('de_DE', new MagentoStore(2)), + $en_GB = new Locale\Locale('en_GB', new MagentoStore(2)), + $en_US = new Locale\Locale('en_US', new MagentoStore(4)), + $ja_JP = new Locale\Locale('ja_JP', new MagentoStore(5)), + $fr_CA = new Locale\Locale('fr_CA', new MagentoStore(8)), +]; + +$scopes = [ + new Scope\Scope( + 'europe', + new MagentoStore(1), + $fr_FR, + $de_DE, + $en_GB + ), + new Scope\Scope( + 'america', + new MagentoStore(4), + $en_US, + $fr_CA, + new Locale\LocaleMapping($en_US, new MagentoStore(9)) // Additional locale mapping for Canada + ), + new Scope\Scope( + 'asia', + new MagentoStore(5), + $ja_JP, + new Locale\LocaleMapping($en_GB, new MagentoStore(6)) // Additional locale mapping for Hong Kong + ), +]; + +$globalized = new FieldResolver\Globalised(); +$localized = new FieldResolver\Localized(...$locales); +$scoped = new FieldResolver\Scoped(...$scopes); +$scopedAndLocalized = new FieldResolver\ScopedAndLocalized(...$scopes); +$axis = new FieldResolver\VariantAxis(); + +$renderer = new Renderer( + 'initialize.sql.twig', + 'finalize-product-parents.sql.twig', + ['configurable'], + // 1st level Product Models + new AttributeRenderer\Image( + new Attribute\AdHoc('image'), + $scoped + ), + new AttributeRenderer\Varchar( + new Attribute\AdHoc('name'), + $scopedAndLocalized + ), + new AttributeRenderer\Text( + new Attribute\AdHoc('description'), + $scopedAndLocalized + ), + new AttributeRenderer\Text( + new Attribute\AdHoc('short_description'), + $scopedAndLocalized + ), + new AttributeRenderer\Varchar( + new Attribute\AdHoc('meta_title'), + $scopedAndLocalized + ), + new AttributeRenderer\Text( + new Attribute\AdHoc('meta_description'), + $scopedAndLocalized + ), + new AttributeRenderer\SimpleSelect( + new Attribute\AdHoc('model'), + $scoped + ), + new AttributeRenderer\SimpleSelect( + new Attribute\AdHoc('manufacturer'), + $scoped + ) +); + +$renderer(fopen('products_models1.sql', 'w'), $twig); + +$renderer = new Renderer( + 'initialize.sql.twig', + 'finalize-product-children.sql.twig', + [], + // 2nd level Product Models + new AttributeRenderer\SimpleSelect( + new Attribute\AdHoc('color'), + $axis + ) +); + +$renderer(fopen('products_models2.sql', 'w'), $twig); + +$renderer = new Renderer( + 'initialize.sql.twig', + 'finalize-products.sql.twig', + ['simple', 'virtual'], + // Product & Product variants + new AttributeRenderer\Status( + new Attribute\AdHoc('status'), + $scopedAndLocalized + ), + new AttributeRenderer\Visibility( + new Attribute\AdHoc('visibility'), + $scopedAndLocalized + ), + new AttributeRenderer\SimpleSelect( + new Attribute\AdHoc('size'), + $axis + ), + new AttributeRenderer\Image( + new Attribute\Aliased('image', 'variation_image'), + $scoped + ), + new AttributeRenderer\Varchar( + new Attribute\Aliased('name', 'variation_name'), + $scopedAndLocalized + ), + new AttributeRenderer\Text( + new Attribute\Aliased('description', 'variation_description'), + $scopedAndLocalized + ), + new AttributeRenderer\Datetime( + new Attribute\AdHoc('news_from_date'), + $scopedAndLocalized + ), + new AttributeRenderer\Datetime( + new Attribute\AdHoc('news_to_date'), + $scopedAndLocalized + ) +); + +$renderer(fopen('products.sql', 'w'), $twig); +``` \ No newline at end of file diff --git a/bin/sql-to-csv b/bin/sql-to-csv new file mode 100755 index 0000000..04a8b90 --- /dev/null +++ b/bin/sql-to-csv @@ -0,0 +1,44 @@ +#!/usr/bin/env php + \PDO::ERRMODE_EXCEPTION, + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', + ]); + + for ($i = 4; $i < $argc; ++$i) { + $requests = array_filter(explode(';', file_get_contents($argv[$i])), function($request) { + return !empty(trim($request)); + }); + + foreach ($requests as $request) { + $pdo->exec($request); + } + } + + $statement = $pdo->prepare($request = file_get_contents('php://stdin')); + $statement->setFetchMode(PDO::FETCH_ASSOC); + $statement->execute(); +} catch (\PDOException $e) { + file_put_contents('php://stderr', var_export($request, true) . PHP_EOL); + file_put_contents('php://stderr', var_export($e->errorInfo, true) . PHP_EOL); + file_put_contents('php://stderr', $e); + return -1; +} catch (\Error $e) { + file_put_contents('php://stderr', $e); + return -1; +} + +$file = new SplFileObject('php://stdout', 'w'); +$file->setCsvControl(';'); + +$i = 0; +foreach ($statement as $index => $row) { + if ($index === 0) { + $file->fputcsv(array_keys($row)); + } + + $file->fputcsv($row); + (++$i % 100) || ob_flush(); +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4203311 --- /dev/null +++ b/composer.json @@ -0,0 +1,14 @@ +{ + "name": "kiboko/akeneo-initialize-from-magento", + "type": "project", + "require": { + "php": "^7.2", + "twig/twig": "^2.10" + }, + "minimum-stability": "stable", + "autoload": { + "psr-4": { + "Kiboko\\Bridge\\Akeneo\\Magento\\": "src/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..935437b --- /dev/null +++ b/composer.lock @@ -0,0 +1,204 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b34b0e5e432d8eb831289b9077e6c65d", + "packages": [ + { + "name": "symfony/polyfill-ctype", + "version": "v1.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-08-06T08:03:45+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2019-08-06T08:03:45+00:00" + }, + { + "name": "twig/twig", + "version": "v2.11.3", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "699ed2342557c88789a15402de5eb834dedd6792" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/699ed2342557c88789a15402de5eb834dedd6792", + "reference": "699ed2342557c88789a15402de5eb834dedd6792", + "shasum": "" + }, + "require": { + "php": "^7.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/debug": "^2.7", + "symfony/phpunit-bridge": "^3.4.19|^4.1.8|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.11-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "https://twig.symfony.com/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "time": "2019-06-18T15:37:11+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.2" + }, + "platform-dev": [] +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5d07a31 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: '2' + +services: + cli: + build: + context: .docker/php@7.2 + volumes: + - ./.docker/php@7.2/config/memory.ini:/usr/local/etc/php/conf.d/memory.ini:ro + - ./:/app + composer: + extends: + service: cli + volumes: + - composer:/opt/docker/.composer/:cached + entrypoint: [ 'composer' ] + +volumes: + composer: + driver: local + driver_opts: + type: tmpfs + device: tmpfs + o: "size=2048m,uid=1000,gid=1000" diff --git a/src/Attribute.php b/src/Attribute.php new file mode 100644 index 0000000..781ef03 --- /dev/null +++ b/src/Attribute.php @@ -0,0 +1,9 @@ +code = $code; + } + + public function codeInSource(): string + { + return $this->code; + } + + public function codeInDestination(): string + { + return $this->code; + } +} \ No newline at end of file diff --git a/src/Attribute/Aliased.php b/src/Attribute/Aliased.php new file mode 100644 index 0000000..6a3aaea --- /dev/null +++ b/src/Attribute/Aliased.php @@ -0,0 +1,29 @@ +code = $code; + $this->alias = $alias; + } + + public function codeInSource(): string + { + return $this->code; + } + + public function codeInDestination(): string + { + return $this->alias; + } +} \ No newline at end of file diff --git a/src/Attribute/ExNihilo.php b/src/Attribute/ExNihilo.php new file mode 100644 index 0000000..04a98b5 --- /dev/null +++ b/src/Attribute/ExNihilo.php @@ -0,0 +1,26 @@ +code = $code; + } + + public function codeInSource(): string + { + return $this->code; + } + + public function codeInDestination(): string + { + return $this->code; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer.php b/src/AttributeRenderer.php new file mode 100644 index 0000000..a388b4d --- /dev/null +++ b/src/AttributeRenderer.php @@ -0,0 +1,15 @@ +attribute = $attribute; + + if ($fieldResolver instanceof VariantAxis) { + throw new \TypeError('Could not accept a VariantAxis renderer in a Datetime attrinute.'); + } + + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'datetime'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return false; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer/Image.php b/src/AttributeRenderer/Image.php new file mode 100644 index 0000000..1ddd693 --- /dev/null +++ b/src/AttributeRenderer/Image.php @@ -0,0 +1,67 @@ +attribute = $attribute; + + if ($fieldResolver instanceof VariantAxis) { + throw new \TypeError('Could not accept a VariantAxis renderer in am Image attrinute.'); + } + + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'image'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return false; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer/SimpleSelect.php b/src/AttributeRenderer/SimpleSelect.php new file mode 100644 index 0000000..eb583c4 --- /dev/null +++ b/src/AttributeRenderer/SimpleSelect.php @@ -0,0 +1,62 @@ +attribute = $attribute; + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'simpleselect'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return $this->fieldResolver instanceof VariantAxis; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer/Status.php b/src/AttributeRenderer/Status.php new file mode 100644 index 0000000..03af4cd --- /dev/null +++ b/src/AttributeRenderer/Status.php @@ -0,0 +1,67 @@ +attribute = $attribute; + + if ($fieldResolver instanceof VariantAxis) { + throw new \TypeError('Could not accept a VariantAxis renderer in a Status attrinute.'); + } + + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'status'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return false; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer/Text.php b/src/AttributeRenderer/Text.php new file mode 100644 index 0000000..adac2d5 --- /dev/null +++ b/src/AttributeRenderer/Text.php @@ -0,0 +1,67 @@ +attribute = $attribute; + + if ($fieldResolver instanceof VariantAxis) { + throw new \TypeError('Could not accept a VariantAxis renderer in a Text attrinute.'); + } + + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'text'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return false; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer/Varchar.php b/src/AttributeRenderer/Varchar.php new file mode 100644 index 0000000..6754318 --- /dev/null +++ b/src/AttributeRenderer/Varchar.php @@ -0,0 +1,67 @@ +attribute = $attribute; + + if ($fieldResolver instanceof VariantAxis) { + throw new \TypeError('Could not accept a VariantAxis renderer in a Varchar attrinute.'); + } + + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'varchar'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return false; + } +} \ No newline at end of file diff --git a/src/AttributeRenderer/Visibility.php b/src/AttributeRenderer/Visibility.php new file mode 100644 index 0000000..e828c28 --- /dev/null +++ b/src/AttributeRenderer/Visibility.php @@ -0,0 +1,67 @@ +attribute = $attribute; + + if ($fieldResolver instanceof VariantAxis) { + throw new \TypeError('Could not accept a VariantAxis renderer in a Visibility attrinute.'); + } + + $this->fieldResolver = $fieldResolver; + } + + public function __toString() + { + return 'visibility'; + } + + public function __invoke(TemplateWrapper $template): string + { + if ($this->attribute instanceof Attribute\ExNihilo) { + return ''; + } + + return $template->render([ + 'attribute' => $this->attribute, + 'fields' => $this->fields(), + ]); + } + + public function template(): string + { + return $this->fieldResolver->template($this); + } + + public function attribute(): Attribute + { + return $this->attribute; + } + + public function fields(): iterable + { + return $this->fieldResolver->fields($this->attribute); + } + + public function isAxis(): bool + { + return false; + } +} \ No newline at end of file diff --git a/src/AttributeResolver.php b/src/AttributeResolver.php new file mode 100644 index 0000000..bb05538 --- /dev/null +++ b/src/AttributeResolver.php @@ -0,0 +1,11 @@ +attribute = $attribute; + } + + public function table(): string + { + return strtr( + 'tmp_{{ attribute }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + ] + ); + } + + public function default(): string + { + return 'attr_default'; + } + + public function alias(): string + { + return $this->default(); + } + + public function column(): string + { + return $this->attribute->codeInDestination(); + } +} \ No newline at end of file diff --git a/src/CodeGenerator/Localized.php b/src/CodeGenerator/Localized.php new file mode 100644 index 0000000..7dcbb96 --- /dev/null +++ b/src/CodeGenerator/Localized.php @@ -0,0 +1,60 @@ +attribute = $attribute; + $this->locale = $locale; + } + + public function table(): string + { + return strtr( + 'tmp_{{ attribute }}_{{ locale }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + '{{ locale }}' => $this->locale->code(), + ] + ); + } + + public function default(): string + { + return 'attr_default'; + } + + public function alias(): string + { + return strtr( + 'attr_{{ locale }}', + [ + '{{ locale }}' => $this->locale->code(), + ] + ); + } + + public function column(): string + { + return strtr( + '{{ attribute }}-{{ locale }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + '{{ locale }}' => $this->locale->code(), + ] + ); + } +} \ No newline at end of file diff --git a/src/CodeGenerator/Scoped.php b/src/CodeGenerator/Scoped.php new file mode 100644 index 0000000..3e81025 --- /dev/null +++ b/src/CodeGenerator/Scoped.php @@ -0,0 +1,61 @@ +attribute = $attribute; + $this->scope = $scope; + } + + public function table(): string + { + return strtr( + 'tmp_{{ attribute }}_{{ scope }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + '{{ scope }}' => $this->scope->code(), + ] + ); + } + + public function default(): string + { + return 'attr_default'; + } + + public function alias(): string + { + return strtr( + 'attr_{{ scope }}', + [ + '{{ scope }}' => $this->scope->code(), + ] + ); + } + + public function column(): string + { + return strtr( + '{{ attribute }}-{{ scope }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + '{{ scope }}' => $this->scope->code(), + ] + ); + } +} \ No newline at end of file diff --git a/src/CodeGenerator/ScopedAndLocalized.php b/src/CodeGenerator/ScopedAndLocalized.php new file mode 100644 index 0000000..dd144a6 --- /dev/null +++ b/src/CodeGenerator/ScopedAndLocalized.php @@ -0,0 +1,68 @@ +attribute = $attribute; + $this->scope = $scope; + $this->locale = $locale; + } + + public function table(): string + { + return strtr( + 'tmp_{{ attribute }}_{{ scope }}_{{ locale }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + '{{ scope }}' => $this->scope->code(), + '{{ locale }}' => $this->locale->code(), + ] + ); + } + + public function default(): string + { + return 'attr_default'; + } + + public function alias(): string + { + return strtr( + 'attr_{{ scope }}_{{ locale }}', + [ + '{{ scope }}' => $this->scope->code(), + '{{ locale }}' => $this->locale->code(), + ] + ); + } + + public function column(): string + { + return strtr( + '{{ attribute }}-{{ locale }}-{{ scope }}', + [ + '{{ attribute }}' => $this->attribute->codeInSource(), + '{{ scope }}' => $this->scope->code(), + '{{ locale }}' => $this->locale->code(), + ] + ); + } +} \ No newline at end of file diff --git a/src/Field.php b/src/Field.php new file mode 100644 index 0000000..b12cb51 --- /dev/null +++ b/src/Field.php @@ -0,0 +1,24 @@ +codeGenerator = $codeGenerator; + $this->store = $store; + } + + public function __toString() + { + return $this->codeGenerator->column($this); + } +} \ No newline at end of file diff --git a/src/FieldResolver.php b/src/FieldResolver.php new file mode 100644 index 0000000..38cb10d --- /dev/null +++ b/src/FieldResolver.php @@ -0,0 +1,9 @@ + $renderer, + ]); + } +} \ No newline at end of file diff --git a/src/FieldResolver/Localized.php b/src/FieldResolver/Localized.php new file mode 100644 index 0000000..e1185f5 --- /dev/null +++ b/src/FieldResolver/Localized.php @@ -0,0 +1,44 @@ +locales = $locales; + } + + public function fields(Attribute $attribute): iterable + { + return iterator_to_array((function(Attribute $attribute) { + foreach ($this->locales as $locale) { + yield new Field( + new CodeGenerator\Localized( + $attribute, + $locale + ), + $locale->store() + ); + } + })($attribute)); + } + + public function template(AttributeRenderer $renderer): string + { + return strtr('product/attribute/extract-{{ type }}-localizable.sql.twig', [ + '{{ type }}' => $renderer, + ]); + } +} \ No newline at end of file diff --git a/src/FieldResolver/Scoped.php b/src/FieldResolver/Scoped.php new file mode 100644 index 0000000..fa00e0a --- /dev/null +++ b/src/FieldResolver/Scoped.php @@ -0,0 +1,43 @@ +scopes = $scopes; + } + + public function fields(Attribute $attribute): iterable + { + return iterator_to_array((function(Attribute $attribute) { + foreach ($this->scopes as $scope) { + yield new Field( + new CodeGenerator\Scoped( + $attribute, + $scope + ), + $scope->store() + ); + } + })($attribute)); + } + + public function template(AttributeRenderer $renderer): string + { + return strtr('product/attribute/extract-{{ type }}-scopable.sql.twig', [ + '{{ type }}' => $renderer, + ]); + } +} \ No newline at end of file diff --git a/src/FieldResolver/ScopedAndLocalized.php b/src/FieldResolver/ScopedAndLocalized.php new file mode 100644 index 0000000..8fdbbe3 --- /dev/null +++ b/src/FieldResolver/ScopedAndLocalized.php @@ -0,0 +1,46 @@ +scopes = $scopes; + } + + public function fields(Attribute $attribute): iterable + { + return iterator_to_array((function(Attribute $attribute) { + foreach ($this->scopes as $scope) { + foreach ($scope->locales() as $locale) { + yield new Field( + new CodeGenerator\ScopedAndLocalized( + $attribute, + $scope, + $locale + ), + $locale->store() + ); + } + } + })($attribute)); + } + + public function template(AttributeRenderer $renderer): string + { + return strtr('product/attribute/extract-{{ type }}-scopable-localizable.sql.twig', [ + '{{ type }}' => $renderer, + ]); + } +} \ No newline at end of file diff --git a/src/FieldResolver/VariantAxis.php b/src/FieldResolver/VariantAxis.php new file mode 100644 index 0000000..d688509 --- /dev/null +++ b/src/FieldResolver/VariantAxis.php @@ -0,0 +1,9 @@ +code = $code; + $this->default = $default; + } + + public function code(): string + { + return $this->code; + } + + public function store(): MagentoStore + { + return $this->default; + } +} \ No newline at end of file diff --git a/src/Locale/LocaleMapping.php b/src/Locale/LocaleMapping.php new file mode 100644 index 0000000..b1814ca --- /dev/null +++ b/src/Locale/LocaleMapping.php @@ -0,0 +1,30 @@ +locale = $locale; + $this->store = $store; + } + + public function code(): string + { + return $this->locale->code(); + } + + public function store(): MagentoStore + { + return $this->store; + } +} \ No newline at end of file diff --git a/src/MagentoStore.php b/src/MagentoStore.php new file mode 100644 index 0000000..ac73da5 --- /dev/null +++ b/src/MagentoStore.php @@ -0,0 +1,14 @@ +id = $id; + } +} \ No newline at end of file diff --git a/src/Renderer.php b/src/Renderer.php new file mode 100644 index 0000000..7929906 --- /dev/null +++ b/src/Renderer.php @@ -0,0 +1,66 @@ +initialization = $initialization; + $this->finalization = $finalization; + $this->types = $types; + $this->attributes = $attributes; + } + + public function __invoke($stream, Environment $twig) + { + if (!is_resource($stream) || !get_resource_type($stream) === 'stream') { + throw new \TypeError(strtr('Expected a stream resource as first argument, got %actual%.', [ + '%actual%' => is_resource($stream) ? sprintf('resource(%s)', get_resource_type($stream)) : + (is_object($stream) ? sprintf('object(%s)', get_class($stream)) : gettype($stream)) + ])); + } + + $template = $twig->load($this->initialization); + fwrite($stream, $template->render([ + 'attributes' => array_map(function (AttributeRenderer $renderer) { + return $renderer->attribute(); + }, $this->attributes), + 'fields' => array_merge([], ...array_map(function (AttributeRenderer $renderer) { + return $renderer->fields(); + }, $this->attributes)), + 'axises' => array_merge([], ...array_map(function (AttributeRenderer $renderer) { + return $renderer->fields();; + }, array_filter($this->attributes, function (AttributeRenderer $renderer) { + return $renderer->isAxis(); + }))), + ]) . PHP_EOL); + + /** @var AttributeRenderer $attribute */ + foreach ($this->attributes as $attribute) { + $template = $twig->load($attribute->template()); + + fwrite($stream, $attribute($template) . PHP_EOL); + } + +// $template = $twig->load($this->finalization); +// fwrite($stream, $template->render([ +// 'types' => $this->types, +// ]) . PHP_EOL); + } +} \ No newline at end of file diff --git a/src/Scope.php b/src/Scope.php new file mode 100644 index 0000000..de99cc9 --- /dev/null +++ b/src/Scope.php @@ -0,0 +1,13 @@ +code = $code; + $this->default = $default; + $this->locales = $locales; + } + + public function locales(): iterable + { + return $this->locales; + } + + public function code(): string + { + return $this->code; + } + + public function store(): MagentoStore + { + return $this->default; + } +} \ No newline at end of file diff --git a/src/Scope/ScopeMapping.php b/src/Scope/ScopeMapping.php new file mode 100644 index 0000000..d3e0147 --- /dev/null +++ b/src/Scope/ScopeMapping.php @@ -0,0 +1,35 @@ +scope = $scope; + $this->store = $store; + } + + public function locales(): iterable + { + return $this->scope->locales(); + } + + public function code(): string + { + return $this->scope->code(); + } + + public function store(): MagentoStore + { + return $this->store; + } +} \ No newline at end of file diff --git a/src/TwigExtension.php b/src/TwigExtension.php new file mode 100644 index 0000000..f422436 --- /dev/null +++ b/src/TwigExtension.php @@ -0,0 +1,73 @@ +codeGenerator->alias(); + }), + new TwigFunction('as_field_column', function(Field $field) { + return $field->codeGenerator->column(); + }), + new TwigFunction('as_field_table', function(Field $field) { + return $field->codeGenerator->table(); + }), + + new TwigFunction('as_default_alias', function() { + return 'attr_default'; + }), + + new TwigFunction('as_attribute_table', function(Attribute $attribute) { + return strtr( + 'tmp_{{ attribute }}', + [ + '{{ attribute }}' => $attribute->codeInDestination(), + ] + ); + }), + new TwigFunction('as_attribute_default_table', function(Attribute $attribute) { + return strtr( + 'tmp_{{ attribute }}_default', + [ + '{{ attribute }}' => $attribute->codeInDestination(), + ] + ); + }), + new TwigFunction('as_attribute_localized_table', function(Attribute $attribute, Locale $locale) { + return strtr( + 'tmp_{{ attribute }}_{{ locale }}', + [ + '{{ attribute }}' => $attribute->codeInDestination(), + '{{ locale }}' => $locale->code(), + ] + ); + }), + new TwigFunction('as_attribute_scoped_table', function(Attribute $attribute, Scope $scope) { + return strtr( + 'tmp_{{ attribute }}_{{ scope }}', + [ + '{{ attribute }}' => $attribute->codeInDestination(), + '{{ scope }}' => $scope->code(), + ] + ); + }), + new TwigFunction('as_attribute_scoped_and_localized_table', function(Attribute $attribute, Scope $scope, Locale $locale) { + return strtr( + 'tmp_{{ attribute }}_{{ scope }}_{{ locale }}', + [ + '{{ attribute }}' => $attribute->codeInDestination(), + '{{ scope }}' => $scope->code(), + '{{ locale }}' => $locale->code(), + ] + ); + }), + ]; + } +} \ No newline at end of file diff --git a/src/VariantAxis.php b/src/VariantAxis.php new file mode 100644 index 0000000..3ab5cf0 --- /dev/null +++ b/src/VariantAxis.php @@ -0,0 +1,7 @@ + "\'#{item}\'")|join(', ')|raw }}) diff --git a/templates/initialize.sql.twig b/templates/initialize.sql.twig new file mode 100644 index 0000000..8b4f44f --- /dev/null +++ b/templates/initialize.sql.twig @@ -0,0 +1,194 @@ +CREATE TEMPORARY TABLE tmp_sku ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.sku, + product.entity_id, + product.type_id +FROM catalog_product_entity AS product +WHERE product.sku IS NOT NULL; + +CREATE TEMPORARY TABLE tmp_options ( + option_id INTEGER NOT NULL, + code VARCHAR(256) NOT NULL, + short_code VARCHAR(256) NOT NULL, + attribute VARCHAR(128) NOT NULL, + sort_order INTEGER NOT NULL, + PRIMARY KEY (option_id), + INDEX (code, attribute), + INDEX (attribute) +) +SELECT + opt_value_default.option_id, + SUBSTRING(CONCAT( + codes.code, + '_', + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + LOWER(opt_value_default.value), + '"', + 'inches' + ), + 'â', + 'a' + ), + 'è', + 'e' + ), + 'é', + 'e' + ), + '/', + '_' + ), + '.', + '_' + ), + ' ', + '_' + ), + ',', + '_' + ), + ')', + '' + ), + '(', + '' + ), + '+', + '_plus' + ), + '_x_', + 'x' + ) + ), 1, 100) AS code, + SUBSTRING(REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + REPLACE( + LOWER(opt_value_default.value), + '"', + 'inches' + ), + 'â', + 'a' + ), + 'è', + 'e' + ), + 'é', + 'e' + ), + '/', + '_' + ), + '.', + '_' + ), + ' ', + '_' + ), + ',', + '_' + ), + ')', + '' + ), + '(', + '' + ), + '+', + '_plus' + ), + '_x_', + 'x' + ), 1, 99 - LENGTH(codes.code)) AS short_code, + attribute.attribute_code AS attribute, + opt.sort_order AS sort_order + +FROM ({{ attributes|map(attribute => "SELECT '#{ attribute.codeInSource }'")|join(' UNION ')|raw }}) AS codes +INNER JOIN eav_attribute AS attribute + ON codes.code = attribute.attribute_code +INNER JOIN eav_attribute_option AS opt + ON opt.attribute_id=attribute.attribute_id +INNER JOIN eav_attribute_option_value AS opt_value_default + ON opt_value_default.option_id=opt.option_id + AND opt_value_default.store_id=0; + +{% for axis in axises %} +CREATE TEMPORARY TABLE {{ as_field_table(axis) }} ( + entity_id INTEGER NOT NULL, + code VARCHAR(256) NOT NULL, + short_code VARCHAR(256) NOT NULL, + PRIMARY KEY (entity_id, code), + INDEX (code) +) +SELECT + {{ as_default_alias() }}.entity_id, + {{ as_default_alias() }}_option.short_code AS short_code, + {{ as_default_alias() }}_option.code AS code +FROM eav_attribute AS attribute +INNER JOIN catalog_product_entity_int AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.attribute_id=attribute.attribute_id +INNER JOIN tmp_options AS {{ as_default_alias() }}_option + ON {{ as_default_alias() }}_option.attribute = attribute.attribute_code + AND {{ as_default_alias() }}.value={{ as_default_alias() }}_option.option_id +WHERE attribute.attribute_code = '{{ attribute.codeInSource }}' + AND attribute.entity_type_id = 4 + AND attribute.attribute_id={{ as_default_alias() }}.attribute_id + AND {{ as_default_alias() }}.store_id = 0; +{% endfor %} + +{#CREATE TEMPORARY TABLE tmp_hierarchy (#} +{# parent VARCHAR(128) NOT NULL,#} +{# child VARCHAR(128) NOT NULL,#} +{# variant VARCHAR(128) NOT NULL,#} +{# color VARCHAR(128) NULL,#} +{# capacity VARCHAR(128) NULL,#} +{# PRIMARY KEY (parent, child, variant),#} +{# INDEX (parent),#} +{# INDEX (child),#} +{# INDEX (variant)#} +{#)#} +{#SELECT#} +{# product.sku AS parent,#} +{# CONCAT(product.sku{%- for axis in axises -%}, ':', {{ as_field_alias(axis) }}.short_code{%- endfor -%}) AS child,#} +{#{%- for axis in axises -%}#} +{# {{ as_field_alias(axis) }}.code AS `{{ attribute.codeInDestination }}`,#} +{#{% endfor -%}#} +{# child.sku AS variant#} +{#FROM catalog_product_entity AS product#} +{#INNER JOIN catalog_product_super_link AS link#} +{# ON link.parent_id=product.entity_id#} +{#INNER JOIN catalog_product_entity AS child#} +{# ON child.entity_id=link.product_id#} +{#{% for axis in axises -%}#} +{#INNER JOIN {{ as_attribute_table(axis) }} AS {{ as_field_alias(axis) }}#} +{# ON {{ as_field_alias(axis) }}.entity_id = child.entity_id#} +{#{% endfor -%}#} +{#WHERE product.sku IS NOT NULL#} +{# AND child.sku IS NOT NULL#} +{# AND product.type_id IN ('configurable');#} \ No newline at end of file diff --git a/templates/product/attribute/extract-datetime-scopable-localizable.sql.twig b/templates/product/attribute/extract-datetime-scopable-localizable.sql.twig new file mode 100644 index 0000000..b836eee --- /dev/null +++ b/templates/product/attribute/extract-datetime-scopable-localizable.sql.twig @@ -0,0 +1,29 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {%- for field in fields -%} + `{{ as_field_column(field) }}` DATETIME NULL, + {% endfor -%} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SELECT + {% for field in fields -%} + COALESCE({{ as_field_alias(field) }}.value, {{ as_default_alias() }}.value) AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +LEFT JOIN catalog_product_entity_datetime AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 +{% for field in fields -%} +LEFT JOIN catalog_product_entity_datetime AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }} +{% endfor -%}; diff --git a/templates/product/attribute/extract-image-scopable.sql.twig b/templates/product/attribute/extract-image-scopable.sql.twig new file mode 100644 index 0000000..bc0e9c9 --- /dev/null +++ b/templates/product/attribute/extract-image-scopable.sql.twig @@ -0,0 +1,31 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {%- for field in fields -%} + `{{ as_field_column(field) }}` VARCHAR(255) NULL, + {% endfor -%} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SELECT + {% for field in fields -%} + COALESCE({{ as_field_alias(field) }}.value, {{ as_default_alias() }}.value) AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +LEFT JOIN catalog_product_entity_varchar AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 + AND {{ as_default_alias() }}.value!='no_selection' +{% for field in fields -%} +LEFT JOIN catalog_product_entity_datetime AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }} + AND {{ as_field_alias(field) }}.value!='no_selection' +{% endfor -%}; \ No newline at end of file diff --git a/templates/product/attribute/extract-simpleselect-scopable-localizable.sql.twig b/templates/product/attribute/extract-simpleselect-scopable-localizable.sql.twig new file mode 100644 index 0000000..66ca736 --- /dev/null +++ b/templates/product/attribute/extract-simpleselect-scopable-localizable.sql.twig @@ -0,0 +1,78 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_default_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + {{ as_default_alias() }}.code +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN catalog_product_entity_int AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 +LEFT JOIN ( + SELECT * + FROM tmp_options + WHERE attribute LIKE '{{ attribute.codeInSource }}' +) AS {{ as_default_alias() }}_option + ON attr_default.value=option_default.option_id; + +{% for field in fields %} +CREATE TEMPORARY TABLE {{ as_field_table(field) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + COALESCE( + {{ as_field_alias(field) }}.code, + {{ as_default_alias() }}.code + ) AS `code` +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN {{ as_attribute_default_table(attribute) }} AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id +LEFT JOIN catalog_product_entity_int AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }} +LEFT JOIN ( + SELECT * + FROM tmp_options + WHERE attribute LIKE '{{ attribute.codeInSource }}' +) AS {{ as_field_alias(field) }}_option + ON {{ as_field_alias(field) }}.value={{ as_field_alias(field) }}_option.option_id; +{% endfor %} + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {% for field in fields %} + `{{ field }}` VARCHAR(255) NOT NULL, + {% endfor %} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + {% for field in fields -%} + {{ as_field_alias(field) }}.value AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +{% for field in fields %} +LEFT JOIN {{ as_field_table(field) }} AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id +{% endfor %}; diff --git a/templates/product/attribute/extract-simpleselect-scopable.sql.twig b/templates/product/attribute/extract-simpleselect-scopable.sql.twig new file mode 100644 index 0000000..188cd08 --- /dev/null +++ b/templates/product/attribute/extract-simpleselect-scopable.sql.twig @@ -0,0 +1,78 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_default_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + {{ as_default_alias() }}.code +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN catalog_product_entity_int AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 +LEFT JOIN ( + SELECT * + FROM tmp_options + WHERE attribute LIKE '{{ attribute.codeInSource }}' +) AS {{ as_default_alias() }}_option + ON attr_default.value=option_default.option_id; + +{% for field in fields %} +CREATE TEMPORARY TABLE {{ as_field_table(field) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + COALESCE( + {{ as_field_alias(field) }}.code, + {{ as_default_alias() }}.code + ) AS `code` +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN {{ as_attribute_default_table(attribute) }} AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id +LEFT JOIN catalog_product_entity_int AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }} +LEFT JOIN ( + SELECT * + FROM tmp_options + WHERE attribute LIKE '{{ attribute.codeInSource }}' +) AS {{ as_field_alias(field) }}_option + ON {{ as_field_alias(field) }}.value={{ as_field_alias(field) }}_option.option_id; +{% endfor %} + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {% for field in fields %} + `{{ field }}` VARCHAR(255) NOT NULL, + {% endfor %} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + {% for field in fields -%} + {{ as_field_alias(field) }}.value AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +{% for field in fields %} +LEFT JOIN {{ as_field_table(field) }} AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id +{% endfor %}; diff --git a/templates/product/attribute/extract-simpleselect.sql.twig b/templates/product/attribute/extract-simpleselect.sql.twig new file mode 100644 index 0000000..a96f718 --- /dev/null +++ b/templates/product/attribute/extract-simpleselect.sql.twig @@ -0,0 +1,25 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + {{ as_default_alias() }}.code +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN catalog_product_entity_int AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 +LEFT JOIN ( + SELECT * + FROM tmp_options + WHERE attribute LIKE '{{ attribute.codeInSource }}' +) AS {{ as_default_alias() }}_option + ON attr_default.value=option_default.option_id; diff --git a/templates/product/attribute/extract-status-scopable-localizable.sql.twig b/templates/product/attribute/extract-status-scopable-localizable.sql.twig new file mode 100644 index 0000000..aa210e4 --- /dev/null +++ b/templates/product/attribute/extract-status-scopable-localizable.sql.twig @@ -0,0 +1,73 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_default_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + CASE {{ as_default_alias() }}.value + WHEN 1 THEN 'enabled' + WHEN 2 THEN 'disabled' + ELSE 'disabled' + END AS value +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN catalog_product_entity_int AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0; + +{% for field in fields %} +CREATE TEMPORARY TABLE {{ as_field_table(field) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + COALESCE(CASE {{ as_field_alias(field) }}.value + WHEN 1 THEN 'enabled' + WHEN 2 THEN 'disabled' + ELSE 'disabled' + END, + {{ as_default_alias() }}.code + ) AS `code` +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN {{ as_attribute_default_table(attribute) }} AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id +LEFT JOIN catalog_product_entity_int AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }}; +{% endfor %} + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {% for field in fields %} + `{{ field }}` VARCHAR(255) NOT NULL, + {% endfor %} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + {% for field in fields -%} + COALESCE({{ as_field_alias(field) }}.value, {{ as_default_alias() }}.value) AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +{% for field in fields %} +LEFT JOIN {{ as_field_table(field) }} AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id +{% endfor %}; diff --git a/templates/product/attribute/extract-text-scopable-localizable.sql.twig b/templates/product/attribute/extract-text-scopable-localizable.sql.twig new file mode 100644 index 0000000..c869198 --- /dev/null +++ b/templates/product/attribute/extract-text-scopable-localizable.sql.twig @@ -0,0 +1,29 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {%- for field in fields -%} + `{{ as_field_column(field) }}` TEXT NULL, + {% endfor -%} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SELECT + {% for field in fields -%} + COALESCE({{ as_field_alias(field) }}.value, {{ as_default_alias() }}.value) AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +LEFT JOIN catalog_product_entity_text AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 +{% for field in fields -%} +LEFT JOIN catalog_product_entity_text AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }} +{% endfor -%}; diff --git a/templates/product/attribute/extract-text.sql.twig b/templates/product/attribute/extract-text.sql.twig new file mode 100644 index 0000000..3578728 --- /dev/null +++ b/templates/product/attribute/extract-text.sql.twig @@ -0,0 +1,23 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {%- for field in fields -%} + `{{ as_field_column(field) }}` TEXT NULL, + {% endfor -%} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SELECT + {% for field in fields -%} + {{ as_default_alias() }}.value AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN catalog_product_entity_text AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 \ No newline at end of file diff --git a/templates/product/attribute/extract-varchar-scopable-localizable.sql.twig b/templates/product/attribute/extract-varchar-scopable-localizable.sql.twig new file mode 100644 index 0000000..331861b --- /dev/null +++ b/templates/product/attribute/extract-varchar-scopable-localizable.sql.twig @@ -0,0 +1,29 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {%- for field in fields -%} + `{{ as_field_column(field) }}` VARCHAR(255) NULL, + {% endfor -%} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci +SELECT + {% for field in fields -%} + COALESCE({{ as_field_alias(field) }}.value, {{ as_default_alias() }}.value) AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +LEFT JOIN catalog_product_entity_varchar AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0 +{% for field in fields -%} +LEFT JOIN catalog_product_entity_varchar AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }} +{% endfor -%}; diff --git a/templates/product/attribute/extract-visibility-scopable-localizable.sql.twig b/templates/product/attribute/extract-visibility-scopable-localizable.sql.twig new file mode 100644 index 0000000..04a05df --- /dev/null +++ b/templates/product/attribute/extract-visibility-scopable-localizable.sql.twig @@ -0,0 +1,76 @@ + +CREATE TEMPORARY TABLE {{ as_attribute_default_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + CASE {{ as_default_alias() }}.value + WHEN 1 THEN 'not_visible' + WHEN 2 THEN 'visible_in_catalog' + WHEN 3 THEN 'visible_in_search' + WHEN 4 THEN 'visible_in_catalog_and_search' + ELSE 'not_visible' + END AS value +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN catalog_product_entity_int AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id + AND {{ as_default_alias() }}.attribute_id=attribute.attribute_id + AND {{ as_default_alias() }}.store_id=0; + +{% for field in fields %} +CREATE TEMPORARY TABLE {{ as_field_table(field) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + code VARCHAR(255) NULL, + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + product.*, + COALESCE(CASE {{ as_field_alias(field) }}.value + WHEN 1 THEN 'not_visible' + WHEN 2 THEN 'visible_in_catalog' + WHEN 3 THEN 'visible_in_search' + WHEN 4 THEN 'visible_in_catalog_and_search' + END, + attr_default.code + ) AS `code` +FROM tmp_sku AS product +INNER JOIN eav_attribute AS attribute + ON attribute.attribute_code='{{ attribute.codeInSource }}' AND attribute.entity_type_id=4 +INNER JOIN {{ as_attribute_default_table(attribute) }} AS {{ as_default_alias() }} + ON {{ as_default_alias() }}.entity_id=product.entity_id +LEFT JOIN catalog_product_entity_int AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id + AND {{ as_field_alias(field) }}.attribute_id=attribute.attribute_id + AND {{ as_field_alias(field) }}.store_id={{ field.store.id }}; +{% endfor %} + +CREATE TEMPORARY TABLE {{ as_attribute_table(attribute) }} ( + sku VARCHAR(128) NOT NULL, + entity_id INTEGER NOT NULL, + type_id VARCHAR(32) NOT NULL, + {% for field in fields %} + `{{ field }}` VARCHAR(255) NOT NULL, + {% endfor %} + PRIMARY KEY (sku), + UNIQUE INDEX (entity_id) +) +SELECT + {% for field in fields -%} + COALESCE({{ as_field_alias(field) }}.value, {{ as_default_alias() }}.value) AS `{{ as_field_column(field) }}`, + {% endfor -%} + product.* +FROM tmp_sku AS product +{% for field in fields %} +LEFT JOIN {{ as_field_table(field) }} AS {{ as_field_alias(field) }} + ON {{ as_field_alias(field) }}.entity_id=product.entity_id +{% endfor %};