I want to save/update the product in phtml file and I need to create productRepository using object manager.
I don't want any controller to be called. Is there any possibility to save product in phtml file without calling controller or helper class?
Magento 2 Object Manager – How to Create a ProductRepository
magento2object-managersave
Related Solutions
TL;DR
If you see a an error with Interceptor.php
be sure to empty /var/generation/
before you look further. It will save you lots of time.
Explanation
After some testing I found the underlying cause. When executing a custom controller for the first time Magento automatically generates files. So far so good. In my case just one. It's living in var\generation\
The full path in this example would be var\generation\Demo\Hello\Controller\Adminhtml\Order\MassPrint\Interceptor.php
.
This class is extending your custom controller. So naturally it is calling the parent constuctor in its own constuctor. So if you make changes to your controller constructor this file must be regenerated to mach your new code. If you don't it will yield unwanted results. So be sure to at least delete the Interceptor class for that controller. Easier would be to empty the whole director so Magento will regenerate all files matching your new code.
rm -rf var/generation/*
Actually magento gives a very good clue where to look in the last part of the error message.
called in /var/generation/Demo/Hello/Controller/Adminhtml/Order/MassPrint/Interceptor.php on line 15
The short answer is, yes, I would mock the entire Context
class.
$mockScopeConfig = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
$mockContext = $this->getMock(\Magento\Framework\App\Helper\Context::class, [], [], '', false);
$mockContext->method('getScopeContext')->willReturn($mockScopeConfig);
$classUnderTest = new \Custom\Shipping\Helper\Config($mockContext);
A bit of background
The base of my reasoning here is that I'm assuming it is good for classes to know as little as possible about other classes, including their parents.
I'm asserting this is correct because less knowledge about other classes means less broken code due to changes in other classes.
Core parent classes that my class under test inherits from are particular problematic because I only want to test code I write (in unit tests). I don't want to test core code.
Because of that I try to keep my code as decoupled as possible from core parent classes.
This means I do not use any dependencies from parent classes, for example, by accessing their protected properties.
If I would be writing the constructor of your helper the way I would like, it would look something like this:
public function __construct(HelperContext $context, ScopeConfigInterface $scopeConfig)
{
parent::__construct($context);
$this->myScopeConfig = $scopeConfig;
}
You see, this class is only marginally coupled to the parent dependency. I don't want my class to know about the parent or its dependencies.
Unfortunately however Magento forces us to couple our class to the Context
class due to an annoying check during bin/magento setup:di:compile
(update: this check has been removed since 2.2.0).
Because of this I would write the constructor like this:
public function __construct(HelperContext $context)
{
parent::__construct($context);
$this->myScopeConfig = $context->getScopeConfig();
}
If the parent class follows the design principles tell-don't-ask, then my class will not need to call any method of the parent class. Instead, all my class will have to do is implement an abstract method which will be called by the parent at the appropriate time (in pattern lingo this is the template method pattern).
Due to the legacy of Magento 1 however not all Magento 2 code follows this principle.
So sometimes I have to mock parts of a context class or parent dependencies, even if my code doesn't need them. I do so manually though and don't rely on the unit test ObjectManager
helper.
(One example of a class that is required is entities extending from \Magento\Framework\Model\AbstractModel
.)
Conclusion
Finally, I would like to ask you why you are extending the AbstractHelper
in the first place?
Just because core modules do it like that?
Does it provide any real benefit?
If your class only requires the scope config, why not have NO parent class and just depend on the ScopeConfigInterface
directly?
That way you avoid a lot of test doubles and potential breakage when core code changes, plus you have less unnecessary overhead during runtime because all the other unneeded dependencies don't have to be instantiated.
Best Answer
No. Noooo. Not the right approach.
You haven't given much information about exactly what problem you are trying to solve, but you should never be loading or saving any model directly in a template.
The correct process depends on the circumstances, but you should be running your product logic in a context where you can take advantage of Dependency Injection (DI)--a block, a controller, an observer, but preferably a model. In that class, you would use DI to get an instance of
\Magento\Catalog\Api\ProductRepositoryInterface
(ProductRepository), and then you could use that to create/load your product and then to save it.Using ObjectManager to directly create and save a Product object, in a template, is breaking just about every rule and code standard in the book. I cannot overstate this: What you are asking to do is very bad practice. Don't do it.
Factory
to the class name you're getting), and use that to create your object instance.Ignoring these standards can give you code that works (like the other answer), but it's code that will probably break in future versions, it's code that won't be findable (it's not where standards say it should be), it's code that isn't maintainable (does things wrong), and you're making a mess of your site in the process.
Taken all together: Here is how to inject, create, and save a product correctly in the context of dependency injection and M2 code standards.
There are a lot of complex concepts at play here, but it's worth the time to learn and understand them and do things right. You'll be able to work with M2 much more effectively when you do.