"Class cannot be saved in the code directory" error
This article describes how to fix the issue where the way you specified dependencies prevents classes from being auto-generated on the fly, and you get the "Class cannot be saved in the generated/code directory" error message.
Affected products and versions
- Adobe Commerce on cloud infrastructure 2.2.0 or later
Issue
Steps to reproduce
- In your local environment, write a custom class with a dependency on the auto-generated class.
- Run the scenario, where your custom class is triggered, and see it working correctly.
- Commit and push your changes to the integration environment. This would trigger the deployment process. Deployment is successful.
- In the integration environment, run the scenario where your custom class is triggered.
Expected result
Everything works correctly, the same way as in your local environment.
Actual result
Failure with the error message saying that your class cannot be saved in the generated/code
directory.
Cause
The cause of the problem is that the class on which you have dependency, does not get generated during the deployment, and cannot be generated later on the fly when the class is triggered, because the generated/code
directory is unavailable for writing after deployment is completed.
There are two main reasons why this could happen:
- Case 1: The class with dependencies on auto-generated classes is located in the entry point (like
index.php
), which is not scanned for dependencies during deployment.
- Case 2: The dependency to the auto-generated class is specified directly (compare to the recommended usage of constructor to declare dependency).
Solution
One common solution for both cases would be creating a real factory instead of the auto-generated class.
Or there is a particular solution for each case.
Case 1 specific solution
Move your class code from the entry point to a separate module and then use it in the entry point.
Example
Original code in, for example, index2.php
:
<?php
use YourVendor\SomeModule\Model\GeneratedFactory;
require realpath(__DIR__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
class SomeClass
{
private $generatedFactory;
public function __construct(GeneratedFactory $generatedFactory)
{
$this->generatedFactory = $generatedFactory;
}
// Some code here...
}
$someObject = $bootstrap->getObjectManager()->create(SomeClass::class);
// There is some code that uses $someObject
You need to take the following steps:
-
Move the class definition to app/code/YourVendor/YourModule
:
<?php
namespace YourVendor\YourModule;
use YourVendor\YourModule\Model\GeneratedFactory;
class YourClass
{
private $generatedFactory;
public function __construct(GeneratedFactory $generatedFactory)
{
$this->generatedFactory = $generatedFactory;
}
// Some code here...
}
-
Edit the entry point my_api/index.php
so that it looks like following:
<?php
use YourVendor\YourModule\YourClass;
require realpath(__DIR__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$someObject = $bootstrap->getObjectManager()->create(YourClass::class);
// Some code using $someObject
Case 2 specific solution
Move dependency declaration to the constructor.
Example
Original class declaration:
<?php
namespace YourVendor\YourModule;
use YourVendor\SomeModule\Model\GeneratedFactory;
use Magento\Framework\App\ObjectManager;
class YourClass
{
private $generatedFactory;
private $someParam;
public function __construct($someParam)
{
$this--->someParam = $someParam;
$this->generatedFactory = ObjectManager::getInstance()->get(GeneratedFactory::class);
}
// Some code here...
}
You need to change its constructor as following:
<?php
namespace YourVendor\YourModule;
use YourVendor\YourModule\Model\GeneratedFactory;
use Magento\Framework\App\ObjectManager;
class YourClass
{
private $generatedFactory;
private $someParam;
public function __construct($someParam, GeneratedFactory $generatedFactory = null)
{
$this->someParam = $someParam;
$this->generatedFactory = $generatedFactory ?: ObjectManager::getInstance()->get(GeneratedFactory::class);
}
// Some code here...
}