-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Possibility to add second code source directory for modules local composer development using content root feature #1263
Comments
Hi @spalewski. Thank you for your report. Add a comment to assign the issue:
|
Hi @spalewski Have you tried to specify the Magento installation as included path? |
Hi @VitaliyBoyko, yes i tried that but I does not fix this feature. My expected behavior is to have something like second app/code folder and have all plugin features there. Like create new module, plugin etc.. Right now: |
I found a solution that might work for others and does not require the developers of this plugin to add a feature for this. Our team actively works on custom Magento 2 modules within the
I put this into a simple script in my main Magento root <?php
namespace YourProjectVendor\ComposerScripts;
class SymlinkModules
{
/**
* Execute
*
* @param string $vendorNamespace
* @throws \JsonException
*/
public static function execute(
string $vendorNamespace
): void {
if (!$vendorNamespace) {
echo "Vendor namespace is required\n";
return;
}
$rootDir = \dirname(__DIR__);
$vendorDir = $rootDir . '/vendor/' . $vendorNamespace;
$appCodeDir = $rootDir . '/app/code/';
$gitignoreFile = $appCodeDir . '.gitignore';
if (!is_dir($vendorDir)) {
echo "No modules found in $vendorDir\n";
return;
}
$modules = array_filter(scandir($vendorDir, SCANDIR_SORT_NONE), static function ($dir) use ($vendorDir) {
return $dir !== '.' && $dir !== '..' && is_dir($vendorDir . '/' . $dir);
});
foreach ($modules as $module) {
$composerJson = file_get_contents($vendorDir . '/' . $module . '/composer.json');
if ($composerJson === false) {
continue;
}
$composerConfig = json_decode($composerJson, true, 512, JSON_THROW_ON_ERROR);
if (!isset($composerConfig['autoload']['psr-4'])) {
continue;
}
$psr4 = $composerConfig['autoload']['psr-4'];
$moduleName = array_key_first($psr4);
$moduleName = str_replace('\\', '/', trim($moduleName, '\\'));
$destination = $appCodeDir . $moduleName;
$autoloadPath = array_values($psr4)[0];
$source = $vendorDir . '/' . $module . (empty($autoloadPath) ? '' : '/' . $autoloadPath);
// Create parent directories if not exist
$destinationDir = \dirname($destination);
if (!is_dir($destinationDir)
&& !mkdir($destinationDir, 0755, true)
&& !is_dir($destinationDir)
) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', $destinationDir));
}
// Remove any existing symlink or directory
if (is_link($destination) || is_dir($destination)) {
unlink($destination);
}
// Calculate relative path from the destination to the source
$relativeSource = self::getRelativePath($destinationDir, $source);
// Create the symlink using the relative path
symlink($relativeSource, $destination);
echo "Symlinked $relativeSource -> $destination\n";
// Update the .gitignore with the module path
self::updateGitignore($gitignoreFile, $moduleName);
}
}
/**
* Get relative path
*
* @param string $from
* @param string $to
*
* @return string
*/
private static function getRelativePath(
string $from,
string $to
): string {
// Normalize
$from = realpath($from);
$to = realpath($to);
if (!$from || !$to) {
return '';
}
$fromParts = explode(DIRECTORY_SEPARATOR, $from);
$toParts = explode(DIRECTORY_SEPARATOR, $to);
// Remove common parts from the beginning of the path
while (count($fromParts) && count($toParts) && $fromParts[0] === $toParts[0]) {
array_shift($fromParts);
array_shift($toParts);
}
// Go up from the source
$relativePath = str_repeat('..' . DIRECTORY_SEPARATOR, count($fromParts));
// Add the remaining target path
$relativePath .= implode(DIRECTORY_SEPARATOR, $toParts);
return $relativePath;
}
/**
* Update gitignore file
*
* @param string $gitignoreFile
* @param string $module
*
* @return void
*/
private static function updateGitignore(
string $gitignoreFile,
string $module
): void {
$modulePath = $module;
// If .gitignore doesn't exist, create it
if (!is_file($gitignoreFile)) {
echo ".gitignore doesn't exist, creating it...\n";
file_put_contents($gitignoreFile, "# Generated by SymlinkModules Script\n");
}
// Check if the path is already in .gitignore to avoid duplication
$gitignoreContents = file_get_contents($gitignoreFile);
// Split the contents by line and check for exact matches
foreach (explode(PHP_EOL, $gitignoreContents) as $line) {
if (trim($line) === $modulePath) {
//echo "$modulePath already exists in .gitignore\n";
return;
}
}
// If not found, append to .gitignore
file_put_contents($gitignoreFile, "\n$modulePath", FILE_APPEND);
//echo "Added $modulePath to .gitignore\n";
}
} Then append the following to you projects "autoload-dev": {
"psr-4": {
"YourProjectVendor\\ComposerScripts\\": "scripts/"
}
},
"scripts": {
"post-install-cmd": [
"([ $COMPOSER_DEV_MODE -eq 0 ] || VENDOR_NAMESPACE=your_vendor_namespace php -r \"require_once 'vendor/autoload.php'; \\\\YourProjectVendor\\\\ComposerScripts\\\\SymlinkModules::execute(getenv('VENDOR_NAMESPACE'));\")"
],
"post-update-cmd": [
"([ $COMPOSER_DEV_MODE -eq 0 ] || VENDOR_NAMESPACE=your_vendor_namespace php -r \"require_once 'vendor/autoload.php'; \\\\YourProjectVendor\\\\ComposerScripts\\\\SymlinkModules::execute(getenv('VENDOR_NAMESPACE'));\")"
]
} Replace Then whenever you run SIDE NOTE: If you are using warden development environment, this method will also work, however, mutagen is responsible for bi-directional syncing of files between your machine and the docker container, and by default symlinks are ignored. You need to update your projects mutagen config and set |
And if you dont have time for the above solution, there is an even quicker one:
Then in your vendor modules, target the root of the module (make sure the directory is highlighted), and hit those hot keys you just bound. You should see the relevant modal for the plugin show up and on submission, the relevant files / dirs will be generated at your current highlighted target. NOTE: This method works for most abilities that this plugin provides, but it will not work certain features like plugin overrides, since the targets are only read out of |
Is your feature request related to a problem? Please describe.
When I develop modules that will be distributed by composer, I use custom "extensions" directory than app/code. Like described here: https://www.yireo.com/blog/2019-05-10-local-composer-development-with-magento2 .
Describe the solution you'd like (*)
It would be nice, to have option to select custom module as a second code source for plugin, so all plugin features will be also available there.
Additional context
https://www.yireo.com/blog/2019-05-10-local-composer-development-with-magento2
From Slack discussion:
About content roots:
https://www.jetbrains.com/help/idea/content-roots.html
The text was updated successfully, but these errors were encountered: