Fix “Class cannot be saved in the generated/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.
Description description
Environment
Adobe Commerce on cloud infrastructure 2.2.0 or later
Issue/Symptoms
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.
Root 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 on the auto-generated class is specified directly (compare to the recommended usage of a constructor to declare dependency).
Resolution resolution
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 :
<?phpuse 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:code language-none <?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.phpso that it looks like the following:code language-none <?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 the dependency declaration to the constructor.
Example:
Original class declaration:
<?phpnamespace 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:
<?phpnamespace 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...}
Related reading
Code generation in our developer documentation.