├── .github
├── FUNDING.yml
└── workflows
│ ├── extdn-integration-tests-post-install.sh
│ ├── extdn-integration-tests-pre-install.sh
│ ├── extdn-integration-tests.yml
│ ├── extdn-phpstan-pre-install.sh
│ ├── extdn-phpstan.yml
│ └── extdn-unit-tests.yml
├── .gitignore
├── .module.ini
├── ARCHITECTURE.md
├── Api
├── CheckoutSessionDataProviderInterface.php
├── CustomerSessionDataProviderInterface.php
└── Data
│ ├── EventInterface.php
│ ├── MergeTagInterface.php
│ ├── ProcessorInterface.php
│ ├── ProductTagInterface.php
│ └── TagInterface.php
├── CHANGELOG.md
├── Config
├── Backend
│ └── ContainerId.php
├── Config.php
├── Frontend
│ ├── ContainerId.php
│ └── Funding.php
├── Source
│ ├── CategoryAttributes.php
│ ├── CustomerAttributes.php
│ ├── OrderStateOptions.php
│ ├── ProductAttributes.php
│ ├── ProductListValue.php
│ └── ViewCartOccurancesOptions.php
├── XmlConfig.php
└── XmlConfig
│ ├── CacheType.php
│ ├── Converter.php
│ ├── Reader.php
│ └── SchemaLocator.php
├── CustomerData
└── GtmCheckout.php
├── DataLayer
├── Event
│ ├── AddPaymentInfo.php
│ ├── AddShippingInfo.php
│ ├── AddToCart.php
│ ├── AddToWishlist.php
│ ├── BeginCheckout.php
│ ├── Login.php
│ ├── Logout.php
│ ├── Promotion
│ │ └── PromotionItem.php
│ ├── Purchase.php
│ ├── Refund.php
│ ├── RemoveFromCart.php
│ ├── SelectPromotion.php
│ ├── SignUp.php
│ ├── ViewCart.php
│ ├── ViewPromotion.php
│ └── ViewSearchResult.php
├── Mapper
│ ├── CartItemDataMapper.php
│ ├── CategoryDataMapper.php
│ ├── CustomerDataMapper.php
│ ├── GuestDataMapper.php
│ ├── OrderDataMapper.php
│ ├── OrderItemDataMapper.php
│ └── ProductDataMapper.php
├── Processor
│ ├── Base.php
│ ├── Cart.php
│ ├── Category.php
│ ├── Checkout.php
│ ├── Product.php
│ └── SuccessPage.php
├── Tag
│ ├── AccountId.php
│ ├── Cart
│ │ ├── CartItems.php
│ │ └── CartValue.php
│ ├── Category
│ │ ├── CategorySize.php
│ │ ├── CurrentCategory.php
│ │ └── Products.php
│ ├── CurrencyCode.php
│ ├── EnhancedConversions.php
│ ├── EnhancedConversions
│ │ └── Sha256EmailAddress.php
│ ├── Event.php
│ ├── LiveOnly.php
│ ├── Order
│ │ ├── Order.php
│ │ └── OrderItems.php
│ ├── Page
│ │ ├── Breadcrumbs.php
│ │ └── VirtualPage.php
│ ├── PagePath.php
│ ├── PageTitle.php
│ ├── PageType.php
│ ├── Product
│ │ ├── CurrentCategoryName.php
│ │ ├── CurrentPrice.php
│ │ ├── CurrentProduct.php
│ │ ├── ProductCategory.php
│ │ └── ProductTagInterface.php
│ └── Version.php
└── TagParser.php
├── Exception
├── BlockNotFound.php
├── EmptyProductDataMapperException.php
├── InvalidConfig.php
└── NotUsingSetProductSkusException.php
├── FAQ.md
├── INSTALL.md
├── LICENSE.txt
├── Logger
└── Debugger.php
├── Observer
├── AddAdditionalLayoutHandles.php
├── TriggerAddToCartDataLayerEvent.php
├── TriggerAddToWishlistDataLayerEvent.php
├── TriggerLoginDataLayerEvent.php
├── TriggerLogoutDataLayerEvent.php
├── TriggerPurchaseDataLayerEvent.php
├── TriggerRemoveFromCartDataLayerEvent.php
└── TriggerSignUpDataLayerEvent.php
├── PWA.md
├── Plugin
├── AddCspInlineScripts.php
├── AddDataToCartSection.php
├── AddDataToCustomerSection.php
├── AddProductDetails.php
├── GetProductsFromCategoryBlockPlugin.php
├── TriggerAddGuestPaymentInfoDataLayerEvent.php
├── TriggerAddPaymentInfoDataLayerEvent.php
├── TriggerAddShippingInfoDataLayerEvent.php
└── TriggerViewSearchResultDataLayerEvent.php
├── README.md
├── SessionDataProvider
├── CheckoutSessionDataProvider.php
└── CustomerSessionDataProvider.php
├── TESTING.md
├── TUTORIAL.md
├── Test
├── Functional
│ ├── BaseTest.php
│ └── Util
│ │ └── CategoryProviderTest.php
├── Integration
│ ├── Block
│ │ ├── DataLayerTest.php
│ │ └── ScriptTest.php
│ ├── DataLayer
│ │ ├── Event
│ │ │ ├── AddToCartTest.php
│ │ │ ├── PurchaseTest.php
│ │ │ └── ViewCartTest.php
│ │ ├── Mapper
│ │ │ ├── CategoryDataMapperTest.php
│ │ │ └── ProductDataMapperTest.php
│ │ ├── Tag
│ │ │ └── VersionTest.php
│ │ └── TagParserTest.php
│ ├── FixtureTrait
│ │ ├── CreateCustomer.php
│ │ ├── GetCategory.php
│ │ ├── GetCustomer.php
│ │ ├── GetOrder.php
│ │ ├── GetProduct.php
│ │ └── Reindex.php
│ ├── ModuleTest.php
│ ├── Observer
│ │ ├── TriggerLoginDataLayerEventTest.php
│ │ ├── TriggerLogoutDataLayerEventTest.php
│ │ └── TriggerPurchaseDataLayerEventTest.php
│ ├── Page
│ │ ├── AddToWishlistTest.php
│ │ ├── CategoryPageTest.php
│ │ ├── CheckoutOnepageSuccessTest.php
│ │ ├── LoginTest.php
│ │ ├── LogoutTest.php
│ │ └── ProductPageTest.php
│ ├── PageTestCase.php
│ ├── Plugin
│ │ ├── AddDataToCartSectionTest.php
│ │ ├── AddDataToCustomerSectionTest.php
│ │ ├── GetProductsFromCategoryBlockPluginTest.php
│ │ └── TriggerAddShippingInfoDataLayerEventTest.php
│ ├── SessionDataProvider
│ │ └── CustomerSessionDataProviderTest.php
│ ├── Stub
│ │ └── FulltextStub.php
│ └── Util
│ │ └── ProductDataMapperTest.php
└── Unit
│ ├── Config
│ └── ConfigTest.php
│ └── Util
│ └── CamelCaseTest.php
├── USAGE.md
├── Util
├── Attribute
│ └── GetAttributeValue.php
├── CamelCase.php
├── CategoryProvider.php
├── Debug.php
├── GetCategoryPath.php
├── GetCurrentCategory.php
├── GetCurrentCategoryProducts.php
├── GetCurrentProduct.php
├── OrderTotals.php
├── PriceFormatter.php
└── ProductProvider.php
├── ViewModel
├── Commons.php
└── DataLayer.php
├── composer.json
├── docs
└── gtm-example.json
├── etc
├── acl.xml
├── adminhtml
│ └── system.xml
├── cache.xml
├── config.xml
├── csp_whitelist.xml
├── data_layer.xml
├── data_layer.xsd
├── di.xml
├── frontend
│ ├── di.xml
│ ├── events.xml
│ └── sections.xml
├── module.xml
├── view.xml
└── webapi_rest
│ └── di.xml
├── package.json
├── phpstan.neon
├── phpunit.xml
├── registration.php
└── view
├── adminhtml
├── requirejs-config.js
├── templates
│ └── funding.phtml
└── web
│ └── js
│ └── validation-mixin.js
└── frontend
├── layout
├── breeze_layout.xml
├── catalog_category_view.xml
├── catalog_product_view.xml
├── checkout_cart_index.xml
├── checkout_index_index.xml
├── checkout_onepage_success.xml
├── default.xml
└── hyva_default.xml
├── requirejs-config.js
├── templates
├── hyva
│ ├── data-layer.phtml
│ ├── script-additions.phtml
│ ├── script-logger.phtml
│ ├── script-product-clicks.phtml
│ └── script-pusher.phtml
├── iframe.phtml
├── luma
│ ├── data-layer.phtml
│ ├── script-additions.phtml
│ ├── script-begin-checkout.phtml
│ └── script-product-clicks.phtml
├── product
│ └── details.phtml
└── script.phtml
└── web
└── js
├── checkout
└── payment-validator
│ ├── register.js
│ └── reload-customer-data.js
├── generic.js
├── logger.js
├── mixins
├── catalog-add-to-cart-mixin.js
├── minicart-mixin.js
├── shipping-save-processor-mixin.js
└── step-navigator-mixin.js
├── product
└── clicks.js
└── push.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: yireo
2 | custom: ["https://www.paypal.me/yireo"]
3 |
4 |
--------------------------------------------------------------------------------
/.github/workflows/extdn-integration-tests-post-install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | bin/magento setup:config:set --cache-backend=redis --cache-backend-redis-server=redis --cache-backend-redis-db=0 -n
--------------------------------------------------------------------------------
/.github/workflows/extdn-integration-tests-pre-install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | composer config minimum-stability dev
3 | composer config prefer-stable false
4 |
5 | composer require --dev yireo/magento2-integration-test-helper --no-update
6 |
7 | composer require yireo/magento2-replace-bundled:^4.0 --no-update
8 | composer require yireo/magento2-replace-inventory:^4.0 --no-update
9 | composer require yireo/magento2-replace-pagebuilder:^4.0 --no-update
10 |
--------------------------------------------------------------------------------
/.github/workflows/extdn-integration-tests.yml:
--------------------------------------------------------------------------------
1 | name: ExtDN Integration Tests
2 | on: [push]
3 |
4 | jobs:
5 | integration-tests:
6 | name: Magento 2 Integration Tests
7 | runs-on: ubuntu-latest
8 | services:
9 | mysql:
10 | image: mysql:8.0
11 | env:
12 | MYSQL_ROOT_PASSWORD: root
13 | ports:
14 | - 3306:3306
15 | options: --tmpfs /tmp:rw --tmpfs /var/lib/mysql:rw --health-cmd="mysqladmin ping"
16 | es:
17 | image: docker.io/wardenenv/elasticsearch:7.8
18 | ports:
19 | - 9200:9200
20 | env:
21 | 'discovery.type': single-node
22 | 'xpack.security.enabled': false
23 | ES_JAVA_OPTS: "-Xms64m -Xmx512m"
24 | options: --health-cmd="curl localhost:9200/_cluster/health?wait_for_status=yellow&timeout=60s" --health-interval=10s --health-timeout=5s --health-retries=3
25 | redis:
26 | image: redis
27 | ports:
28 | - 6379:6379
29 | options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
30 | env:
31 | MAGENTO_MARKETPLACE_USERNAME: ${{ secrets.MAGENTO_MARKETPLACE_USERNAME }}
32 | MAGENTO_MARKETPLACE_PASSWORD: ${{ secrets.MAGENTO_MARKETPLACE_PASSWORD }}
33 | MODULE_NAME: ${{ secrets.MODULE_NAME }}
34 | COMPOSER_NAME: ${{ secrets.COMPOSER_NAME }}
35 | steps:
36 | - uses: actions/checkout@v4
37 | - name: Cache Composer dependencies
38 | uses: actions/cache@v4
39 | with:
40 | path: /tmp/composer-cache
41 | key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
42 |
43 | - uses: extdn/github-actions-m2/magento-integration-tests/8.3@master
44 | env:
45 | MAGENTO_VERSION: '2.4.7-p3'
46 | COMPOSER_VERSION: 2
47 | with:
48 | magento_pre_install_script: .github/workflows/extdn-integration-tests-pre-install.sh
49 | magento_post_install_script: .github/workflows/extdn-integration-tests-post-install.sh
50 |
--------------------------------------------------------------------------------
/.github/workflows/extdn-phpstan-pre-install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | composer config minimum-stability dev
3 | composer config prefer-stable false
4 |
5 | composer require yireo/magento2-integration-test-helper --no-update
6 |
7 | composer config --no-plugins allow-plugins true
8 |
9 | composer require --dev phpstan/extension-installer:^1.0 --no-update
10 | composer require --dev bitexpert/phpstan-magento:^0.32 --no-update
11 |
12 | composer require yireo/magento2-replace-bundled:^4.0 --no-update
13 | composer require yireo/magento2-replace-inventory:^4.0 --no-update
14 | composer require yireo/magento2-replace-pagebuilder:^4.0 --no-update
15 |
--------------------------------------------------------------------------------
/.github/workflows/extdn-phpstan.yml:
--------------------------------------------------------------------------------
1 | name: ExtDN PHPStan
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | phpstan:
6 | name: PHPStan
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 |
11 | - name: "Determine composer cache directory"
12 | id: "determine-composer-cache-directory"
13 | run: "echo \"::set-output name=directory::$(composer config cache-dir)\""
14 |
15 | - name: Cache Composer dependencies
16 | uses: actions/cache@v4
17 | with:
18 | path: "${{ steps.determine-composer-cache-directory.outputs.directory }}"
19 | key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
20 |
21 | - uses: extdn/github-actions-m2/magento-phpstan/8.3@master
22 | with:
23 | composer_name: yireo/magento2-googletagmanager2
24 | composer_version: 2
25 | phpstan_level: 2
26 | magento_pre_install_script: .github/workflows/extdn-phpstan-pre-install.sh
27 |
--------------------------------------------------------------------------------
/.github/workflows/extdn-unit-tests.yml:
--------------------------------------------------------------------------------
1 | name: ExtDN Unit Tests
2 | on: [push]
3 |
4 | jobs:
5 | unit-tests:
6 | name: Magento 2 Unit Tests
7 | runs-on: ubuntu-latest
8 | env:
9 | MAGENTO_MARKETPLACE_USERNAME: ${{ secrets.MAGENTO_MARKETPLACE_USERNAME }}
10 | MAGENTO_MARKETPLACE_PASSWORD: ${{ secrets.MAGENTO_MARKETPLACE_PASSWORD }}
11 | MODULE_NAME: ${{ secrets.MODULE_NAME }}
12 | COMPOSER_NAME: ${{ secrets.COMPOSER_NAME }}
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Cache Composer dependencies
17 | uses: actions/cache@v4
18 | with:
19 | path: /tmp/composer-cache
20 | key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
21 |
22 | - uses: extdn/github-actions-m2/magento-unit-tests/8.3@master
23 | env:
24 | MAGENTO_VERSION: '2.4.7-p3'
25 | COMPOSER_VERSION: 2
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | phpunit-log/
3 | .idea/
4 | package-lock.json
5 |
--------------------------------------------------------------------------------
/.module.ini:
--------------------------------------------------------------------------------
1 | EXTENSION_VENDOR="Yireo"
2 | EXTENSION_NAME="GoogleTagManager2"
3 | COMPOSER_NAME="yireo/magento2-googletagmanager2"
4 | PHP_VERSIONS=("7.4", "8.1", "8.2", "8.3","8.4")
5 | PHPSTAN_LEVEL=2
6 |
--------------------------------------------------------------------------------
/Api/CheckoutSessionDataProviderInterface.php:
--------------------------------------------------------------------------------
1 | validate()) {
14 | throw new InvalidConfig('Invalid container ID "' . $this->getValue() . '". It should start with "GTM-"');
15 | }
16 |
17 | return parent::beforeSave();
18 | }
19 |
20 | private function validate(): bool
21 | {
22 | if ($this->_appState->getMode() === State::MODE_DEVELOPER) {
23 | return true;
24 | }
25 |
26 | if (empty($this->getValue())) {
27 | return true;
28 | }
29 |
30 | if (preg_match('/^GTM-/', $this->getValue())) {
31 | return true;
32 | }
33 |
34 | return false;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Config/Frontend/ContainerId.php:
--------------------------------------------------------------------------------
1 | toHtml();
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/Config/Source/CategoryAttributes.php:
--------------------------------------------------------------------------------
1 | categoryAttributeRepository = $categoryAttributeRepository;
22 | $this->searchCriteriaBuilder = $searchCriteriaBuilder;
23 | $this->sortOrderFactory = $sortOrderFactory;
24 | }
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | public function toOptionArray(): array
30 | {
31 | $options = [['value' => '', 'label' => '']];
32 |
33 | $this->searchCriteriaBuilder->addFilter('is_visible', 1);
34 | $sortOrder = $this->sortOrderFactory->create(['field' => 'attribute_code', 'direction' => 'asc']);
35 | $this->searchCriteriaBuilder->addSortOrder($sortOrder);
36 | $searchCriteria = $this->searchCriteriaBuilder->create();
37 |
38 | $searchResult = $this->categoryAttributeRepository->getList($searchCriteria);
39 | foreach ($searchResult->getItems() as $categoryAttribute) {
40 | $options[] = [
41 | 'value' => $categoryAttribute->getAttributeCode(),
42 | 'label' => $categoryAttribute->getAttributeCode() . ': '.$categoryAttribute->getDefaultFrontendLabel()
43 | ];
44 | }
45 |
46 | return $options;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Config/Source/CustomerAttributes.php:
--------------------------------------------------------------------------------
1 | attributeRepository = $attributeRepository;
26 | $this->searchCriteriaBuilder = $searchCriteriaBuilder;
27 | $this->sortOrderFactory = $sortOrderFactory;
28 | }
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | public function toOptionArray(): array
34 | {
35 | $options = [['value' => '', 'label' => '']];
36 |
37 | $sortOrder = $this->sortOrderFactory->create(['field' => 'attribute_code', 'direction' => 'asc']);
38 | $this->searchCriteriaBuilder->addSortOrder($sortOrder);
39 | $searchCriteria = $this->searchCriteriaBuilder->create();
40 |
41 | $searchResult = $this->attributeRepository->getList('customer', $searchCriteria);
42 | foreach ($searchResult->getItems() as $customerAttribute) {
43 | /** @var Attribute $customerAttribute */
44 | if (false === $this->isAttributeDisplayedInFrontend($customerAttribute)) {
45 | continue;
46 | }
47 |
48 |
49 | $options[] = [
50 | 'value' => $customerAttribute->getAttributeCode(),
51 | 'label' => $customerAttribute->getAttributeCode() . ': ' . $customerAttribute->getDefaultFrontendLabel()
52 | ];
53 | }
54 |
55 | return $options;
56 | }
57 |
58 | private function isAttributeDisplayedInFrontend(Attribute $attribute): bool
59 | {
60 | $forms = $attribute->getUsedInForms();
61 | foreach ($forms as $form) {
62 | if (preg_match('/^customer_/', $form)) {
63 | return true;
64 | }
65 | }
66 |
67 | return false;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Config/Source/OrderStateOptions.php:
--------------------------------------------------------------------------------
1 | getStates() as $state) {
15 | $options[] = ['value' => $state, 'label' => $state];
16 | }
17 |
18 | return $options;
19 | }
20 |
21 | private function getStates(): array
22 | {
23 | return [
24 | Order::STATE_NEW,
25 | Order::STATE_PAYMENT_REVIEW,
26 | Order::STATE_PENDING_PAYMENT,
27 | Order::STATE_PROCESSING,
28 | Order::STATE_COMPLETE,
29 | Order::STATE_CANCELED,
30 | Order::STATE_CLOSED,
31 | Order::STATE_HOLDED,
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Config/Source/ProductAttributes.php:
--------------------------------------------------------------------------------
1 | productAttributeRepository = $productAttributeRepository;
24 | $this->searchCriteriaBuilder = $searchCriteriaBuilder;
25 | $this->sortOrderFactory = $sortOrderFactory;
26 | }
27 |
28 | /**
29 | * {@inheritdoc}
30 | */
31 | public function toOptionArray(): array
32 | {
33 | $options = [['value' => '', 'label' => '']];
34 |
35 | $this->searchCriteriaBuilder->addFilter('is_visible', 1);
36 | $this->searchCriteriaBuilder->addFilter('is_visible_on_front', 1);
37 | $sortOrder = $this->sortOrderFactory->create(['field' => 'attribute_code', 'direction' => 'asc']);
38 | $this->searchCriteriaBuilder->addSortOrder($sortOrder);
39 | $searchCriteria = $this->searchCriteriaBuilder->create();
40 |
41 | $searchResult = $this->productAttributeRepository->getList($searchCriteria);
42 | foreach ($searchResult->getItems() as $productAttribute) {
43 | if (in_array($productAttribute->getAttributeCode(), self::REQUIRED_ATTRIBUTES)) {
44 | continue;
45 | }
46 |
47 | $options[] = [
48 | 'value' => $productAttribute->getAttributeCode(),
49 | 'label' => $productAttribute->getAttributeCode() . ': ' . $productAttribute->getDefaultFrontendLabel(),
50 | 'disabled' => true
51 | ];
52 | }
53 |
54 | return $options;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Config/Source/ProductListValue.php:
--------------------------------------------------------------------------------
1 | self::PRODUCT_FIRST_CATEGORY,
20 | 'label' => __('Product First Category'),
21 | ],
22 | [
23 | 'value' => self::CURRENT_CATEGORY,
24 | 'label' => __('Current Category'),
25 | ],
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Config/Source/ViewCartOccurancesOptions.php:
--------------------------------------------------------------------------------
1 | self::CART_PAGE_ONLY, 'label' => __('Cart-page only')],
19 | ['value' => self::EVERYWHERE, 'label' => __('Everywhere (including minicart)')],
20 | ];
21 |
22 | return $options;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Config/XmlConfig.php:
--------------------------------------------------------------------------------
1 | dataStorage = $dataStorage;
20 | }
21 |
22 | public function getDefault(): array
23 | {
24 | return $this->dataStorage->get('default');
25 | }
26 |
27 | public function getEvents(): array
28 | {
29 | return $this->dataStorage->get('events');
30 | }
31 |
32 | public function getEvent(string $eventName): array
33 | {
34 | $events = $this->getEvents();
35 | foreach ($events as $eventId => $eventData) {
36 | if ($eventName === $eventId) {
37 | return $eventData;
38 | }
39 | }
40 |
41 | return [];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Config/XmlConfig/CacheType.php:
--------------------------------------------------------------------------------
1 | get(self::TYPE_IDENTIFIER), self::CACHE_TAG);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Config/XmlConfig/Reader.php:
--------------------------------------------------------------------------------
1 | 'name'];
10 | }
11 |
--------------------------------------------------------------------------------
/Config/XmlConfig/SchemaLocator.php:
--------------------------------------------------------------------------------
1 | moduleReader = $moduleReader;
22 | }
23 |
24 | /**
25 | * @inheritdoc
26 | */
27 | public function getSchema()
28 | {
29 | return $this->getXsdPath();
30 | }
31 |
32 | /**
33 | * @inheritdoc
34 | */
35 | public function getPerFileSchema()
36 | {
37 | return $this->getXsdPath();
38 | }
39 |
40 | private function getXsdPath(): string
41 | {
42 | return $this->moduleReader->getModuleDir(
43 | Dir::MODULE_ETC_DIR,
44 | 'Yireo_GoogleTagManager2')
45 | . '/' . 'data_layer.xsd';
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/CustomerData/GtmCheckout.php:
--------------------------------------------------------------------------------
1 | checkoutSessionDataProvider = $checkoutSessionDataProvider;
19 | }
20 |
21 | /**
22 | * @return array
23 | */
24 | public function getSectionData(): array
25 | {
26 | $gtmEvents = $this->checkoutSessionDataProvider->get();
27 | $this->checkoutSessionDataProvider->clear();
28 | return ['gtm_events' => $gtmEvents];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/DataLayer/Event/AddPaymentInfo.php:
--------------------------------------------------------------------------------
1 | cartItems = $cartItems;
29 | $this->cartRepository = $cartRepository;
30 | $this->priceFormatter = $priceFormatter;
31 | }
32 |
33 | /**
34 | * @return string[]
35 | */
36 | public function get(): array
37 | {
38 | /** @var Cart $cart */
39 | $cart = $this->cartRepository->get($this->cartId);
40 | return [
41 | 'event' => 'add_payment_info',
42 | 'ecommerce' => [
43 | 'currency' => $cart->getQuoteCurrencyCode(),
44 | 'value' => $this->priceFormatter->format((float)$cart->getSubtotal()),
45 | 'coupon' => $cart->getCouponCode(),
46 | 'payment_type' => $this->paymentMethod,
47 | 'items' => $this->cartItems->get()
48 | ]
49 | ];
50 | }
51 |
52 | /**
53 | * @param string $paymentMethod
54 | * @return AddPaymentInfo
55 | */
56 | public function setPaymentMethod(string $paymentMethod): AddPaymentInfo
57 | {
58 | $this->paymentMethod = $paymentMethod;
59 | return $this;
60 | }
61 |
62 | /**
63 | * @param int $cartId
64 | * @return AddPaymentInfo
65 | */
66 | public function setCartId(int $cartId): AddPaymentInfo
67 | {
68 | $this->cartId = $cartId;
69 | return $this;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/DataLayer/Event/AddToCart.php:
--------------------------------------------------------------------------------
1 | productDataMapper = $productDataMapper;
31 | $this->currencyCode = $currencyCode;
32 | $this->priceFormatter = $priceFormatter;
33 | }
34 |
35 | /**
36 | * @return string[]
37 | * @throws LocalizedException
38 | * @throws NoSuchEntityException
39 | */
40 | public function get(): array
41 | {
42 | $qty = ($this->qty > 0) ? $this->qty : 1;
43 |
44 | $itemData = $this->productDataMapper->mapByProduct($this->product);
45 | $itemData['quantity'] = $qty;
46 | $value = $itemData['price'] * $qty;
47 |
48 | return [
49 | 'event' => 'add_to_cart',
50 | 'ecommerce' => [
51 | 'currency' => $this->currencyCode->get(),
52 | 'value' => $this->priceFormatter->format((float)$value),
53 | 'items' => [$itemData]
54 | ]
55 | ];
56 | }
57 |
58 | /**
59 | * @param Product $product
60 | * @return AddToCart
61 | */
62 | public function setProduct(Product $product): AddToCart
63 | {
64 | $this->product = $product;
65 | return $this;
66 | }
67 |
68 | public function setQty(int $qty): AddToCart
69 | {
70 | $this->qty = $qty;
71 | return $this;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/DataLayer/Event/AddToWishlist.php:
--------------------------------------------------------------------------------
1 | productDataMapper = $productDataMapper;
23 | }
24 |
25 | /**
26 | * @return string[]
27 | * @throws LocalizedException
28 | * @throws NoSuchEntityException
29 | */
30 | public function get(): array
31 | {
32 | $itemData = $this->productDataMapper->mapByProduct($this->product);
33 |
34 | return [
35 | 'event' => 'add_to_wishlist',
36 | 'ecommerce' => [
37 | 'items' => [$itemData]
38 | ]
39 | ];
40 | }
41 |
42 | /**
43 | * @param ProductInterface $product
44 | * @return AddToWishlist
45 | */
46 | public function setProduct(ProductInterface $product): AddToWishlist
47 | {
48 | $this->product = $product;
49 | return $this;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/DataLayer/Event/BeginCheckout.php:
--------------------------------------------------------------------------------
1 | quote = $quote;
31 | $this->cartItems = $cartItems;
32 | $this->cartValue = $cartValue;
33 | $this->currencyCode = $currencyCode;
34 | }
35 |
36 | public function get(): array
37 | {
38 | return [
39 | 'event' => 'begin_checkout',
40 | 'ecommerce' => [
41 | 'currency' => $this->currencyCode->get(),
42 | 'value' => $this->cartValue->get(),
43 | 'coupon' => $this->quote->getCouponCode(),
44 | 'items' => $this->cartItems->get()
45 | ]
46 | ];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/DataLayer/Event/Login.php:
--------------------------------------------------------------------------------
1 | customerDataMapper = $customerDataMapper;
19 | }
20 |
21 | public function setCustomer(CustomerInterface $customer): Login
22 | {
23 | $this->customer = $customer;
24 | return $this;
25 | }
26 |
27 | public function get(): array
28 | {
29 | return [
30 | 'event' => 'login',
31 | 'customer' => $this->customerDataMapper->mapByCustomer($this->customer)
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/DataLayer/Event/Logout.php:
--------------------------------------------------------------------------------
1 | 'logout'
13 | ];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/DataLayer/Event/Promotion/PromotionItem.php:
--------------------------------------------------------------------------------
1 | id = $id;
28 | $this->name = $name;
29 | $this->createName = $createName;
30 | $this->createSlot = $createSlot;
31 | $this->locationId = $locationId;
32 | }
33 |
34 | public function get(): array
35 | {
36 | return [
37 | 'promotion_id' => $this->id,
38 | 'promotion_name' => $this->name,
39 | 'creative_name' => $this->createName,
40 | 'creative_slot' => $this->createSlot,
41 | 'location_id' => $this->locationId,
42 | ];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/DataLayer/Event/Refund.php:
--------------------------------------------------------------------------------
1 | orderItems = $orderItems;
28 | $this->config = $config;
29 | $this->priceFormatter = $priceFormatter;
30 | $this->orderTotals = $orderTotals;
31 | }
32 |
33 | /**
34 | * @return string[]
35 | */
36 | public function get(): array
37 | {
38 | $order = $this->order;
39 | return [
40 | 'event' => 'refund',
41 | 'ecommerce' => [
42 | 'transaction_id' => $order->getIncrementId(),
43 | 'affiliation' => $this->config->getStoreName(),
44 | 'currency' => $order->getOrderCurrencyCode(),
45 | 'value' => $this->priceFormatter->format($this->orderTotals->getValueTotal($order)),
46 | 'tax' => $this->priceFormatter->format((float)$order->getTaxAmount()),
47 | 'shipping' => $this->priceFormatter->format($this->orderTotals->getShippingTotal($order)),
48 | 'coupon' => $order->getCouponCode(),
49 | 'payment_method' => $order->getPayment() ? $order->getPayment()->getMethod() : '',
50 | 'items' => $this->orderItems->setOrder($order)->get(),
51 | ]
52 | ];
53 | }
54 |
55 | /**
56 | * @param OrderInterface $order
57 | * @return Refund
58 | */
59 | public function setOrder(OrderInterface $order): Refund
60 | {
61 | $this->order = $order;
62 | return $this;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/DataLayer/Event/RemoveFromCart.php:
--------------------------------------------------------------------------------
1 | cartItemDataMapper = $cartItemDataMapper;
20 | }
21 |
22 | /**
23 | * @return array
24 | */
25 | public function get(): array
26 | {
27 | $cartItemData = $this->cartItemDataMapper->mapByCartItem($this->cartItem);
28 | return [
29 | 'event' => 'remove_from_cart',
30 | 'ecommerce' => [
31 | 'items' => [$cartItemData]
32 | ]
33 | ];
34 | }
35 |
36 | /**
37 | * @param CartItemInterface $cartItem
38 | * @return RemoveFromCart
39 | */
40 | public function setCartItem(CartItemInterface $cartItem): RemoveFromCart
41 | {
42 | $this->cartItem = $cartItem;
43 | return $this;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/DataLayer/Event/SelectPromotion.php:
--------------------------------------------------------------------------------
1 | promotionItems as $promotionItem) {
23 | $promotionsItemsData[] = $promotionItem->get();
24 | }
25 |
26 | return [
27 | 'event' => 'select_promotion',
28 | 'ecommerce' => [
29 | 'items' => $promotionsItemsData
30 | ]
31 | ];
32 | }
33 |
34 | /**
35 | * @param PromotionItem[] $promotionItems
36 | * @return SelectPromotion
37 | */
38 | public function setPromotionItems(array $promotionItems): SelectPromotion
39 | {
40 | $this->promotionItems = $promotionItems;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/DataLayer/Event/SignUp.php:
--------------------------------------------------------------------------------
1 | 'sign_up',
13 | 'method' => 'Standard' // @TODO: implement mapping based on the route used?
14 | ];
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/DataLayer/Event/ViewPromotion.php:
--------------------------------------------------------------------------------
1 | promotionItems as $promotionItem) {
23 | $promotionsItemsData[] = $promotionItem->get();
24 | }
25 |
26 | return [
27 | 'event' => 'view_promotion',
28 | 'ecommerce' => [
29 | 'items' => $promotionsItemsData
30 | ]
31 | ];
32 | }
33 |
34 | /**
35 | * @param PromotionItem[] $promotionItems
36 | * @return ViewPromotion
37 | */
38 | public function setPromotionItems(array $promotionItems): ViewPromotion
39 | {
40 | $this->promotionItems = $promotionItems;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/DataLayer/Event/ViewSearchResult.php:
--------------------------------------------------------------------------------
1 | 'view_search_result',
15 | 'search_term' => $this->searchTerm,
16 | ];
17 | }
18 |
19 | public function setSearchTerm(?string $searchTerm): void
20 | {
21 | $this->searchTerm = $searchTerm;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/DataLayer/Mapper/CategoryDataMapper.php:
--------------------------------------------------------------------------------
1 | config = $config;
24 | $this->getAttributeValue = $getAttributeValue;
25 | }
26 |
27 | /**
28 | * @param CategoryInterface $category
29 | * @return array
30 | * @throws LocalizedException
31 | */
32 | public function mapByCategory(CategoryInterface $category): array
33 | {
34 | $prefix = 'category_';
35 | $categoryData = [];
36 | $categoryFields = $this->getCategoryFields();
37 | foreach ($categoryFields as $categoryAttributeCode) {
38 | $dataLayerKey = $prefix . $categoryAttributeCode;
39 | $attributeValue = $this->getAttributeValue->getCategoryAttributeValue($category, $categoryAttributeCode);
40 | if (empty($attributeValue)) {
41 | continue;
42 | }
43 |
44 | $categoryData[$dataLayerKey] = $attributeValue;
45 | }
46 |
47 | return $categoryData;
48 | }
49 |
50 | /**
51 | * @return string[]
52 | */
53 | private function getCategoryFields(): array
54 | {
55 | return array_filter(array_merge(['id', 'name'], $this->config->getCategoryEavAttributeCodes()));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/DataLayer/Mapper/CustomerDataMapper.php:
--------------------------------------------------------------------------------
1 | camelCase = $camelCase;
27 | $this->config = $config;
28 | $this->getAttributeValue = $getAttributeValue;
29 | }
30 |
31 | /**
32 | * @param CustomerInterface $customer
33 | * @param string $prefix
34 | * @return array
35 | */
36 | public function mapByCustomer(CustomerInterface $customer, string $prefix = ''): array
37 | {
38 | $customerData = [];
39 | $customerFields = $this->getCustomerFields();
40 | foreach ($customerFields as $customerAttributeCode) {
41 | $dataLayerKey = lcfirst($prefix . $this->camelCase->to($customerAttributeCode));
42 | $attributeValue = $this->getAttributeValue->getCustomerAttributeValue($customer, $customerAttributeCode);
43 |
44 | if (empty($attributeValue)) {
45 | continue;
46 | }
47 |
48 | $customerData[$dataLayerKey] = $attributeValue;
49 | }
50 |
51 | return $customerData;
52 | }
53 |
54 | /**
55 | * @return string[]
56 | */
57 | private function getCustomerFields(): array
58 | {
59 | return array_filter(array_merge(['id'], $this->config->getCustomerEavAttributeCodes()));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/DataLayer/Mapper/GuestDataMapper.php:
--------------------------------------------------------------------------------
1 | config = $config;
24 | $this->getAttributeValue = $getAttributeValue;
25 | }
26 |
27 | /**
28 | * @param Order $order
29 | * @return array
30 | * @throws LocalizedException
31 | */
32 | public function mapByOrder(Order $order): array
33 | {
34 | $prefix = 'customer_';
35 | $guestData = [];
36 | $guestFields = $this->getGuestFields();
37 | foreach ($guestFields as $guestAttributeCode) {
38 | $guestAttributeCode = $prefix . $guestAttributeCode;
39 | $dataLayerKey = $prefix . $guestAttributeCode;
40 | $attributeValue = $this->getAttributeValue->getAttributeValue($order, 'order', $guestAttributeCode);
41 | if (empty($attributeValue)) {
42 | continue;
43 | }
44 |
45 | $guestData[$dataLayerKey] = $attributeValue;
46 | }
47 |
48 | return $guestData;
49 | }
50 |
51 | /**
52 | * @return string[]
53 | */
54 | private function getGuestFields(): array
55 | {
56 | return array_filter(array_merge(['id'], $this->config->getCustomerEavAttributeCodes()));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/DataLayer/Processor/Base.php:
--------------------------------------------------------------------------------
1 | xmlConfig = $xmlConfig;
16 | }
17 |
18 | public function process(array $data): array
19 | {
20 | $default = $this->xmlConfig->getDefault();
21 | return array_merge($data, $default);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/DataLayer/Processor/Cart.php:
--------------------------------------------------------------------------------
1 | config = $config;
19 | }
20 |
21 | public function get(): string
22 | {
23 | return $this->config->getId();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Cart/CartItems.php:
--------------------------------------------------------------------------------
1 | cartModel = $cartModel;
31 | $this->cartItemDataMapper = $cartItemDataMapper;
32 | $this->productProvider = $productProvider;
33 | }
34 |
35 | /**
36 | * @return array
37 | * @throws NoSuchEntityException
38 | */
39 | public function get(): array
40 | {
41 | $cartItems = $this->cartModel->getQuote()->getAllVisibleItems();
42 | if (!$cartItems) {
43 | return [];
44 | }
45 |
46 | $this->productProvider->addProductSkus($this->getSkusFromCartItems($cartItems));
47 | $cartItemsData = [];
48 |
49 | foreach ($cartItems as $cartItem) {
50 | $cartItemData = $this->cartItemDataMapper->mapByCartItem($cartItem);
51 | $cartItemsData[] = $cartItemData;
52 | }
53 |
54 | return $cartItemsData;
55 | }
56 |
57 | /**
58 | * @param Item[] $cartItems
59 | * @return array
60 | */
61 | private function getSkusFromCartItems(array $cartItems): array
62 | {
63 | $productSkus = [];
64 | foreach ($cartItems as $cartItem) {
65 | $productSkus[] = $cartItem->getSku();
66 | }
67 |
68 | return array_unique($productSkus);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Cart/CartValue.php:
--------------------------------------------------------------------------------
1 | cartModel = $cartModel;
23 | $this->priceFormatter = $priceFormatter;
24 | }
25 |
26 | /**
27 | * @return float
28 | */
29 | public function get(): float
30 | {
31 | return $this->priceFormatter->format((float)$this->cartModel->getQuote()->getSubtotal());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Category/CategorySize.php:
--------------------------------------------------------------------------------
1 | size = $size;
18 | }
19 |
20 | /**
21 | * @return int
22 | */
23 | public function get(): int
24 | {
25 | return $this->size;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Category/CurrentCategory.php:
--------------------------------------------------------------------------------
1 | getCurrentCategory = $getCurrentCategory;
24 | $this->categoryDataMapper = $categoryDataMapper;
25 | }
26 |
27 | /**
28 | * @return string[]
29 | * @throws NoSuchEntityException
30 | */
31 | public function merge(): array
32 | {
33 | $currentCategory = $this->getCurrentCategory->get();
34 | return $this->categoryDataMapper->mapByCategory($currentCategory);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Category/Products.php:
--------------------------------------------------------------------------------
1 | getCurrentCategoryProducts = $getCurrentCategoryProducts;
32 | $this->getCurrentCategory = $getCurrentCategory;
33 | $this->productDataMapper = $productDataMapper;
34 | $this->config = $config;
35 | }
36 |
37 | /**
38 | * @return array
39 | * @throws NoSuchEntityException
40 | */
41 | public function get(): array
42 | {
43 | $productsData = [];
44 | $i = 1;
45 | foreach ($this->getCurrentCategoryProducts->getProducts() as $product) {
46 | if ($this->config->getMaximumCategoryProducts() > 0 && $i > $this->config->getMaximumCategoryProducts()) {
47 | break;
48 | }
49 |
50 | $product->setCategory($this->getCurrentCategory->get());
51 | $productData = $this->productDataMapper->mapByProduct($product);
52 | $productData['quantity'] = 1;
53 | $productData['index'] = $i;
54 | $productsData[] = $productData;
55 | $i++;
56 | }
57 |
58 | return $productsData;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/DataLayer/Tag/CurrencyCode.php:
--------------------------------------------------------------------------------
1 | storeManager = $storeManager;
21 | $this->logger = $logger;
22 | }
23 |
24 | public function get(): string
25 | {
26 | try {
27 | $store = $this->storeManager->getStore();
28 | /** @var Store $store */
29 | return $store->getCurrentCurrencyCode() ?: '';
30 | } catch (NoSuchEntityException $e) {
31 | $this->logger->warning('Cannot retrieve currency code for current store. ' . $e->getMessage());
32 | return '';
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/DataLayer/Tag/EnhancedConversions/Sha256EmailAddress.php:
--------------------------------------------------------------------------------
1 | checkoutSession = $checkoutSession;
24 | $this->orderRepository = $orderRepository;
25 | }
26 |
27 | public function get(): string
28 | {
29 | $order = $this->getOrder();
30 | return hash('sha256', trim(strtolower($order->getCustomerEmail())));
31 | }
32 |
33 | /**
34 | * @return OrderInterface
35 | */
36 | private function getOrder(): OrderInterface
37 | {
38 | return $this->orderRepository->get($this->checkoutSession->getLastRealOrder()->getId());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Event.php:
--------------------------------------------------------------------------------
1 | state = $state;
18 | }
19 |
20 | public function get(): bool
21 | {
22 | return $this->state->getMode() === State::MODE_PRODUCTION;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Order/Order.php:
--------------------------------------------------------------------------------
1 | checkoutSession = $checkoutSession;
34 | $this->orderRepository = $orderRepository;
35 | $this->config = $config;
36 | $this->priceFormatter = $priceFormatter;
37 | $this->orderTotals = $orderTotals;
38 | }
39 |
40 | /**
41 | * @return array
42 | */
43 | public function merge(): array
44 | {
45 | $order = $this->getOrder();
46 | return [
47 | 'currency' => (string)$order->getOrderCurrencyCode(),
48 | 'value' => $this->priceFormatter->format($this->orderTotals->getValueTotal($order)),
49 | 'tax' => $this->priceFormatter->format((float)$order->getTaxAmount()),
50 | 'shipping' => $this->priceFormatter->format($this->orderTotals->getShippingTotal($order)),
51 | 'affiliation' => $this->config->getStoreName(),
52 | 'transaction_id' => $order->getIncrementId(),
53 | 'coupon' => $order->getCouponCode(),
54 | 'payment_method' => $order->getPayment() ? $order->getPayment()->getMethod() : ''
55 | ];
56 | }
57 |
58 | /**
59 | * @return OrderInterface
60 | */
61 | private function getOrder(): OrderInterface
62 | {
63 | return $this->orderRepository->get($this->checkoutSession->getLastRealOrder()->getId());
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Order/OrderItems.php:
--------------------------------------------------------------------------------
1 | checkoutSession = $checkoutSession;
26 | $this->orderItemDataMapper = $orderItemDataMapper;
27 | }
28 |
29 | /**
30 | * @return array
31 | */
32 | public function get(): array
33 | {
34 | $order = $this->order;
35 | if (empty($order)) {
36 | $order = $this->checkoutSession->getLastRealOrder();
37 | }
38 |
39 | /** @var Order $order */
40 | $orderItemsData = [];
41 | foreach ($order->getAllVisibleItems() as $orderItem) {
42 | $orderItemsData[] = $this->orderItemDataMapper->mapByOrderItem($orderItem);
43 | }
44 |
45 | return $orderItemsData;
46 | }
47 |
48 | /**
49 | * @param OrderInterface $order
50 | * @return OrderItems
51 | */
52 | public function setOrder(OrderInterface $order): OrderItems
53 | {
54 | $this->order = $order;
55 | return $this;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Page/Breadcrumbs.php:
--------------------------------------------------------------------------------
1 | catalogHelper = $catalogHelper;
16 | }
17 |
18 | public function get(): array
19 | {
20 | $data = [];
21 | $breadcrumbs = $this->catalogHelper->getBreadcrumbPath();
22 | foreach ($breadcrumbs as $breadcrumb) {
23 | if (is_array($breadcrumb) && isset($breadcrumb['label'])) {
24 | $data[] = $breadcrumb['label'];
25 | }
26 | }
27 |
28 | return $data;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Page/VirtualPage.php:
--------------------------------------------------------------------------------
1 | storeManager = $storeManager;
17 | }
18 |
19 | public function get(): string
20 | {
21 | /** @var Store $store */
22 | $store = $this->storeManager->getStore();
23 | $url = $store->getCurrentUrl();
24 | $urlData = parse_url($url);
25 | return isset($urlData['path']) ? rtrim($urlData['path'], '/') : '';
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/DataLayer/Tag/PagePath.php:
--------------------------------------------------------------------------------
1 | url = $url;
16 | }
17 |
18 | public function get(): string
19 | {
20 | return $this->url->getCurrentUrl();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/DataLayer/Tag/PageTitle.php:
--------------------------------------------------------------------------------
1 | pageTitle = $pageTitle;
16 | }
17 |
18 | public function get(): string
19 | {
20 | return $this->pageTitle->get();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/DataLayer/Tag/PageType.php:
--------------------------------------------------------------------------------
1 | request = $request;
16 | }
17 |
18 | public function get(): string
19 | {
20 | $moduleName = $this->request->getModuleName();
21 | $controllerName = $this->request->getControllerName();
22 | $actionName = $this->request->getActionName();
23 | return $moduleName . '/' . $controllerName . '/' . $actionName;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Product/CurrentCategoryName.php:
--------------------------------------------------------------------------------
1 | getCurrentProduct = $getCurrentProduct;
23 | $this->productCategory = $productCategory;
24 | }
25 |
26 | /**
27 | * @return string
28 | * @throws NoSuchEntityException
29 | */
30 | public function get(): string
31 | {
32 | $currentProduct = $this->getCurrentProduct->get();
33 | return $this->productCategory->setProduct($currentProduct)->get();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Product/CurrentPrice.php:
--------------------------------------------------------------------------------
1 | getCurrentProduct = $getCurrentProduct;
26 | $this->priceFormatter = $priceFormatter;
27 | }
28 |
29 | /**
30 | * @return float
31 | * @throws NoSuchEntityException
32 | */
33 | public function get(): float
34 | {
35 | /** @var Product $product */
36 | $product = $this->getCurrentProduct->get();
37 | return $this->priceFormatter->format(
38 | (float) $product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue()
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Product/CurrentProduct.php:
--------------------------------------------------------------------------------
1 | getCurrentProduct = $getCurrentProduct;
26 | $this->productDataMapper = $productDataMapper;
27 | }
28 |
29 | /**
30 | * @return string[]
31 | * @throws NoSuchEntityException
32 | */
33 | public function merge(): array
34 | {
35 | return $this->productDataMapper->mapByProduct($this->getProduct());
36 | }
37 |
38 | /**
39 | * @return ProductInterface
40 | * @throws NoSuchEntityException
41 | */
42 | public function getProduct(): ProductInterface
43 | {
44 | if ($this->product instanceof ProductInterface) {
45 | return $this->product;
46 | }
47 |
48 | return $this->getCurrentProduct->get();
49 | }
50 |
51 | /**
52 | * @param ProductInterface $product
53 | * @return void
54 | */
55 | public function setProduct(ProductInterface $product)
56 | {
57 | $this->product = $product;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Product/ProductCategory.php:
--------------------------------------------------------------------------------
1 | categoryProvider = $categoryProvider;
23 | }
24 |
25 | /**
26 | * @param Product $product
27 | * @return $this
28 | */
29 | public function setProduct(Product $product): ProductCategory
30 | {
31 | $this->product = $product;
32 | return $this;
33 | }
34 |
35 | /**
36 | * @return string
37 | */
38 | public function get(): string
39 | {
40 | /** @var Category|null $category */
41 | $category = $this->product->getCategory();
42 | if (is_object($category) && $category instanceof CategoryInterface) {
43 | return $category->getName();
44 | }
45 |
46 | try {
47 | return $this->categoryProvider->getFirstByProduct($this->product)->getName();
48 | } catch (NoSuchEntityException $e) {
49 | return '';
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/DataLayer/Tag/Product/ProductTagInterface.php:
--------------------------------------------------------------------------------
1 | composerRegistrar = $composerRegistrar;
19 | }
20 |
21 | public function get(): string
22 | {
23 | $path = $this->composerRegistrar->getPath('module', 'Yireo_GoogleTagManager2');
24 | $composerPath = $path.'/composer.json';
25 | $composerData = json_decode(file_get_contents($composerPath), true);
26 | return $composerData['version'];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/DataLayer/TagParser.php:
--------------------------------------------------------------------------------
1 | $tagValue) {
22 | $data = $this->convertTag($tagName, $tagValue, $data);
23 | }
24 |
25 | foreach ($processors as $processor) {
26 | $data = array_replace_recursive($data, $processor->process($data));
27 | }
28 |
29 | return $data;
30 | }
31 |
32 | /**
33 | * @param string $tagName
34 | * @param mixed $tagValue
35 | * @param array $data
36 | * @return array
37 | */
38 | private function convertTag($tagName, $tagValue, array $data): array
39 | {
40 | if ($tagValue instanceof MergeTagInterface) {
41 | unset($data[$tagName]);
42 | return array_merge($data, $tagValue->merge());
43 | }
44 |
45 | if (is_object($tagValue)) {
46 | $data[$tagName] = $this->getValueFromFromTagValueObject($tagValue);
47 | return $data;
48 | }
49 |
50 | if (is_array($tagValue)) {
51 | foreach ($tagValue as $key => $value) {
52 | $tagValue = $this->convertTag($key, $value, $tagValue);
53 | }
54 |
55 | $data[$tagName] = $tagValue;
56 | return $data;
57 | }
58 |
59 | if (is_null($tagValue)) {
60 | unset($data[$tagName]);
61 | }
62 |
63 | return $data;
64 | }
65 |
66 | /**
67 | * @param ArgumentInterface $tagValueObject
68 | * @return mixed
69 | * @throws RuntimeException
70 | */
71 | private function getValueFromFromTagValueObject(ArgumentInterface $tagValueObject)
72 | {
73 | if ($tagValueObject instanceof TagInterface || $tagValueObject instanceof EventInterface) {
74 | return $tagValueObject->get();
75 | }
76 |
77 | throw new RuntimeException('Unknown object in data layer: ' . get_class($tagValueObject));
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Exception/BlockNotFound.php:
--------------------------------------------------------------------------------
1 | config = $config;
34 | $this->logger = $logger;
35 | }
36 |
37 | /**
38 | * @param string $msg
39 | * @param mixed $data
40 | *
41 | * @return bool
42 | */
43 | public function debug(string $msg, $data = null): bool
44 | {
45 | if ($this->config->isDebug() === false) {
46 | return false;
47 | }
48 |
49 | if (!empty($data)) {
50 | $msg .= ': ' . var_export($data, true);
51 | }
52 |
53 | $this->logger->notice($msg);
54 | return true;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Observer/AddAdditionalLayoutHandles.php:
--------------------------------------------------------------------------------
1 | request = $request;
19 | $this->layout = $layout;
20 | }
21 |
22 | public function execute(Observer $observer)
23 | {
24 | $handles = [];
25 | $handles[] = 'yireo_googletagmanager2';
26 | $handles[] = 'yireo_googletagmanager2_'.$this->getSystemPath();
27 |
28 | foreach ($handles as $handle) {
29 | $this->layout->getUpdate()->addHandle($handle);
30 | }
31 | }
32 |
33 | private function getSystemPath(): string
34 | {
35 | $parts = explode('/', $this->request->getFullActionName());
36 | return implode('_', array_slice($parts, 0, 3));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Observer/TriggerAddToCartDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | checkoutSessionDataProvider = $checkoutSessionDataProvider;
21 | $this->addToCartEvent = $addToCartEvent;
22 | }
23 |
24 | public function execute(Observer $observer)
25 | {
26 | /** @var ProductInterface $product */
27 | $product = $observer->getData('product');
28 | $qty = (int)$observer->getData('request')->getParam('qty');
29 | if ($qty === 0) {
30 | $qty = 1;
31 | }
32 |
33 | $this->checkoutSessionDataProvider->add(
34 | 'add_to_cart_event',
35 | $this->addToCartEvent->setProduct($product)->setQty($qty)->get()
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Observer/TriggerAddToWishlistDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | customerSessionDataProvider = $customerSessionDataProvider;
21 | $this->addToWishlistEvent = $addToWishlistEvent;
22 | }
23 |
24 | public function execute(Observer $observer)
25 | {
26 | /** @var ProductInterface $product */
27 | $product = $observer->getData('product');
28 | $this->customerSessionDataProvider->add(
29 | 'add_to_wishlist_event',
30 | $this->addToWishlistEvent->setProduct($product)->get()
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Observer/TriggerLoginDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | customerSessionDataProvider = $customerSessionDataProvider;
20 | $this->loginEvent = $loginEvent;
21 | }
22 |
23 | public function execute(Observer $observer)
24 | {
25 | $customer = $observer->getEvent()->getCustomer();
26 | $eventData = $this->loginEvent->setCustomer($customer)->get();
27 | $this->customerSessionDataProvider->add('login_event', $eventData);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Observer/TriggerLogoutDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | customerSessionDataProvider = $customerSessionDataProvider;
20 | $this->logoutEvent = $logoutEvent;
21 | }
22 |
23 | public function execute(Observer $observer)
24 | {
25 | $this->customerSessionDataProvider->add('logout_event', $this->logoutEvent->get());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Observer/TriggerPurchaseDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | checkoutSessionDataProvider = $checkoutSessionDataProvider;
21 | $this->purchaseEvent = $purchaseEvent;
22 | }
23 |
24 | public function execute(Observer $observer)
25 | {
26 | /** @var OrderInterface $order */
27 | $order = $observer->getData('order');
28 | $purchaseEventData = $this->purchaseEvent->setOrder($order)->get();
29 | if (empty($purchaseEventData)) {
30 | return;
31 | }
32 |
33 | $this->checkoutSessionDataProvider->add(
34 | 'purchase_event',
35 | $purchaseEventData
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Observer/TriggerRemoveFromCartDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | checkoutSessionDataProvider = $checkoutSessionDataProvider;
21 | $this->removeFromCartEvent = $removeFromCartEvent;
22 | }
23 |
24 | public function execute(Observer $observer)
25 | {
26 | /** @var CartItemInterface $quoteItem */
27 | $quoteItem = $observer->getData('quote_item');
28 | $this->checkoutSessionDataProvider->add(
29 | 'remove_from_cart_event',
30 | $this->removeFromCartEvent->setCartItem($quoteItem)->get()
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Observer/TriggerSignUpDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | customerSessionDataProvider = $customerSessionDataProvider;
20 | $this->signUpEvent = $signUpEvent;
21 | }
22 |
23 | public function execute(Observer $observer)
24 | {
25 | $eventData = $this->signUpEvent->get();
26 | $this->customerSessionDataProvider->add('sign_up_event', $eventData);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/PWA.md:
--------------------------------------------------------------------------------
1 | # PWA compatibility
2 | This frontend extension will be completely replaced by React or Vue functionality, and therefore, this extension will not be made compatible with PWA.
3 |
--------------------------------------------------------------------------------
/Plugin/AddCspInlineScripts.php:
--------------------------------------------------------------------------------
1 | replaceInlineScripts = $replaceInlineScripts;
16 | }
17 |
18 | public function afterToHtml(Template $block, $html): string
19 | {
20 | if (false === strstr((string)$block->getNameInLayout(), 'yireo_googletagmanager2.')) {
21 | return (string)$html;
22 | }
23 |
24 | return $this->replaceInlineScripts->replace((string)$html);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Plugin/AddDataToCartSection.php:
--------------------------------------------------------------------------------
1 | checkoutCart = $checkoutCart;
35 | $this->checkoutSessionDataProvider = $checkoutSessionDataProvider;
36 | $this->viewCartEvent = $viewCartEvent;
37 | }
38 |
39 | /**
40 | * @param CustomerData $subject
41 | * @param array $result
42 | * @return array
43 | */
44 | public function afterGetSectionData(CustomerData $subject, $result)
45 | {
46 | $quoteId = $this->checkoutCart->getQuote()->getId();
47 | if (empty($result) || !is_array($result) || empty($quoteId)) {
48 | return $result;
49 | }
50 |
51 | $gtmData = [];
52 |
53 | $gtmEvents = $this->checkoutSessionDataProvider->get();
54 | $gtmEvents['view_cart_event'] = $this->viewCartEvent->get();
55 | $this->checkoutSessionDataProvider->clear();
56 |
57 | return array_merge($result, ['gtm' => $gtmData, 'gtm_events' => $gtmEvents]);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Plugin/AddProductDetails.php:
--------------------------------------------------------------------------------
1 | layout = $layout;
19 | }
20 |
21 | /**
22 | * @param AbstractProduct $abstractProduct
23 | * @param mixed $html
24 | * @param ProductInterface $product
25 | * @return string
26 | */
27 | public function afterGetProductDetailsHtml(AbstractProduct $abstractProduct, $html, ProductInterface $product)
28 | {
29 | try {
30 | $block = $this->getProductDetailsBlock();
31 | } catch (BlockNotFound $blockNotFound) {
32 | return $html;
33 | }
34 |
35 | $html .= $block->setData('product', $product)->toHtml();
36 | return $html;
37 | }
38 |
39 | /**
40 | * @return AbstractBlock
41 | * @throws BlockNotFound
42 | */
43 | private function getProductDetailsBlock(): AbstractBlock
44 | {
45 | $block = $this->layout->getBlock('yireo_googletagmanager2.product-details');
46 | if ($block instanceof AbstractBlock) {
47 | return $block;
48 | }
49 |
50 | throw new BlockNotFound('Block "yireo_googletagmanager2.product-details" not found');
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Plugin/GetProductsFromCategoryBlockPlugin.php:
--------------------------------------------------------------------------------
1 | categorySize = $categorySize;
27 | $this->getCurrentCategoryProducts = $getCurrentCategoryProducts;
28 | $this->config = $config;
29 | }
30 |
31 | /**
32 | * @param ListProduct $listProductBlock
33 | * @param AbstractCollection $collection
34 | * @return AbstractCollection
35 | */
36 | public function afterGetLoadedProductCollection(
37 | ListProduct $listProductBlock,
38 | AbstractCollection $collection
39 | ): AbstractCollection {
40 | $maximumCategoryProducts = $this->config->getMaximumCategoryProducts();
41 | if ($maximumCategoryProducts <= 0) {
42 | return $collection;
43 | }
44 | $i = 0;
45 | foreach ($collection as $product) {
46 | if ($i > $maximumCategoryProducts) {
47 | break;
48 | }
49 |
50 | $this->getCurrentCategoryProducts->addProduct($product);
51 | $i++;
52 | }
53 |
54 | $this->categorySize->setSize($collection->count());
55 | return $collection;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Plugin/TriggerAddGuestPaymentInfoDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | checkoutSessionDataProvider = $checkoutSessionDataProvider;
24 | $this->addPaymentInfo = $addPaymentInfo;
25 | $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
26 | }
27 |
28 | public function afterSavePaymentInformationAndPlaceOrder(
29 | GuestPaymentInformationManagementInterface $subject,
30 | $orderId,
31 | $cartId,
32 | $email,
33 | PaymentInterface $paymentMethod,
34 | ?AddressInterface $billingAddress = null
35 | ) {
36 | $cartId = $this->maskedQuoteIdToQuoteId->execute($cartId);
37 | $addPaymentInfoEventData = $this->addPaymentInfo
38 | ->setPaymentMethod($paymentMethod->getMethod())
39 | ->setCartId((int)$cartId)
40 | ->get();
41 | $this->checkoutSessionDataProvider->add('add_payment_info_event', $addPaymentInfoEventData);
42 |
43 | return $orderId;
44 | }
45 |
46 | public function afterSavePaymentInformation(
47 | GuestPaymentInformationManagementInterface $subject,
48 | $orderId,
49 | $cartId,
50 | $email,
51 | PaymentInterface $paymentMethod,
52 | ?AddressInterface $billingAddress = null
53 | ) {
54 | $cartId = $this->maskedQuoteIdToQuoteId->execute($cartId);
55 | $addPaymentInfoEventData = $this->addPaymentInfo
56 | ->setPaymentMethod($paymentMethod->getMethod())
57 | ->setCartId((int)$cartId)
58 | ->get();
59 | $this->checkoutSessionDataProvider->add('add_payment_info_event', $addPaymentInfoEventData);
60 |
61 | return $orderId;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Plugin/TriggerAddShippingInfoDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | checkoutSessionDataProvider = $checkoutSessionDataProvider;
21 | $this->addShippingInfo = $addShippingInfo;
22 | }
23 |
24 | /**
25 | * @param ShippingInformationManagementInterface $subject
26 | * @param PaymentDetailsInterface $paymentDetails
27 | * @param mixed $cartId
28 | * @param ShippingInformationInterface $addressInformation
29 | * @return PaymentDetailsInterface
30 | */
31 | public function afterSaveAddressInformation(
32 | ShippingInformationManagementInterface $subject,
33 | PaymentDetailsInterface $paymentDetails,
34 | $cartId,
35 | ShippingInformationInterface $addressInformation
36 | ) {
37 |
38 | $event = $this->addShippingInfo->get();
39 | if (array_key_exists('event', $event)) {
40 | $this->checkoutSessionDataProvider->add('add_shipping_info_event', $event);
41 | }
42 |
43 | return $paymentDetails;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Plugin/TriggerViewSearchResultDataLayerEvent.php:
--------------------------------------------------------------------------------
1 | viewSearchResultEvent = $viewSearchResultEvent;
19 | $this->customerSessionDataProvider = $customerSessionDataProvider;
20 | }
21 |
22 | public function afterExecute(Index $subject, $return)
23 | {
24 | $searchTerm = $subject->getRequest()->getParam('q');
25 | $this->viewSearchResultEvent->setSearchTerm($searchTerm);
26 | $this->customerSessionDataProvider->add('view_search_result', $this->viewSearchResultEvent->get());
27 | return $return;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SessionDataProvider/CheckoutSessionDataProvider.php:
--------------------------------------------------------------------------------
1 | checkoutSession = $checkoutSession;
20 | $this->debugger = $debugger;
21 | }
22 |
23 | public function add(string $identifier, array $data)
24 | {
25 | $gtmData = $this->get();
26 | $gtmData[$identifier] = $data;
27 | $this->debugger->debug('CheckoutSessionDataProvider::add(): ' . $identifier, $data);
28 | $this->checkoutSession->setYireoGtmData($gtmData);
29 | }
30 |
31 | public function get(): array
32 | {
33 | $gtmData = $this->checkoutSession->getYireoGtmData();
34 | if (is_array($gtmData)) {
35 | return $gtmData;
36 | }
37 |
38 | return [];
39 | }
40 |
41 | public function clear()
42 | {
43 | $this->checkoutSession->setYireoGtmData([]);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/SessionDataProvider/CustomerSessionDataProvider.php:
--------------------------------------------------------------------------------
1 | customerSession = $customerSession;
19 | $this->debugger = $debugger;
20 | }
21 |
22 | public function add(string $identifier, array $data)
23 | {
24 | $gtmData = $this->get();
25 | $gtmData[$identifier] = $data;
26 | $this->debugger->debug('CustomerSessionDataProvider::add(): ' . $identifier, $data);
27 | $this->customerSession->setYireoGtmData($gtmData);
28 | }
29 |
30 | public function get(): array
31 | {
32 | $gtmData = $this->customerSession->getYireoGtmData();
33 | if (is_array($gtmData)) {
34 | return $gtmData;
35 | }
36 |
37 | return [];
38 | }
39 |
40 | public function clear()
41 | {
42 | $this->customerSession->setYireoGtmData([]);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/TESTING.md:
--------------------------------------------------------------------------------
1 | # Testing
2 | ## Unit testing
3 | This extension ships with PHPUnit tests. The generic PHPUnit configuration in Magento 2 will pick up on these
4 | tests. To only test Yireo extensions, simply run PHPUnit from within this folder. Note that this assumes that
5 | the extension is installed via composer. For instance:
6 |
7 | phpunit
8 |
9 | Also note that Mockery (`mockery/mockery`) is used for the integration tests. If you want to test this module, you need to install its dev-dependencies:
10 |
11 | composer require yireo/magento2-googletagmanager2 --dev
12 |
13 | The JavaScript code ships with MochaJS unit tests. To install the stuff, simply run (within this extension directory) the following:
14 |
15 | npm install
16 | npm run mocha
17 |
18 | Or just use `yarn`:
19 |
20 | yarn
21 | npm run mocha
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Test/Functional/BaseTest.php:
--------------------------------------------------------------------------------
1 | get(ComponentRegistrar::class);
15 | $path = $componentRegistrar->getPath('module', 'Yireo_GoogleTagManager2');
16 | $this->assertTrue(file_exists($path.'/registration.php'));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Test/Functional/Util/CategoryProviderTest.php:
--------------------------------------------------------------------------------
1 | expectException(NotUsingSetProductSkusException::class);
17 | $categoryProvider = ObjectManager::getInstance()->get(CategoryProvider::class);
18 | $categoryProvider->getLoadedCategories();
19 | }
20 |
21 | public function testGetById()
22 | {
23 | $categoryProvider = ObjectManager::getInstance()->get(CategoryProvider::class);
24 | $categoryProvider->addCategoryIds([11, 38]);
25 | $category = $categoryProvider->getById(11);
26 | $this->assertEquals(11, $category->getId());
27 | }
28 |
29 | public function testGetCategories()
30 | {
31 | $categoryProvider = ObjectManager::getInstance()->get(CategoryProvider::class);
32 | $categoryProvider->addCategoryIds([11, 38]);
33 | $categories = $categoryProvider->getLoadedCategories();
34 | $this->assertEquals(2, count($categories));
35 |
36 | $categoryProvider->addCategoryIds(['12', 14]);
37 | $categories = $categoryProvider->getLoadedCategories();
38 | $this->assertEquals(4, count($categories));
39 |
40 | $categoryProvider->addCategoryIds([12, 14]);
41 | $categories = $categoryProvider->getLoadedCategories();
42 | $this->assertEquals(4, count($categories));
43 | }
44 |
45 | public function testGetByProduct()
46 | {
47 | $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
48 | $product = $productRepository->getById(1);
49 |
50 | $categoryProvider = ObjectManager::getInstance()->get(CategoryProvider::class);
51 | $categoryProvider->addCategoryIds([11, 38]);
52 | $categories = $categoryProvider->getAllByProduct($product);
53 | $this->assertEquals(2, count($categories));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Test/Integration/Block/ScriptTest.php:
--------------------------------------------------------------------------------
1 | assertEnabledFlagIsWorking();
26 |
27 | $this->layout->getUpdate()->addPageHandles(['empty', '1column']);
28 | $this->layout->generateXml();
29 |
30 | $this->dispatch('/');
31 |
32 | $this->assertContainerInLayout('before.body.end');
33 | $this->assertStringContainsString('Yireo_GoogleTagManager2', $this->layout->getUpdate()->asString());
34 |
35 | $body = $this->getResponse()->getBody(); // @phpstan-ignore-line
36 | $this->assertTrue((bool)strpos($body, 'yireoGoogleTagManager'), 'Script not found in HTML body: ' . $body);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Test/Integration/DataLayer/Event/AddToCartTest.php:
--------------------------------------------------------------------------------
1 | getProductBySku('simple1002');
30 | $addToCartEvent = ObjectManager::getInstance()->get(AddToCart::class);
31 | $data = $addToCartEvent->setProduct($product)->get();
32 | $this->assertCount(1, $data['ecommerce']['items']);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Test/Integration/DataLayer/Mapper/CategoryDataMapperTest.php:
--------------------------------------------------------------------------------
1 | getCategoryByName('Category 999');
25 | $categoryDataMapper = ObjectManager::getInstance()->get(CategoryDataMapper::class);
26 | $categoryData = $categoryDataMapper->mapByCategory($category);
27 | $this->assertEquals('Category 999', $categoryData['category_name']);
28 | $this->assertEquals($category->getId(), $categoryData['category_id']);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Test/Integration/DataLayer/Mapper/ProductDataMapperTest.php:
--------------------------------------------------------------------------------
1 | getProductBySku('simple1002');
25 | $productDataMapper = ObjectManager::getInstance()->get(ProductDataMapper::class);
26 | $productData = $productDataMapper->mapByProduct($product);
27 | $this->assertArrayHasKey('item_name', $productData);
28 | $this->assertStringContainsString('Simple Product', $productData['item_name']);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Test/Integration/DataLayer/Tag/VersionTest.php:
--------------------------------------------------------------------------------
1 | get(Version::class);
14 | $this->assertTrue((bool)preg_match('/^([0-9]+)\.([0-9]+)\.([0-9]+)$/', $version->get()));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Test/Integration/DataLayer/TagParserTest.php:
--------------------------------------------------------------------------------
1 | 'example1',
16 | 'example2' => null,
17 | ];
18 |
19 | $tagParser = ObjectManager::getInstance()->get(TagParser::class);
20 | $data = $tagParser->parse($data, []);
21 | $this->assertTrue(count($data) === 1);
22 | }
23 |
24 | public function testIfAddedTagsAreCorrect()
25 | {
26 | $mock = $this->createMock(TagInterface::class);
27 | $mock->method('get')->willReturn(['exampleKey' => 'exampleValue']);
28 | $data = [
29 | 'example1' => $mock
30 | ];
31 |
32 | $tagParser = ObjectManager::getInstance()->get(TagParser::class);
33 | $data = $tagParser->parse($data, []);
34 | $this->assertTrue(count($data) === 1);
35 | $this->assertTrue(isset($data['example1']));
36 | $this->assertTrue(is_array($data['example1']));
37 | $this->assertEquals('exampleValue', $data['example1']['exampleKey'], var_export($data, true));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Test/Integration/FixtureTrait/CreateCustomer.php:
--------------------------------------------------------------------------------
1 | get(CustomerRepositoryInterface::class);
21 | try {
22 | return $customerRepository->getById($id);
23 | } catch (NoSuchEntityException $e) {
24 | } catch (LocalizedException $e) {
25 | }
26 |
27 | /** @var Customer $customer */
28 | $customer = $objectManager->create(Customer::class);
29 | $customer->setWebsiteId(1)
30 | ->setId($id)
31 | ->setEmail('customer@example.com')
32 | ->setPassword('password')
33 | ->setGroupId(1)
34 | ->setStoreId(1)
35 | ->setIsActive(1)
36 | ->setPrefix('Mr.')
37 | ->setFirstname('John')
38 | ->setMiddlename('A')
39 | ->setLastname('Smith')
40 | ->setSuffix('Esq.')
41 | ->setDefaultBilling(1)
42 | ->setDefaultShipping(1)
43 | ->setTaxvat('12')
44 | ->setGender(0)
45 | ->addData($data);
46 |
47 | $customer->isObjectNew(true);
48 | // @phpstan-ignore-next-line
49 | $customer->save();
50 |
51 | $customerRegistry = $objectManager->get(CustomerRegistry::class);
52 | $customerRegistry->remove($customer->getId());
53 | return $customerRepository->getById($customer->getId());
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Test/Integration/FixtureTrait/GetCategory.php:
--------------------------------------------------------------------------------
1 | get(GetCategoryByName::class);
18 | return $getCategoryByName->execute($categoryName);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Test/Integration/FixtureTrait/GetCustomer.php:
--------------------------------------------------------------------------------
1 | get(CustomerRepositoryInterface::class);
22 | return $customerRepository->getById($customerId);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Test/Integration/FixtureTrait/GetOrder.php:
--------------------------------------------------------------------------------
1 | get(OrderRepositoryInterface::class);
18 | $searchCriteriaBuilder = ObjectManager::getInstance()->get(SearchCriteriaBuilder::class);
19 | $searchCriteriaBuilder->setPageSize(1);
20 | $searchItems = $orderRepository->getList($searchCriteriaBuilder->create());
21 | $orders = $searchItems->getItems();
22 | return array_shift($orders);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Test/Integration/FixtureTrait/GetProduct.php:
--------------------------------------------------------------------------------
1 | get(ProductRepositoryInterface::class);
22 | return $productRepository->getById($productId);
23 | }
24 |
25 | /**
26 | * @param string $productSku
27 | * @return ProductInterface
28 | * @throws NoSuchEntityException
29 | */
30 | public function getProductBySku(string $productSku): ProductInterface
31 | {
32 | $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
33 | return $productRepository->get($productSku);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Test/Integration/FixtureTrait/Reindex.php:
--------------------------------------------------------------------------------
1 | get(IndexerFactory::class);
15 | $indexer = $indexerFactory->create()->load($indexerId);
16 | $indexer->reindexAll();
17 | }
18 |
19 | public function reindexAll()
20 | {
21 | $objectManager = ObjectManager::getInstance();
22 | $config = $objectManager->get(ConfigInterface::class);
23 | $indexerFactory = $objectManager->get(IndexerFactory::class);
24 | foreach (array_keys($config->getIndexers()) as $indexerId) {
25 | $indexer = $indexerFactory->create()->load($indexerId);
26 | $indexer->reindexAll();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Test/Integration/ModuleTest.php:
--------------------------------------------------------------------------------
1 | assertModuleIsEnabled('Yireo_GoogleTagManager2');
19 | $this->assertModuleIsRegistered('Yireo_GoogleTagManager2');
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Test/Integration/Observer/TriggerLoginDataLayerEventTest.php:
--------------------------------------------------------------------------------
1 | createCustomer();
34 |
35 | ObjectManager::getInstance()->get(CheckoutSessionDataProvider::class)->clear();
36 | $customer = $this->getCustomer();
37 |
38 | $eventManager = ObjectManager::getInstance()->get(ManagerInterface::class);
39 | $eventManager->dispatch('customer_data_object_login', ['customer' => $customer]);
40 |
41 | $data = ObjectManager::getInstance()->get(CustomerSessionDataProvider::class)->get();
42 | $this->assertArrayHasKey('login_event', $data, var_export($data, true));
43 | $this->assertArrayHasKey('event', $data['login_event']);
44 | $this->assertEquals('login', $data['login_event']['event']);
45 | }
46 | }
--------------------------------------------------------------------------------
/Test/Integration/Observer/TriggerLogoutDataLayerEventTest.php:
--------------------------------------------------------------------------------
1 | createCustomer();
34 |
35 | ObjectManager::getInstance()->get(CheckoutSessionDataProvider::class)->clear();
36 | $customer = $this->getCustomer();
37 |
38 | $eventManager = ObjectManager::getInstance()->get(ManagerInterface::class);
39 | $eventManager->dispatch('customer_logout', ['customer' => $customer]);
40 |
41 | $data = ObjectManager::getInstance()->get(CustomerSessionDataProvider::class)->get();
42 | $this->assertArrayHasKey('logout_event', $data, var_export($data, true));
43 | $this->assertArrayHasKey('event', $data['logout_event']);
44 | $this->assertEquals('logout', $data['logout_event']['event']);
45 | }
46 | }
--------------------------------------------------------------------------------
/Test/Integration/Observer/TriggerPurchaseDataLayerEventTest.php:
--------------------------------------------------------------------------------
1 | get(CheckoutSessionDataProvider::class)->clear();
25 | $order = $this->getOrder();
26 |
27 | $eventManager = ObjectManager::getInstance()->get(ManagerInterface::class);
28 | $eventManager->dispatch('sales_order_place_after', ['order' => $order]);
29 |
30 | $data = ObjectManager::getInstance()->get(CheckoutSessionDataProvider::class)->get();
31 | $this->assertArrayHasKey('purchase_event', $data);
32 | $this->assertArrayHasKey('event', $data['purchase_event']);
33 | $this->assertEquals('purchase', $data['purchase_event']['event']);
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/Test/Integration/Page/LoginTest.php:
--------------------------------------------------------------------------------
1 | createCustomer();
24 |
25 | /** @var HttpRequest $request */
26 | $request = $this->getRequest();
27 |
28 | $request->setPostValue([
29 | 'login' => [
30 | 'username' => 'customer@example.com',
31 | 'password' => 'password',
32 | ],
33 | 'formKey' => $this->objectManager->get(FormKey::class)->getFormKey(),
34 | ]);
35 | $request->setMethod(HttpRequest::METHOD_POST);
36 | $this->dispatch('customer/account/loginPost');
37 |
38 | $customerSectionPool = $this->objectManager->get(SectionPool::class);
39 | $data = $customerSectionPool->getSectionsData(['customer']);
40 |
41 | $this->assertArrayHasKey('login_event', $data['customer']['gtm_events']);
42 | $this->assertEquals('login', $data['customer']['gtm_events']['login_event']['event']);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Test/Integration/Page/LogoutTest.php:
--------------------------------------------------------------------------------
1 | createCustomer();
23 | $this->loginCustomer();
24 |
25 | $this->objectManager->get(CustomerSessionDataProvider::class)->clear();
26 |
27 | $this->dispatch('customer/account/logout');
28 |
29 | $customerSectionPool = $this->objectManager->get(SectionPool::class);
30 | $data = $customerSectionPool->getSectionsData(['customer']);
31 |
32 | $this->assertArrayHasKey('logout_event', $data['customer']['gtm_events']);
33 | $this->assertEquals('logout', $data['customer']['gtm_events']['logout_event']['event']);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Test/Integration/Page/ProductPageTest.php:
--------------------------------------------------------------------------------
1 | assertEnabledFlagIsWorking();
32 |
33 | $product = $this->getProductBySku('simple1002');
34 |
35 | $this->dispatch('catalog/product/view/id/' . $product->getId());
36 | $this->assertRequestActionName('view');
37 |
38 | $body = $this->getResponse()->getBody();
39 | $this->assertStringContainsString($product->getName(), $body);
40 |
41 | $block = $this->layout->getBlock('yireo_googletagmanager2.data-layer');
42 | $this->assertNotEmpty($block);
43 |
44 | $this->assertDataLayerEquals('product', 'page_type');
45 |
46 | $event = $this->getEventFromDataLayerEvents('view_item_event', 'view_item');
47 | $this->assertArrayHasKey('ecommerce', $event);
48 | $this->assertNotEmpty($event['ecommerce']['items']);
49 |
50 | $productData = array_shift($event['ecommerce']['items']);
51 | $this->assertNonEmptyValueInArray('item_name', $productData);
52 | $this->assertNonEmptyValueInArray('item_id', $productData);
53 | $this->assertNonEmptyValueInArray('price', $productData);
54 | $this->assertNonEmptyValueInArray('item_list_id', $productData);
55 | $this->assertNonEmptyValueInArray('item_list_name', $productData);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Test/Integration/Plugin/AddDataToCartSectionTest.php:
--------------------------------------------------------------------------------
1 | assertInterceptorPluginIsRegistered(
20 | Cart::class,
21 | AddDataToCartSection::class,
22 | 'Yireo_GoogleTagManager2::addAdditionalDataToCartSection'
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Test/Integration/Plugin/AddDataToCustomerSectionTest.php:
--------------------------------------------------------------------------------
1 | assertInterceptorPluginIsRegistered(
20 | Customer::class,
21 | AddDataToCustomerSection::class,
22 | 'Yireo_GoogleTagManager2::addDataToCustomerSection'
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Test/Integration/Plugin/GetProductsFromCategoryBlockPluginTest.php:
--------------------------------------------------------------------------------
1 | assertInterceptorPluginIsRegistered(
20 | ListProductBlock::class,
21 | GetProductsFromCategoryBlockPlugin::class,
22 | 'Yireo_GoogleTagManager2::getProductsFromCategoryBlockPlugin'
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Test/Integration/Plugin/TriggerAddShippingInfoDataLayerEventTest.php:
--------------------------------------------------------------------------------
1 | assertInterceptorPluginIsRegistered(
20 | ShippingInformationManagementInterface::class,
21 | TriggerAddShippingInfoDataLayerEvent::class,
22 | 'Yireo_GoogleTagManager2::triggerAddShippingInfoDataLayerEvent'
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Test/Integration/SessionDataProvider/CustomerSessionDataProviderTest.php:
--------------------------------------------------------------------------------
1 | get(SerializerInterface::class);
19 |
20 | $customerSessionDataProvider = ObjectManager::getInstance()->get(CustomerSessionDataProvider::class);
21 | $customerSessionDataProvider->add('foobar', ['foo' => 'bar']);
22 |
23 | $this->getRequest()->setParams(['sections' => 'customer']);
24 | $this->dispatch('customer/section/load');
25 | $body = $this->getResponse()->getBody(); // @phpstan-ignore-line
26 | $data = $serializer->unserialize($body, true);
27 | $this->assertEquals('bar', $data['customer']['gtm_events']['foobar']['foo']);
28 |
29 | $this->getRequest()->setParams(['sections' => 'customer']);
30 | $this->dispatch('customer/section/load');
31 | $body = $this->getResponse()->getBody(); // @phpstan-ignore-line
32 | $data = $serializer->unserialize($body, true);
33 | $this->assertEmpty($data['customer']['gtm_events']);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Test/Integration/Stub/FulltextStub.php:
--------------------------------------------------------------------------------
1 | createProduct(
19 | 1,
20 | 'Product 1',
21 | 'product1',
22 | 1.42,
23 | );
24 |
25 | $productDataMapper = ObjectManager::getInstance()->get(ProductDataMapper::class);
26 | $productData = $productDataMapper->mapByProduct($product);
27 |
28 | $this->assertNonEmptyValueInArray('item_id', $productData);
29 | $this->assertSame(1, $productData['magento_id']);
30 | $this->assertSame('product1', $productData['magento_sku']);
31 | $this->assertSame('product1', $productData['item_id']);
32 | $this->assertSame('product1', $productData['item_sku']);
33 | $this->assertSame('Product 1', $productData['item_name']);
34 | $this->assertSame(1.42, $productData['price']);
35 | }
36 |
37 | /**
38 | * @param int $id
39 | * @param string $name
40 | * @param string $sku
41 | * @param float $price
42 | * @return ProductInterface
43 | */
44 | private function createProduct(int $id, string $name, string $sku, float $price): ProductInterface
45 | {
46 | $productFactory = ObjectManager::getInstance()->get(ProductInterfaceFactory::class);
47 | $product = $productFactory->create();
48 | $product->setId($id);
49 | $product->setName($name);
50 | $product->setSku($sku);
51 | $product->setPrice($price);
52 | return $product;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Test/Unit/Util/CamelCaseTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('Foobar', $camelCase->to('foobar'));
26 | $this->assertEquals('FooBar', $camelCase->to('foo_bar'));
27 | $this->assertEquals('FooBar', $camelCase->to('Foo_Bar'));
28 | }
29 |
30 | public function testFromCamelCase()
31 | {
32 | $camelCase = new CamelCase();
33 | $this->assertEquals('foobar', $camelCase->from('foobar'));
34 | $this->assertEquals('foo_bar', $camelCase->from('fooBar'));
35 | $this->assertEquals('foo_bar', $camelCase->from('FooBar'));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Util/CamelCase.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2022 Yireo (https://www.yireo.com/)
8 | * @license Open Source License (OSL v3)
9 | */
10 |
11 | namespace Yireo\GoogleTagManager2\Util;
12 |
13 | class CamelCase
14 | {
15 | /**
16 | * @param string $string
17 | * @return string
18 | */
19 | public function from(string $string): string
20 | {
21 | return strtolower(trim(preg_replace('/([A-Z]|[0-9]+)/', "_$1", $string), '_'));
22 | }
23 |
24 | /**
25 | * @param string $string
26 | * @return string
27 | */
28 | public function to(string $string): string
29 | {
30 | return str_replace('_', '', ucwords($string, '_'));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Util/Debug.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright 2017 Yireo (https://www.yireo.com/)
8 | * @license Open Source License (OSL v3)
9 | */
10 |
11 | namespace Yireo\GoogleTagManager2\Util;
12 |
13 | use Psr\Log\LoggerInterface;
14 | use Yireo\GoogleTagManager2\Config\Config;
15 |
16 | class Debug
17 | {
18 | /**
19 | * @var Config
20 | */
21 | private $config;
22 |
23 | /**
24 | * @var LoggerInterface
25 | */
26 | private $logger;
27 |
28 | /**
29 | * Data constructor.
30 | *
31 | * @param Config $config
32 | * @param LoggerInterface $logger
33 | */
34 | public function __construct(
35 | Config $config,
36 | LoggerInterface $logger
37 | ) {
38 | $this->config = $config;
39 | $this->logger = $logger;
40 | }
41 |
42 | /**
43 | * Debugging method
44 | *
45 | * @param string $string String to debug
46 | * @param mixed|null $variable Tag to dump to debug message
47 | *
48 | * @return bool
49 | */
50 | public function debug(string $string, $variable = null)
51 | {
52 | if ($this->config->isDebug() === false) {
53 | return false;
54 | }
55 |
56 | if ($variable !== null) {
57 | $string .= ': ' . var_export($variable, true);
58 | }
59 |
60 | $this->logger->info('Yireo_GoogleTagManager: ' . $string);
61 |
62 | return true;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Util/GetCategoryPath.php:
--------------------------------------------------------------------------------
1 | categoryRepository = $categoryRepository;
29 | }
30 |
31 | /**
32 | * @param ?CategoryModel $category
33 | * @return string
34 | */
35 | public function getCategoryPath(?CategoryModel $category = null): string
36 | {
37 | if (!$category instanceof CategoryModel) {
38 | return "direct";
39 | }
40 |
41 | $categoryPath = $category->getPath();
42 | $categoryIdArray = explode('/', $categoryPath);
43 | $categoryNames = [];
44 |
45 | foreach ($categoryIdArray as $categoryId) {
46 | if (!in_array($categoryId, self::ROOT_CATEGORY_IDS)) {
47 | try {
48 | $category = $this->categoryRepository->get($categoryId);
49 | } catch (NoSuchEntityException $e) {
50 | continue;
51 | }
52 | $categoryNames[] = $category->getName();
53 | }
54 | }
55 | return implode('/', $categoryNames);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Util/GetCurrentCategory.php:
--------------------------------------------------------------------------------
1 | request = $request;
24 | $this->categoryRepository = $categoryRepository;
25 | $this->storeManager = $storeManager;
26 | }
27 |
28 | /**
29 | * @return CategoryInterface
30 | * @throws NoSuchEntityException
31 | */
32 | public function get(): CategoryInterface
33 | {
34 | $categoryId = (int)$this->request->getParam('id');
35 | try {
36 | $category = $this->categoryRepository->get($categoryId);
37 | } catch (NoSuchEntityException $e) {
38 | /** @var Store $store */
39 | $store = $this->storeManager->getStore();
40 | $category = $this->categoryRepository->get($store->getRootCategoryId());
41 | }
42 |
43 | return $category;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Util/GetCurrentCategoryProducts.php:
--------------------------------------------------------------------------------
1 | products[$product->getId()] = $product;
14 | }
15 |
16 | /**
17 | * @return array
18 | */
19 | public function getProducts(): array
20 | {
21 | return $this->products;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Util/GetCurrentProduct.php:
--------------------------------------------------------------------------------
1 | request = $request;
24 | $this->productRepository = $productRepository;
25 | $this->storeManager = $storeManager;
26 | }
27 |
28 | /**
29 | * @return ProductInterface
30 | * @throws NoSuchEntityException
31 | */
32 | public function get(): ProductInterface
33 | {
34 | $productId = (int)$this->request->getParam('id');
35 | if ($this->request->getActionName() === 'configure' || empty($productId)) {
36 | $productId = (int)$this->request->getParam('product_id');
37 | }
38 |
39 | return $this->productRepository->getById($productId, false, $this->storeManager->getStore()->getId());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Util/OrderTotals.php:
--------------------------------------------------------------------------------
1 | getSubtotal() - abs((float)$order->getDiscountAmount());
13 | }
14 |
15 | public function getShippingTotal(OrderInterface $order): float
16 | {
17 | return (float)$order->getShippingAmount() - (float)$order->getShippingDiscountAmount();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Util/PriceFormatter.php:
--------------------------------------------------------------------------------
1 | dataLayer = $dataLayer;
41 | $this->config = $config;
42 | $this->serializer = $serializer;
43 | }
44 |
45 | /**
46 | * @return array
47 | */
48 | public function getConfiguration(): array
49 | {
50 | $configuration = [];
51 | $configuration['cookie_restriction_mode'] = $this->config->getCookieRestrictionModeName();
52 | $configuration['website_id'] = $this->config->getCurrentWebsiteId();
53 | $configuration['data_layer'] = $this->dataLayer->getDataLayer();
54 | $configuration['id'] = $this->config->getId();
55 | $configuration['debug'] = $this->config->isDebug();
56 | $configuration['gtm_url'] = $this->config->getGoogleTagmanagerUrl();
57 | return $configuration;
58 | }
59 |
60 | /**
61 | * @return string
62 | */
63 | public function getConfigurationAsJson(): string
64 | {
65 | return $this->serializer->serialize($this->getConfiguration());
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/etc/cache.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | XML definitions for data layer construction
7 |
8 |
9 |
--------------------------------------------------------------------------------
/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0
7 |
8 | 0
9 | 0
10 | 100
11 | material
12 | id,name
13 | id
14 | everywhere
15 | 1
16 | product_first_category
17 | 0
18 |
19 | new,payment_review,pending_payment,holded,processing,complete
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/etc/csp_whitelist.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | *.googletagmanager.com
8 | tagmanager.google.com
9 |
10 |
11 |
12 |
13 | *.google-analytics.com
14 | *.analytics.google.com
15 | *.googletagmanager.com
16 |
17 |
18 |
19 |
20 | tagmanager.google.com
21 | fonts.google.com
22 |
23 |
24 |
25 |
26 | *.googletagmanager.com
27 | *.google-analytics.com
28 | www.googletagmanager.com
29 | ssl.gstatic.com
30 | www.gstatic.com
31 |
32 |
33 |
34 |
35 | fonts.gstatic.com
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/etc/data_layer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
17 |
18 |
--------------------------------------------------------------------------------
/etc/data_layer.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/etc/frontend/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | - Yireo\GoogleTagManager2\CustomerData\GtmCheckout
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/etc/frontend/events.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/etc/frontend/sections.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/etc/view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - Yireo_GoogleTagManager2::js
5 |
6 |
7 |
--------------------------------------------------------------------------------
/etc/webapi_rest/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Yireo_GoogleTagManager2",
3 | "repository": {
4 | "type": "git",
5 | "url": "https://github.com/yireo/Yireo_GoogleTagManager2.git"
6 | },
7 | "devDependencies": {
8 | "amd-loader": "^0.0.8",
9 | "casperjs": "^1.1",
10 | "jsdom": "^11.0",
11 | "mocha": "10.1.0",
12 | "requirejs": "^2.3"
13 | },
14 | "scripts": {
15 | "mocha": "./node_modules/mocha/bin/mocha view/frontend/web/js/test/mocha",
16 | "casper": "./node_modules/casperjs/bin/casperjs --engine=slimerjs view/frontend/web/js/test/casper/index.js"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | excludePaths:
3 | - */Test/*/*
4 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | Test/Unit
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | *
17 |
18 | Test
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
-
9 |
-
10 |
- Yireo_GoogleTagManager2/js/generic
11 | - true
12 |
13 | -
14 |
- Yireo_GoogleTagManager2/js/push
15 | - true
16 |
17 | -
18 |
- Yireo_GoogleTagManager2/js/product/clicks
19 | - true
20 |
21 | -
22 |
- Yireo_GoogleTagManager2/js/logger
23 | - true
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/view/frontend/layout/catalog_product_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
13 |
14 |
15 |
16 |
17 | - Yireo\GoogleTagManager2\DataLayer\Processor\Product
18 |
19 |
20 |
21 | -
22 |
- view_item
23 | -
24 |
-
25 |
-
26 |
- Yireo\GoogleTagManager2\DataLayer\Tag\Product\CurrentProduct
27 |
28 |
29 | - Yireo\GoogleTagManager2\DataLayer\Tag\Product\CurrentPrice
30 | - Yireo\GoogleTagManager2\DataLayer\Tag\CurrencyCode
31 |
32 |
33 |
34 |
35 |
36 | - product
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Yireo\GoogleTagManager2\DataLayer\Tag\Product\CurrentProduct
45 | Yireo\GoogleTagManager2\ViewModel\DataLayer
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/view/frontend/layout/checkout_cart_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
13 |
14 |
15 |
16 |
17 | - Yireo\GoogleTagManager2\DataLayer\Processor\Cart
18 |
19 |
20 |
21 | - Yireo\GoogleTagManager2\DataLayer\Event\ViewCart
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/view/frontend/layout/checkout_onepage_success.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
13 |
14 |
15 |
16 |
17 | - Yireo\GoogleTagManager2\DataLayer\Processor\SuccessPage
18 |
19 |
20 |
21 | - Yireo\GoogleTagManager2\DataLayer\Event\Purchase
22 |
23 |
24 |
25 | - Yireo\GoogleTagManager2\DataLayer\Tag\EnhancedConversions
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/view/frontend/layout/hyva_default.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
13 |
14 |
15 |
19 |
20 |
24 |
25 |
26 |
29 |
30 |
33 |
34 |
37 |
38 | .products a.product
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/view/frontend/requirejs-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * GoogleTagManager2 plugin for Magento
3 | *
4 | * @author Yireo (https://www.yireo.com/)
5 | * @copyright Copyright (c) 2022 Yireo (https://www.yireo.com/)
6 | * @license Open Software License
7 | */
8 |
9 | var config = {
10 | map: {
11 | '*': {
12 | 'yireoGoogleTagManager': 'Yireo_GoogleTagManager2/js/generic',
13 | 'yireoGoogleTagManagerPush': 'Yireo_GoogleTagManager2/js/push',
14 | 'yireoGoogleTagManagerProductClicks': 'Yireo_GoogleTagManager2/js/product/clicks',
15 | 'yireoGoogleTagManagerLogger': 'Yireo_GoogleTagManager2/js/logger'
16 | }
17 | },
18 | config: {
19 | mixins: {
20 | 'Magento_Checkout/js/model/shipping-save-processor/default': {
21 | 'Yireo_GoogleTagManager2/js/mixins/shipping-save-processor-mixin': true
22 | },
23 | 'Magento_Catalog/js/catalog-add-to-cart': {
24 | 'Yireo_GoogleTagManager2/js/mixins/catalog-add-to-cart-mixin': false
25 | },
26 | 'Magento_Checkout/js/model/step-navigator': {
27 | 'Yireo_GoogleTagManager2/js/mixins/step-navigator-mixin': true
28 | },
29 | 'mage/dropdown': {
30 | 'Yireo_GoogleTagManager2/js/mixins/minicart-mixin': true
31 | },
32 | }
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/view/frontend/templates/hyva/data-layer.phtml:
--------------------------------------------------------------------------------
1 | getDataLayerViewModel();
9 | $dataLayerJson = $dataLayerViewModel->getDataLayerAsJson();
10 | $dataLayerEventsJsonChunks = $dataLayerViewModel->getDataLayerEventsAsJsonChunks();
11 | ?>
12 |
18 |
--------------------------------------------------------------------------------
/view/frontend/templates/hyva/script-logger.phtml:
--------------------------------------------------------------------------------
1 |
3 |
23 |
--------------------------------------------------------------------------------
/view/frontend/templates/hyva/script-product-clicks.phtml:
--------------------------------------------------------------------------------
1 | getProductPath();
16 | ?>
17 |
56 |
--------------------------------------------------------------------------------
/view/frontend/templates/hyva/script-pusher.phtml:
--------------------------------------------------------------------------------
1 |
11 |
66 |
--------------------------------------------------------------------------------
/view/frontend/templates/iframe.phtml:
--------------------------------------------------------------------------------
1 | getConfig();
9 | ?>
10 |
11 |
--------------------------------------------------------------------------------
/view/frontend/templates/luma/data-layer.phtml:
--------------------------------------------------------------------------------
1 | getDataLayerViewModel();
9 | $dataLayerJson = $dataLayerViewModel->getDataLayerAsJson();
10 | $dataLayerEventsJsonChunks = $dataLayerViewModel->getDataLayerEventsAsJsonChunks();
11 | ?>
12 |
20 |
--------------------------------------------------------------------------------
/view/frontend/templates/luma/script-additions.phtml:
--------------------------------------------------------------------------------
1 | getCommonsViewModel();
17 | ?>
18 |
21 |
--------------------------------------------------------------------------------
/view/frontend/templates/luma/script-begin-checkout.phtml:
--------------------------------------------------------------------------------
1 | getData('begin_checkout_event');
9 | $encodedEvent = json_encode($beginCheckoutEvent->get());
10 | ?>
11 |
14 |
--------------------------------------------------------------------------------
/view/frontend/templates/luma/script-product-clicks.phtml:
--------------------------------------------------------------------------------
1 | getProductPath();
15 | ?>
16 |
21 |
--------------------------------------------------------------------------------
/view/frontend/templates/product/details.phtml:
--------------------------------------------------------------------------------
1 | getDataLayer();
12 | $productDetails = $block->getProductDetails();
13 | $product = $block->getProduct();
14 | if ($product instanceof ProductInterface) {
15 | $productDetails->setProduct($product);
16 | }
17 |
18 | $product = $productDetails->getProduct();
19 | $productData = $dataLayer->toJson($productDetails->merge());
20 | ?>
21 |
24 |
--------------------------------------------------------------------------------
/view/frontend/templates/script.phtml:
--------------------------------------------------------------------------------
1 | getConfig();
9 | $gtmIds = explode(',', $config->getId());
10 | ?>
11 |
12 | waitForUserInteraction() === false)
14 | ? "'load', 'keydown', 'mouseover', 'scroll', 'touchstart', 'wheel'"
15 | : "'keydown', 'mouseover', 'scroll', 'touchstart', 'wheel'";
16 | ?>
17 |
49 |
--------------------------------------------------------------------------------
/view/frontend/web/js/checkout/payment-validator/register.js:
--------------------------------------------------------------------------------
1 | define(
2 | [
3 | 'uiComponent',
4 | 'Magento_Checkout/js/model/payment/additional-validators',
5 | 'Yireo_GoogleTagManager2/js/checkout/payment-validator/reload-customer-data',
6 | 'yireoGoogleTagManagerLogger'
7 | ],
8 | function (Component, additionalValidators, reloadCustomerDataValidator, logger) {
9 | 'use strict';
10 | logger('Registering additional payment validator');
11 | additionalValidators.registerValidator(reloadCustomerDataValidator);
12 | return Component.extend({});
13 | }
14 | );
--------------------------------------------------------------------------------
/view/frontend/web/js/checkout/payment-validator/reload-customer-data.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'Magento_Customer/js/customer-data',
3 | 'yireoGoogleTagManagerLogger'
4 | ], function (customerData, logger) {
5 | 'use strict';
6 | return {
7 | validate: function () {
8 | logger('payment-validator-reload-customer-data');
9 | customerData.reload(['gtm-checkout'], true);
10 | return true;
11 | }
12 | }
13 | });
--------------------------------------------------------------------------------
/view/frontend/web/js/logger.js:
--------------------------------------------------------------------------------
1 | define([], function () {
2 | return function (...args) {
3 | const debug = window.YIREO_GOOGLETAGMANAGER2_DEBUG || false;
4 | if (false === debug) {
5 | return;
6 | }
7 |
8 | var color = 'gray';
9 | if (args[0].toLowerCase().startsWith('push')) {
10 | color = 'green';
11 | }
12 |
13 | if (args[0].toLowerCase().startsWith('warning')) {
14 | color = 'orange';
15 | }
16 |
17 | var css = 'color:white; background-color:' + color + '; padding:1px;'
18 | console.log('%cYireo_GoogleTagManager2', css, ...args);
19 | };
20 | });
21 |
--------------------------------------------------------------------------------
/view/frontend/web/js/mixins/catalog-add-to-cart-mixin.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'yireoGoogleTagManagerPush'
4 | ], function ($, pusher) {
5 | 'use strict';
6 |
7 | const enabled = window.YIREO_GOOGLETAGMANAGER2_ENABLED;
8 | if (enabled === null || enabled === undefined) {
9 | return function (targetWidget) {
10 | return $.mage.catalogAddToCart;
11 | };
12 | }
13 |
14 | var mixin = {
15 | submitForm: function (form) {
16 | const formData = Object.fromEntries(new FormData(form[0]).entries());
17 | const productId = formData.product;
18 |
19 | const debugClicks = window['YIREO_GOOGLETAGMANAGER2_DEBUG_CLICKS'] || false;
20 | const productData = window['YIREO_GOOGLETAGMANAGER2_PRODUCT_DATA_ID_' + productId] || {};
21 | productData.quantity = formData.qty || 1;
22 |
23 | const eventData = {
24 | 'event': 'add_to_cart',
25 | 'ecommerce': {
26 | 'items': [productData]
27 | }
28 | };
29 |
30 | if (debugClicks && confirm("Press to continue with add-to-cart") === false) {
31 | return;
32 | }
33 |
34 | pusher(eventData, 'push [catalog-add-to-cart-mixin.js]');
35 | return this._super(form);
36 | }
37 | };
38 |
39 | return function (targetWidget) {
40 | $.widget('mage.catalogAddToCart', targetWidget, mixin);
41 | return $.mage.catalogAddToCart;
42 | };
43 | });
44 |
--------------------------------------------------------------------------------
/view/frontend/web/js/mixins/minicart-mixin.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | ], function ($) {
4 | 'use strict';
5 |
6 | const enabled = window.YIREO_GOOGLETAGMANAGER2_ENABLED;
7 | if (enabled === null || enabled === undefined) {
8 | return function (targetWidget) {
9 | return $.mage.dropdownDialog;
10 | };
11 | }
12 |
13 | var mixin = {
14 | open: function () {
15 | const rt = this._super();
16 | window.dispatchEvent(new CustomEvent('minicart_collapse'));
17 | return rt;
18 | }
19 | };
20 |
21 | return function (targetWidget) {
22 | $.widget('mage.dropdownDialog', targetWidget, mixin);
23 | return $.mage.dropdownDialog;
24 | };
25 | });
26 |
--------------------------------------------------------------------------------
/view/frontend/web/js/mixins/shipping-save-processor-mixin.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'mage/utils/wrapper',
3 | 'Magento_Customer/js/customer-data',
4 | 'yireoGoogleTagManagerLogger'
5 | ], function (wrapper, customerData, logger) {
6 | 'use strict';
7 |
8 | return function (shippingSaveProcessor) {
9 | shippingSaveProcessor.saveShippingInformation = wrapper.wrapSuper(shippingSaveProcessor.saveShippingInformation, function (type) {
10 | const rt = this._super(type);
11 | rt.done(function() {
12 | logger('customerData reload (shipping-save-processor-mixin.js)', type);
13 | customerData.reload(['gtm-checkout'], true);
14 | });
15 |
16 | return rt;
17 | })
18 |
19 | return shippingSaveProcessor;
20 | };
21 | });
22 |
--------------------------------------------------------------------------------
/view/frontend/web/js/mixins/step-navigator-mixin.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'mage/utils/wrapper',
3 | 'yireoGoogleTagManagerPush',
4 | 'yireoGoogleTagManagerLogger',
5 | ], function (wrapper, pusher, logger, stepNavigator) {
6 | 'use strict';
7 | return function (stepNavigator) {
8 | const enabled = window.YIREO_GOOGLETAGMANAGER2_ENABLED;
9 | if (enabled === null || enabled === undefined) {
10 | return stepNavigator;
11 | }
12 |
13 | stepNavigator.steps.subscribe(function (steps) {
14 | const firstStep = steps[0];
15 | const eventData = window.YIREO_GOOGLETAGMANAGER2_BEGIN_CHECKOUT;
16 |
17 | if (firstStep === undefined || firstStep == null || firstStep.length <= 0) {
18 | logger('Error: No steps detected. Triggering event anyway :o')
19 | pusher(eventData, 'push (page event "begin_checkout") [step-navigator-mixin.js]');
20 | return;
21 | }
22 |
23 | if (!firstStep.isVisible()) {
24 | return;
25 | }
26 |
27 | if (eventData === null || eventData === undefined) {
28 | logger('skipped "begin_checkout" event because data is empty')
29 | return;
30 | }
31 |
32 | pusher(eventData, 'push (page event "begin_checkout") [step-navigator-mixin.js]');
33 | });
34 |
35 | return stepNavigator;
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/view/frontend/web/js/product/clicks.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'yireoGoogleTagManagerPush'
4 | ], function($, pusher) {
5 | return function(config, element) {
6 | const productPath = config.productPath || '.product-items a.product';
7 | $(productPath).click(function(event) {
8 | const debugClicks = window['YIREO_GOOGLETAGMANAGER2_DEBUG_CLICKS'] || false;
9 | const $parent = $(this).closest('[id^=product-item-info_]');
10 | const regex = /_(\d+)$/;
11 | const matches = $parent.attr('id').match(regex);
12 | const productId = matches[1];
13 | const productData = window['YIREO_GOOGLETAGMANAGER2_PRODUCT_DATA_ID_' + productId] || {};
14 | productData.item_id = productId;
15 |
16 | const eventData = {
17 | 'event': 'select_item',
18 | 'ecommerce': {
19 | 'items': [productData]
20 | }
21 | }
22 |
23 | pusher(eventData, 'push (page event "select_item") [clicks.js]');
24 |
25 | if (debugClicks && confirm("Press to continue with redirect") === false) {
26 | event.preventDefault();
27 | }
28 | });
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/view/frontend/web/js/push.js:
--------------------------------------------------------------------------------
1 | define(['yireoGoogleTagManagerLogger'], function (logger) {
2 | return function (eventData, message) {
3 | window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS = window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS || [];
4 |
5 | const metaData = Object.assign({}, eventData.meta);
6 |
7 | const cleanEventData = Object.assign({}, eventData);
8 | if (cleanEventData.meta) {
9 | delete cleanEventData.meta;
10 | }
11 |
12 | if (cleanEventData.length === 0) {
13 | return;
14 | }
15 |
16 | if (metaData && metaData.allowed_pages && metaData.allowed_pages.length > 0
17 | && false === metaData.allowed_pages.some(page => window.location.pathname.includes(page))) {
18 | logger('Warning: Skipping event, not in allowed pages', window.location.pathname, eventData);
19 | return;
20 | }
21 |
22 | // Prevent the same event from being triggered twice, when containing the same data
23 | const eventHash = btoa(encodeURIComponent(JSON.stringify(cleanEventData)));
24 | if (window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS.includes(eventHash)) {
25 | logger('Warning: Event already triggered', eventData);
26 | return;
27 | }
28 |
29 | if (!message) {
30 | message = 'push (unknown) [unknown]';
31 | }
32 |
33 | logger(message, eventData);
34 | window.dataLayer = window.dataLayer || [];
35 |
36 | if (cleanEventData.ecommerce) {
37 | window.dataLayer.push({ecommerce: null});
38 | }
39 |
40 | window.dataLayer.push(cleanEventData);
41 | window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS.push(eventHash);
42 | };
43 | });
44 |
--------------------------------------------------------------------------------