2
votes

i want Bake to allow multiple foreign keys between the same tables. Multiple belongsTo and multiple HABTM (using multiple joinTables).

I have no problems with the belongsTo part, but i am struggling with implementing multiple HABTM tables. My syntax looks like this:

[joinTable, prefix, [joined Tables Array]],
["in_protocols_plant_controllers", "in", ["protocols", "plant_controllers"]], 
["out_protocols_plant_controllers", "out", ["protocols", "plant_controllers"]]

The prefix is used for producing different alias association names. I edited the ModelTask and my model file looks now how i want it to. This is what i added to ModelTask.findBelongsToMany( ) before return $associations.

foreach (Configure::read("specialBelongsToMany") as $elem) {
    $joinTableName = $elem[0];
    if (!in_array($joinTableName, $tables)) { debug($elem); die(); }
    $prefix = $elem[1];
    $conTables = $elem[2];

    $currIndex = array_search($tableName, $conTables);

    if ($currIndex !== false) {
        $otherIndex = $currIndex == 0 ? 1 : 0;
        $otherTableName = $conTables[$otherIndex];
        if (!in_array($otherTableName, $tables)) { debug($elem); die(); }

        $assoc = [
            'alias' => $this->_camelize($prefix . "_" . $otherTableName),
            'className' => $this->_camelize($otherTableName),
            'foreignKey' => $this->_modelKey($tableName),
            'targetForeignKey' => $this->_modelKey($otherTableName),
            'joinTable' => $joinTableName
        ];
        $associations['belongsToMany'][] = $assoc;
    }
}

My power_analyzers association produced by this looks like

[
    'belongsTo' => [
        (int) 0 => [
            'alias' => 'Manufacturers',
            'className' => 'Contacts',
            'foreignKey' => 'manufacturer_id'
        ]
    ],
    'hasMany' => [],
    'belongsToMany' => [
        (int) 0 => [
            'alias' => 'PlantControllers',
            'foreignKey' => 'power_analyzer_id',
            'targetForeignKey' => 'plant_controller_id',
            'joinTable' => 'plant_controllers_power_analyzers'
        ]
    ]
]

I didnt edited any other stuff in my tasks besides findbelongsto() in ModelTask for my multiple belongsTo logic and the above code.

But for some reason TableRegistry::get() is returning not existing associations and i dont know how to fix this. TableRegistry::get() in ControllerTask.php returns in function bake() this modelobject for power_analyzers

object(App\Model\Table\PowerAnalyzersTable) {

    'registryAlias' => 'PowerAnalyzers',
    'table' => 'power_analyzers',
    'alias' => 'PowerAnalyzers',
    'entityClass' => 'App\Model\Entity\PowerAnalyzer',
    'associations' => [
        (int) 0 => 'manufacturers',
        (int) 1 => 'plantcontrollers',
        (int) 2 => 'plantcontrollerspoweranalyzers',
        **(int) 3 => 'inplantcontrollers',** // i dont want this
        **(int) 4 => 'outplantcontrollers'** // i dont want this
    ],
    'behaviors' => [
        (int) 0 => 'Timestamp',
        (int) 1 => 'Search'
    ],
    'defaultConnection' => 'default',
    'connectionName' => 'default'

}

Here is a snipped of my database schema. It returns that power_analyzers and power_controller_features are connected with in_protocols_plan_controllers and out_protocols_plant_controllers, vice versa. This association isnt returned in ModelTask.

Where is the information for TableRegistry saved? Why does it save associations which aren't listed in the corresponding model file? How can i fix by TableRegistry? I hope you can help me.

TableRegistry is definitly retrieving data from ModelTask, but why does it connect different HABTM associations? Without my modification it does not.

I have problems with this because my Controllers now try to contain the not existing associations in their find.

Thanks for your help!

Edit:

I tried something more and figured out, that the tableobject changes while ModelTask.php is running. The two unwanted associations get added after protocol table's ModelTask bake() is done (after $this->bakeTest but before end of function). This is the table which introduces the associations. But i couldnt figure out why the associations of protocols were also added to power_analyzers associations. And what makes me more confused is that the tableobject changes after bakeTest(). What the hell has bakeTest to do with associations.

