Question

So, We have 3 environments: development, staging and production.

.gitignore file includes "vendor" folder and it has been working fine for us fine. Once We pull changes in Staging We would just run:

composer install
php bin/magento setup:upgrade && php bin/magento setup:di:compile && php bin/magento setup:static-content:deploy && php bin/magento c:enable && php bin/magento c:c

The issue is: We had to customize a third party extension installed by composer (under "vendor" folder) and this path is ignored by Git. What should I do to keep track of this changes and deploy them to staging server?

I could "git add -f " but would like your opinion on best practices.

EDIT: I've tried to follow instructions as per this reply without success: https://magento.stackexchange.com/a/282689/54768

Basically I installed a module using composer. I just need to edit one file (actually a few lines in a specific function):

File location: /magento2/vendor/coingate/magento2-plugin/Model/Payment.php

So, I created following module:

Directory location: /magento2/app/code/CCHY/OverrideCoingate/

registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'CCHY_OverrideCoingate',
    __DIR__
);

composer.json

{
    "name": "cchy/overridecoingate",
    "description": "",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "coingate/magento2-plugin": "null",

        "magento/magento-composer-installer": "*"
    },
    "suggest": {

    },
    "type": "magento2-module",
    "version": "1.0.0",
    "license": [

    ],
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "CCHY\\OverrideCoingate\\": ""
        }
    },
    "extra": {
        "map": [
            [
                "*",
                "CCHY/OverrideCoingate"
            ]
        ]
    }
}

etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <preference for="Coingate\Merchant\Model\Payment" type="CCHY\OverrideCoingate\Model\Payment" />
</config>

etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="CCHY_OverrideCoingate" setup_version="1.0.0">
        <sequence>
            <module name="CoinGate_Merchant"/>
        </sequence>
    </module>
</config>

Model/Payment.php: (this is the original file. I just changed namespace and the portions in BOLD inside function getCoinGateRequest)

<?php
/**
 * CoinGate payment method model
 *
 * @category    CoinGate
 * @package     CoinGate_Merchant
 * @author      CoinGate
 * @copyright   CoinGate (https://coingate.com)
 * @license     https://github.com/coingate/magento2-plugin/blob/master/LICENSE The MIT License (MIT)
 */
namespace CCHY\OverrideCoingate\Model;

use CoinGate\CoinGate;
use CoinGate\Merchant as CoinGateMerchant;
use Magento\Directory\Model\CountryFactory;
use Magento\Framework\Api\AttributeValueFactory;
use Magento\Framework\Api\ExtensionAttributesFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Response\Http;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Module\ModuleListInterface;
use Magento\Framework\Registry;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Framework\UrlInterface;
use Magento\Payment\Helper\Data;
use Magento\Payment\Model\Method\AbstractMethod;
use Magento\Payment\Model\Method\Logger;
use Magento\Sales\Model\Order;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Sales\Api\OrderManagementInterface;

class Payment extends AbstractMethod
{
    const COINGATE_MAGENTO_VERSION = '1.2.6';
    const CODE = 'coingate_merchant';

    protected $_code = 'coingate_merchant';

    protected $_isInitializeNeeded = true;

    protected $urlBuilder;
    protected $coingate;
    protected $storeManager;
    protected $orderManagement;

    /**
     * @param Context $context
     * @param Registry $registry
     * @param ExtensionAttributesFactory $extensionFactory
     * @param AttributeValueFactory $customAttributeFactory
     * @param Data $paymentData
     * @param ScopeConfigInterface $scopeConfig
     * @param Logger $logger
     * @param CoinGateMerchant $coingate
     * @param UrlInterface $urlBuilder
     * @param StoreManagerInterface $storeManager
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param OrderManagementInterface $orderManagement
     * @param array $data
     * @internal param ModuleListInterface $moduleList
     * @internal param TimezoneInterface $localeDate
     * @internal param CountryFactory $countryFactory
     * @internal param Http $response
     */
    public function __construct(
        Context $context,
        Registry $registry,
        ExtensionAttributesFactory $extensionFactory,
        AttributeValueFactory $customAttributeFactory,
        Data $paymentData,
        ScopeConfigInterface $scopeConfig,
        Logger $logger,
        UrlInterface $urlBuilder,
        StoreManagerInterface $storeManager,
        OrderManagementInterface $orderManagement,
        CoinGateMerchant $coingate,
        array $data = [],
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null

    ) {
        parent::__construct(
            $context,
            $registry,
            $extensionFactory,
            $customAttributeFactory,
            $paymentData,
            $scopeConfig,
            $logger,
            $resource,
            $resourceCollection,
            $data
        );

        $this->urlBuilder = $urlBuilder;
        $this->storeManager = $storeManager;
        $this->orderManagement = $orderManagement;
        $this->coingate = $coingate;

        \CoinGate\CoinGate::config([
            'environment' => $this->getConfigData('sandbox_mode') ? 'sandbox' : 'live',
            'auth_token'  => $this->getConfigData('api_auth_token'),
            'user_agent'  => 'CoinGate - Magento 2 Extension v' . self::COINGATE_MAGENTO_VERSION
        ]);
    }

