3

If you see AbstractExpress class for PayPal payment method, you could see:

/**
 * @param \Magento\Framework\App\Action\Context $context
 * @param \Magento\Customer\Model\Session $customerSession
 * @param \Magento\Checkout\Model\Session $checkoutSession
 * @param \Magento\Sales\Model\OrderFactory $orderFactory
 * @param \Magento\Paypal\Model\Express\Checkout\Factory $checkoutFactory
 * @param \Magento\Framework\Session\Generic $paypalSession
 * @param \Magento\Framework\Url\Helper\Data $urlHelper
 * @param \Magento\Customer\Model\Url $customerUrl
 */
public function __construct(
    \Magento\Framework\App\Action\Context $context,
    \Magento\Customer\Model\Session $customerSession,
    \Magento\Checkout\Model\Session $checkoutSession,
    \Magento\Sales\Model\OrderFactory $orderFactory,
    \Magento\Paypal\Model\Express\Checkout\Factory $checkoutFactory,
    \Magento\Framework\Session\Generic $paypalSession,
    \Magento\Framework\Url\Helper\Data $urlHelper,
    \Magento\Customer\Model\Url $customerUrl
) {
    $this->_customerSession = $customerSession;
    $this->_checkoutSession = $checkoutSession;
    $this->_orderFactory = $orderFactory;
    $this->_checkoutFactory = $checkoutFactory;
    $this->_paypalSession = $paypalSession;
    $this->_urlHelper = $urlHelper;
    $this->_customerUrl = $customerUrl;
    parent::__construct($context);
    $parameters = ['params' => [$this->_configMethod]];
    $this->_config = $this->_objectManager->create($this->_configType, $parameters);
}

The question is: how does the system know which parameters to pass to constructor and of what type? I think they are defined somewhere, but I have looked at di.xml files of this module but I have only find this:

<type name="Magento\Paypal\Controller\Express\AbstractExpress">
    <arguments>
        <argument name="paypalSession" xsi:type="object">Magento\Paypal\Model\Session</argument>
    </arguments>
</type>

Only payPal Session appears (although of different type).

10

Explanation

To pass parameters to contructors you may use the direct injection mechanism in Magento2 (look for di.xml).

Defining an argument in a class constructor in Magento 2 is very similar to Magento 1 Mage::getSingleton method. You will get a singleton class of the type you required in the constructor.

Magento will just pass you a singleton instance of the object you required in the constructor by using a reflection mechanism. If you declare a parameter of type \Magento\Customer\Model\Session you will automatically get that class as singleton. Just like doing Mage::getSingleton('customer/session') in Magento 1.

If you need to pass arguments you problably need to use a Factory. Using a factory is like the Mage::getModel in Magento 1.

Factories are automatically created by Magento 2 framework, you do not need to define them unless you really need. Just add Factory to your class name.

In you example I see you use \Magento\Sales\Model\OrderFactory. That class is a factory for \Magento\Sales\Model\Order and you can use it do instantiate orders like Mage::getModel('sales/order') in Magento 1.

If you need to pass arguments to a factory constructor you just have to pass them to create method with an hash.

For example:

$this->myObjectFactory->create(['myparam' => $myvalue]);

This will create a new object and will pass $myvalue to the constructor argument called $myparam. No matter of its position.

Example 1: You need to access customer session in your class

<?php
namespace MyNS\MyModule\Model;

class MyClass {
    protected $customerSession;

    public function __construct(
        \Magento\Customer\Model\Session $customerSession
    ) {
        $this->customerSession = $customerSession;
    }

    public function getCustomerSession() {
        return $this->customerSession;
    }
}

In the previous example, an instance of \Magento\Customer\Model\Session is "automatically" passed to $customerSession parameter. Magento just detects you are asking for that class in your constructor and it will pass you an instance of such object.

This is an equivalend of: Mage::getSingleton('customer/session') in Magento 1.

Have a look here to understand how it does: Magento\Framework\ObjectManager\Factory\AbstractFactory

Example 2: You need to load an order information

<?php
namespace MyNS\MyModule\Model;

class MyClass {
    protected $orderInterfaceFactory;

    public function __construct(
        \Magento\Sales\Api\Data\OrderInterfaceFactory $orderInterfaceFactory
    ) {
        $this->orderInterfaceFactory = $orderInterfaceFactory;
    }

    public function getOrder($orderId) {
        $order = $this->orderInterfaceFactory->create();
        $order->load($orderId);

        return $order;
    }
}

In this example you do not need a singleton, you need an order instance. To achieve this you need to require a Factory Class. A Factory Class is a special class that can create class instances.

This is the equivalent of Mage::getModel('sales/order') in Magento 1.

Factory classes:

Factory classes can be both manually or automatically defined. If you create your own class and you do not create the Factory class, Magento will automatically build it for you.

So, if you create:

<?php
namespace MyNS\MyModule\Model;

class MyClass {
    public function myMethod() { return "foo"; }
}

and you need to access to and instance of it, you can just ask for MyClassFactory, even if you did not define such class:

<?php
namespace MyNS\MyModule\Model;

class MyOtherClass {
    protected $myClassFactory;

    public function __construct(
        MyClassFactory $myClassFactory
    ) {
        $this->myClassFactory = $myClassFactory;
    }

    public function myOtherMethod() {
        $myClassInstance = $this->myClassFactory->create();
        $myClassInstance->myMethod();
    }
}

Interfaces and preferences

In Magento2, when you ask for a class direct injection, is recommended to ask for an Interface Class.

In my previous example (Example #2), I do not require an Order class, but and OrderInterface class. This is the best way because interfaces are supposed to be always compatible between Magento different versions.

You question may be: An interface is not a class, how can I instantiate it?

The answer is in the Magento 2 prefernce mechanism (it is the equivalent of the rewrite mechanism in Magento 1).

OrderInterface is mapped to Magento\Sales\Model\Order in di.xml file of magento-sales module:

...
<preference for="Magento\Sales\Api\Data\OrderInterface" type="Magento\Sales\Model\Order"/>
...

So, when you ask for OrderInterface, Magento will give you an instance of Magento\Sales\Model\Order.

If your module need to rewrite Magento\Sales\Model\Order, you can define your new preference pointing to another class. This new class MUST implement OrderInterface.

This allows a perfect integration between modules.

  • Can you give a real example, please? My brain is very tired trying to understand this but I cannot still figure it out. For example, I have this controller definition, class Failure extends \Magento\Framework\App\Action\Action. To this, I need to have a Cart Management instance, so I have added it to the constructor, but a runtime error occurs telling that the parameters for constructor are incorrect. Maybe what you have explained is aplicable to new objects, however, in my case I need to get existing objects according to current user session in order to finish my payment gateway. – jstuardo Mar 30 '16 at 21:38
  • Updated my post with more detailed information. Hope it helps. – Phoenix128_RiccardoT Mar 31 '16 at 16:36
  • Like already mentioned in the question, instead of creating a factory, you can also set constructor arguments with the type configuration in the di.xml like described here: alanstorm.com/magento_2_object_manager_argument_replacement. This way each module can override constructor arguments. To create a class instance with a complete new set of constructor arguments, you can create a virtual type like described here: alanstorm.com/magento_2_object_manager_virtual_types – David Verholen Mar 31 '16 at 16:42

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.