Maybe it would help me, if I knew where the association (int) 2 => 'plantcontrollerspoweranalyzers', got added to the tableobject. This is a wanted behaviour. I think that my modification doesnt work with it and i have to modify that part.

My bake processes are always clean. I am using a python script deleting all old stuff before bake.

Thanks for your help so far!

Edit 2:

Found a trace from Table::belongsToMany(). I will look into this the next days.

########## DEBUG ##########
'power_analyzers   InPlantControllers'
###########################

Cake\ORM\Table::belongsToMany() - CORE\src\ORM\Table.php, line 1117
Cake\ORM\Association\BelongsToMany::_generateTargetAssociations() - CORE\src\ORM\Association\BelongsToMany.php, line 364
Cake\ORM\Association\BelongsToMany::junction() - CORE\src\ORM\Association\BelongsToMany.php, line 322
Bake\Shell\Task\TestTask::_processModel() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 459
Bake\Shell\Task\TestTask::_processModel() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 456
Bake\Shell\Task\TestTask::_processModel() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 456
Bake\Shell\Task\TestTask::_processModel() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 456
Bake\Shell\Task\TestTask::_processModel() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 456
Bake\Shell\Task\TestTask::generateFixtureList() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 424
Bake\Shell\Task\TestTask::bake() - ROOT\vendor\cakephp\bake\src\Shell\Task\TestTask.php, line 231
App\Shell\Task\ModelTask::bakeTest() - APP/Shell\Task\ModelTask.php, line 1195
App\Shell\Task\ModelTask::bake() - APP/Shell\Task\ModelTask.php, line 118
App\Shell\Task\ModelTask::main() - APP/Shell\Task\ModelTask.php, line 101
Bake\Shell\BakeShell::Bake\Shell\{closure}() - ROOT\vendor\cakephp\bake\src\Shell\BakeShell.php, line 259
Cake\Collection\Collection::each() - CORE\src\Collection\CollectionTrait.php, line 51
Bake\Shell\BakeShell::all() - ROOT\vendor\cakephp\bake\src\Shell\BakeShell.php, line 260
Cake\Console\Shell::runCommand() - CORE\src\Console\Shell.php, line 493
Cake\Console\CommandRunner::run() - CORE\src\Console\CommandRunner.php, line 141
[main] - ROOT\bin\cake.php, line 12
1
TableRegistry::get() will return what it's being asked for, either a concrete table class instance for the given alias, or an auto-table (that is an instance of \Cake\ORM\Table), it has no involvement with associations. I think you need to add more details to your question (including your TableRegistry::get() usage and possibly the modified task code), as it's really hard to tell where in your code, what technical problem it is exactly that you are experiencing.ndm
I added some stuff. Do you need more information?Carsten
So what exactly does the generated table class look like? I'm fairly certain that the controller task doesn't add anything, and neither does the table locator, so I would suspect that the associations are present in the table class (or in a cached version maybe if you're using some sort of opcode cache). Also have you tried a clean run, that is, remove the genereated model(s) and controller(s) and rebake them?ndm
I dont get this.... especially the part that bakeTest() of my protocols table changes the association of power_analyzers. Does somebody know where the association to the join table is added? Maybe this could be the solution for my modification. I am talking about ** (int) 2 => 'plantcontrollerspoweranalyzers' **. This is also not added after ModelTask.findBelongsToMany() but somewhere else.Carsten
I'd suggest that you add breakpoints in \Cake\ORM\Table::addAssociations() and \Cake\ORM\Table::belongsToMany(), then you'll see from where the methods are being invoked (if at all). All I can do from here without seeing the full context, is shooting into the dark, which isn't too helpful.ndm

1 Answers

0
votes

I found out that TestTask is running over all association calling Cake\ORM\Association::junction() which adds "missing" associations. I wanted not to edit the core files of cakephp. Instead I avoided that TestTask calls the junction function by adding this code at the beginning of TestTask::_processModel()

foreach (Configure::read("specialBelongsToMany") as $elem) {
    $joinTableName = $elem[0];
    $prefix = $elem[1];
    $conTables = $elem[2];

    foreach ($conTables as $conTable) {
        $conAlias = Inflector::camelize($prefix . "_" . $conTable);
        if ($conAlias == $subject->alias()) {
            return;
        }
    }
}