    /**
     * @param Order $order
     * @return array
     */
    public function getCoinGateRequest(Order $order)
    {
        $token = substr(md5(rand()), 0, 32);

        $payment = $order->getPayment();
        $payment->setAdditionalInformation('coingate_order_token', $token);
        $payment->save();

        $description = [];
        foreach ($order->getAllItems() as $item) {
            $description[] = number_format($item->getQtyOrdered(), 0) . ' × ' . $item->getName();
        }

        $params = [
            'order_id' => $order->getIncrementId(),
            'price_amount' => number_format($order->**getBaseGrandTotal()**, 2, '.', ''),
            'price_currency' => $order->**getBaseOrderCurrencyCode(),**
            'receive_currency' => $this->getConfigData**('base_currency')**,
            'callback_url' => ($this->urlBuilder->getUrl('coingate/payment/callback') .
               '?token=' . $payment->getAdditionalInformation('coingate_order_token')),
            'cancel_url' => $this->urlBuilder->getUrl('coingate/payment/cancelOrder'),
            'success_url' => $this->urlBuilder->getUrl('coingate/payment/returnAction'),
            'title' => $this->storeManager->getWebsite()->getName(),
            'description' => join($description, ', '),
            'token' => $payment->getAdditionalInformation('coingate_order_token')
        ];

        $cgOrder = \CoinGate\Merchant\Order::create($params);

        if ($cgOrder) {
            return [
                'status' => true,
                'payment_url' => $cgOrder->payment_url
            ];
        } else {
            return [
                'status' => false
            ];
        }
    }

    /**
     * @param Order $order
     */
    public function validateCoinGateCallback(Order $order)
    {

        try {
            if (!$order || !$order->getIncrementId()) {
                $request_order_id = (filter_input(INPUT_POST, 'order_id')
                    ? filter_input(INPUT_POST, 'order_id') : filter_input(INPUT_GET, 'order_id')
                );

                throw new \Exception('Order #' . $request_order_id . ' does not exists');
            }

            $payment = $order->getPayment();
            $get_token = filter_input(INPUT_GET, 'token');
            $token1 = $get_token ? $get_token : '';
            $token2 = $payment->getAdditionalInformation('coingate_order_token');

            if ($token2 == '' || $token1 != $token2) {
                throw new \Exception('Tokens do match.');
            }

            $request_id = (filter_input(INPUT_POST, 'id')
                ? filter_input(INPUT_POST, 'id') :  filter_input(INPUT_GET, 'id'));
            $cgOrder = \CoinGate\Merchant\Order::find($request_id);

            if (!$cgOrder) {
                throw new \Exception('CoinGate Order #' . $request_id . ' does not exist');
            }

            if ($cgOrder->status == 'paid') {
                $order->setState(Order::STATE_PROCESSING);
                $order->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING));
                $order->save();
            } elseif (in_array($cgOrder->status, ['invalid', 'expired', 'canceled', 'refunded'])) {
                $this->orderManagement->cancel($cgOrder->order_id);

            }
        } catch (\Exception $e) {
            $this->_logger->error($e);
        }
    }
}

No matter what I do after setup:upgrade , setup:di:compile and c:f it will use the original file insider vendor folder.

Was it helpful?

Solution

First of all: do not edit any vendor files. If you want to change anything, do it by custom extensions. If you edit vendor files in any way, you will not have possibility to easy udpate Magento Core in the future. If your problems are environment related: be sure to have all packages locked on specific version and your PHP version is same on all envs (because some packages have few versions for different PHP version).

However, you can use diff files and run them (automatically) as patches, just after composer install specific package. You need to install cweagans/composer-patches package to make it work. Just read about it here: https://github.com/cweagans/composer-patches.

For example:

composer.json

{
  (...)
  "extra": {
    "magento-force": "override",
    "composer-exit-on-patch-failure": true,
    "patches": {
      "magento/framework": {
        "PRODSECBUG-2198": "patches/PRODSECBUG-2198/PRODSECBUG-2198-2.3-CE.composer-2019-03-27-06-12-47-magento-framework.patch"
      },
      "magento/module-catalog": {
        "PRODSECBUG-2198": "patches/PRODSECBUG-2198/PRODSECBUG-2198-2.3-CE.composer-2019-03-27-06-12-47-magento-module-catalog.patch"
      }
    }
  }
  (...)
}

Above example install patches published by Magento Team on two packages magento/framework and magento/module-catalog.

OTHER TIPS

For the customization of third party in your website you need to override/extend the module according to your requirements.

In your case I think you need to create your module for customization of controller or model in case of controller or model customization, however you may override/extend the templates and the layout by copying the third party module to your current theme.

For more information you may check this link https://webkul.com/blog/overriding-rewriting-classes-magento2/ and extending/overriding https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/layouts/layout-extend.html

You can customise third party extensions that are in vendor by creating local override. You would version your local override.

I was going to type up an answer but came across this question

Magento 2 override Third party module installed with Composer

And this great answer

https://magento.stackexchange.com/a/252619/70343

So the key is it depends on what you are overriding but this should get you started

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top