propelorm/Propel

Namespace duplication in files & directories and include error: Cannot find <TableName>TableMap files

bogdandynamic opened this issue · 2 comments

The scenario: PgSql database, PHP 5.6, propel 2 installed via composer
Step 1: Generating schema.xml file from existing PgSql database with namespace parameter "PROJECT\Database" (the namespace appears in the database tag and also in each of the table tags inside it). All OK until now.
Step 2: When generating class files from the schema.xml (calling from the ROOT_PROJECT_DIRECTORY with the command vendor/bin/propel model:build --platform=pgsql --output-dir="Database/" --schema-dir="Database/Config/") it generates all the files in ROOT_PROJECT_DIRECTORY/Database/PROJECT/Database directory when the intended directory should be (from my perspective) ROOT_PROJECT_DIRECTORY/Database (as specified from the --output-dir parameter). This was fixed by removing the namespace from the database tag in the schema.xml file. I tried keeping the namespace from the database tag and removing the namespace from the table tags but that screws the namespaces inside the generated files.
The directory structure looks like this after doing the above:

  • ROOT_PROJECT_DIRECTORY
    • Database
      • Base
      • Config (where the schema.xml file is located)
      • Map

Step 3: Trying to run an example to echo a count() from one table but PHP cannot find the following file: ROOT_PROJECT_DIRECTORY\DatabaseTableMap.php file, that is located in ROOT_PROJECT_DIRECTORY\Database\Map directory.
After doing some debugging I've found the Propel\Runtime\Map\DatabaseMap.php file where there the getTableByPhpName function resides (line 181). The workaround for me was switching the whole if (class_exists($tmClass = $phpName . 'TableMap')) with if (class_exists($tmClass = substr_replace($phpName, '\\Map\\', strrpos($phpName, '\\'), 1) . 'TableMap') || class_exists($tmClass = '\\Map\\' .$phpName . 'TableMap'))

One more thing to take into consideration is the fact that PgSql can have table/column/etc. names in CamelCase/camelCase style (like I like to name them). When generating the schema.xml from an existing database, the phpName parameter for every table/column was is the UNDERSCORE style and did not found any parameter in the cli command or configuration parameter in the xml/yaml/php configuration file to specify the style for the phpName. I had to change the default UNDERSCORE style throughout the code into NOCHANGE. If there is a configuration parameter or something equivalent please disregard this last paragraph.

On further debugging I discovered that if the --namespace parameter in the propel database:reverse command is a fully qualified namespace (starts with \), the namespaces declaration inside the files generated with the propel model:build command are as expected. I believe that should be mentioned in the documentation.

The problem seems to be in Propel\Generator\Manager\ModelManager.php file in the doBuild function. The $path variable will be in the form NAMESPACE\[EMPTY|BASE|MAP\]TableName.php and later on you do $file = new \SplFileInfo($this->getWorkingDirectory() . DIRECTORY_SEPARATOR . $path);.
In many cases the working directory will be the same as some part or the whole NAMESPACE provided before. I'll try and provide a fix.

Managed to fix the issue in my case by adding the following code in the doBuildfunction in Propel\Generator\Manager\ModelManager.php.

       $path = $builder->getClassFilePath();
        // ADDED CODE
        $databaseNamespace = $builder->getDatabase()->getNamespace();
        if($databaseNamespace[0] == '\\'){
            $databaseNamespace = substr($databaseNamespace, 1);
        }
        $namespaceComponents = explode('\\', $databaseNamespace);
        foreach($namespaceComponents as $component){
            $pos = strpos($path, $component);
            if($pos !== false){
                $path = substr($path, $pos + strlen($component));
                if($path[0] == '\\' || $path[0] == '/'){
                    $path = substr($path, 1);
                }
            }
        }
        // END OF ADDED CODE
        $file = new \SplFileInfo($this->getWorkingDirectory() . DIRECTORY_SEPARATOR . $path);
        $this->filesystem->mkdir($file->getPath());

Hope this helps someone.