12

In Magento 1 I could copy abstract class to local or community directory and Magento used it, when autoload load this class.

Is there some solution to make to Magento loads my class instead vendor/magento/framework/Model/AbstractModel.php?

Preferences in di.xml for abstract classes does not work. Plugins only?

8

Plugins only?

Yes. You can write plugins for abstract classes and plugins should always be preferred over preferences if possible.

Preferences are useful if you want to replace an implementation. I can't think of a use case to replace the implementation for all models that extend AbstractModel, if that's even logically possible. So what you probably want is to add or change functionality and this is what plugins are for.

  • 1
    Hi, how can i override a protected function from abstract class in this case ? could you please help – Manashvi Birla Jun 7 '16 at 9:15
  • 2
    That's not possible. The best is to try to find a way to add plugins to the public methods that use the protected method, and change the behavior in this way, even if it involves more code and some duplication. – Fabian Schmengler Jun 7 '16 at 9:40
  • 1
    "I can't think of a use case to replace the implementation for all models that extend AbstractModel" I have such a use case: I have payment module, which uses abstract base class for 4 controllers, each using a method from base to verify the response from payment gateway. Now, the response has changed and I would have to change all 3. – Tero Lahtinen Jun 15 '18 at 13:18
6

Full solution: include replaced classes before magento autoload them. So step by step:

  1. In file app/etc/NonComposerComponentRegistration.php add row

    $pathList[] = dirname(__DIR__) . '/etc/ClassReplacer.php';
    
  2. In app/etc place file ClassReplacer.php with content

    class ClassReplacer
    {
        public function includeReplacedFiles($src)
        {
            try {
                $replacedFiles = $this->listDir($src, false, true);
                foreach ($replacedFiles as $replacedFile) {
                    include_once $src . $replacedFile;
                }
            } catch (Exception $e) {
                return;
            }
        }
    
        protected function listDir($dir, $prependDir = false, $recursive = false, $entityRegexp = null, $currPath = '')
        {
            if (!is_dir($dir)) {
                return array();
            }
            $currPath = $prependDir ? $dir : $currPath;
            $currPath = $currPath !== '' ? rtrim($currPath, '/') . '/' : '';
            $files = array();
            foreach (scandir($dir) as $file) {
                if (in_array($file, array('.', '..'))) {
                    continue;
                }
                $entity = $currPath . $file;
                if ($recursive && is_dir("$dir/$file")) {
                    $files = array_merge($files, $this->listDir("$dir/$file", false, true, $entityRegexp, $entity . '/'));
                    continue;
                }
                if ($entityRegexp && !preg_match($entityRegexp, $entity)) continue;
                $files[] = $entity;
            }
            return $files;
        }
    }
    $replace = new ClassReplacer();
    $replace->includeReplacedFiles(dirname(__DIR__) . '/code/Magento/');
    
  3. Place to app/code/Magento some class, which will be replaced, e.g. app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php

  • 1
    Dirty hack, but some times it might be helpful to be able to do that. M1 required manual changes in core classes (so copy to app/code/local) if you wanted invoice PDF to look different, I bet there will be some case where this will be needed. – Zefiryn Apr 14 '17 at 20:29
5

If abstract class have any public or protected method you want to override, there is a way actually if you can't use plugins.

I had to override method _processDownload inside \Magento\Downloadable\Controller\Download, addings some 'if-s' inside. (If anyone has idea how can I add something like this inside method using plugins I will be gratful). Class is abstract so preference didn't worked. Plugins too, as method is protected. The thing I had to do was override all class extending from Download, using preference. These classes:

Magento\Downloadable\Controller\Download\Link Magento\Downloadable\Controller\Download\LinkSample Magento\Downloadable\Controller\Download\Sample

And inside them rewrite method from parent class (the one I wass supposed to override). So actually code of overridden methods was copied to three places and was exactly the same.

It's not ideal but, works.

4

You can try using Magento Plugin for enhancing existing functionality of any Abstract class, although the scope of the function should be Public. Recently I worked on same issue where I need to exclude products having a custom attribute assigned from the Recently viewed product list.

I used plugin for the function named getItemsCollection from the class named Magento\Reports\Block\Product\AbstractProduct using the following syntax:

file: app\code\Package\Module\etc\frontend\di.xml

<type name="Magento\Reports\Block\Product\AbstractProduct">
    <plugin name="Package_Module::aroundGetItemsCollection" type="Package\Module\Block\Viewed" sortOrder="20"/>
</type>

file: app\code\Package\Module\Block\Viewed.php

public function afterGetItemsCollection(
    $subject, $result
) {
    $result = $result->addAttributeToFilter('skip_hire_product', [['neq' => 1], ['null' => true]], 'left');
    return $result;
}

You can use around as well as before plugins too. Hope this work for you.

  • 1
    i got success with above code in one of my requirement, actually i wanted to change some functionality of email module abstract class method and Plugin helped me to achieve this, thanks – bhargav shastri Oct 11 '18 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.