1
votes

I have got these two CakePHP V 2.4.5 models:

class Owner extends AppModel {
    public $name = 'Owner';
    public $hasMany = array('Car');
}

and

class Car extends AppModel {
    public $name = 'Car';
    public $belongsTo = array('Owner');
}

in my controller I wrote:

var $uses = array('Owner', 'Car');

public function test(){
    $data = array(
        'Owner' => array(
            'name' => 'Me'
        ),
        'Car' => array(
            array('color' => 'red'),
            array('color' => 'blue')
        )
    );
    $this->Owner->saveAssociated($data, array('deep' => true));
}

But CakePHP created the owner and forgets to create his cars:

1   BEGIN
2   INSERT INTO `test`.`owners` (`name`) VALUES ('Me')
3   COMMIT

This is what my ERM looks like:

ERM

Why does CakePHP not save the cars?

5
what's your dbms? Maybe your table doesn't support transactions. Try saveAssociated($data, array('atomic' => false)) (you don't need the deep option because Owner id in direct association with Car) - arilia
I am using MySQL V 5.1.41 in the standard configuration that was shipped with Xampp for Windows. - YMMD
and what kind of table? See my edit above. - arilia
I just handed a transaction into PhpMyAdmin, so I could verify that MySQL let's me insert multiple records in one transaction. There it worked. Using atomic => false indeed omitted the BEGIN and COMMIT statements, but there was still only the one statement left which inserted the owner. Do you need any more information? - YMMD
I was just curious about the kind of storage engine you are using. But if you can make trasactions then I guess is InnoDB - arilia

5 Answers

8
votes

It's not possible to give a directly-working answer because: There's nothing wrong with the code in the question. That probably means you aren't running your own code at all.

Given that the only answer that can be given is advice, the only class that matters for the example in the question is Owner.

Check filenames

The model conventions are:

The Model class OptionValue would be found in a file named OptionValue.php

Misnamed model files is by far the most common cause of "why is my model logic not being executed" questions. One pitfall is to add the suffix Model.php to model files, is the model file named correctly?

In this case check that the file app/Model/Owner.php exists.

Verify that the model is your model

If you're sure the model file is named correctly check what the app is using:

debug(get_class($this->Owner));
########## DEBUG ##########
'Owner'
###########################

If the output is "AppModel" - the model file is not loaded. There are very few reasons why CakePHP will not use a file that exists and one of them will apply:

  • The file does not exist - there is a typo in the name
  • The application is not looking in the same directory you are
  • The application doesn't have permission to open the file

Verify the association exists

debug($this->Owner->hasMany);
########## DEBUG ##########
array(
'Car' => array(
    'className' => 'Car',
    'foreignKey' => 'owner_id',
    'conditions' => '',
    'fields' => '',
    'order' => '',
    'limit' => '',
    'offset' => '',
    'dependent' => '',
    'exclusive' => '',
    'finderQuery' => '',
    'counterQuery' => ''
)
)
###########################

If Car is not in the output - the association isn't saved because to cake it doesn't exist - you'll need to identify why.

Check which files are loaded

If in doubt, check which files are loaded at run time:

debug(get_included_files());
########## DEBUG ##########
array(
    ...
    ...app/Model/Owner.php
    ...
)
###########################

This may indicate that a different file is being loaded, which prevents app/Model/Owner.php from being loaded.

Use a test to verify what's happening

You can use a test case to aide debugging for example:

<?php
App::uses('Owner', 'Model');

class OwnerTest extends CakeTestCase {

    public $fixtures = array(
        'app.car',
        'app.owner',
    );

    public function setUp() {
        parent::setUp();
        $this->Owner = ClassRegistry::init('Owner');
    }

    public function tearDown() {
        unset($this->Owner);
        parent::tearDown();
    }

    public function testCreate() {
        $data = array(
            'Owner' => array(
                'name' => 'Me'
            ),
            'Car' => array(
                array(
                    'Car' => array(
                        'color' => 'red',
                    )
                ),
                array(
                    'color' => 'blue',
                )
            )
        );

        $this->assertSame('Owner', get_class($this->Owner));

        $this->Owner->getDatasource()->getLog();
        $this->Owner->saveAssociated($data);
        $log = $this->Owner->getDatasource()->getLog();
        $expected = array();
        $this->assertSame($expected, $log, 'Look ma, the sql log');
    }
}

Which should output:

-> Console/cake test Test/Case/Model/OwnerTest.php 

Welcome to CakePHP v2.4.5 Console
---------------------------------------------------------------
App : app
Path: /var/www/files.dev/htdocs/app/
---------------------------------------------------------------
CakePHP Test Shell
---------------------------------------------------------------
PHPUnit 3.7.24 by Sebastian Bergmann.

F

Time: 169 ms, Memory: 9.00Mb

There was 1 failure:

1) OwnerTest::testCreate
Look ma, the sql log
Failed asserting that Array (
    'log' => Array (
        0 => Array (
            'query' => 'BEGIN'
            'params' => Array ()
            'affected' => null
            'numRows' => null
            'took' => null
        )
        1 => Array (
            'query' => 'INSERT INTO `test_database_name`.`owners` (`name`) VALUES ('Me')'
            'params' => Array ()
            'affected' => 1
            'numRows' => 1
            'took' => 0.0
        )
        2 => Array (
            'query' => 'INSERT INTO `test_database_name`.`cars` (`color`, `owner_id`) VALUES ('red', 1)'
            'params' => Array ()
            'affected' => 1
            'numRows' => 1
            'took' => 0.0
        )
        3 => Array (
            'query' => 'INSERT INTO `test_database_name`.`cars` (`color`, `owner_id`) VALUES ('blue', 1)'
            'params' => Array ()
            'affected' => 1
            'numRows' => 1
            'took' => 0.0
        )
        4 => Array (
            'query' => 'COMMIT'
            'params' => Array ()
            'affected' => 1
            'numRows' => 1
            'took' => 0.0
        )
    )
    'count' => 9
    'time' => 0.0
) is identical to Array ().

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
$

Note that correctly one owner was created for "Me", and two cars - each linked to the owner record for "Me".

3
votes

I'v tested your code.
It works perfect.
Try to remove models cache in /tmp/cache folder
My results:

(default) 3 queries took 0 ms   Nr  Query   Error   Affected    Num. rows   Took (ms)
1   BEGIN       0   0   0
2   INSERT INTO `intranet`.`owners` (`name`) VALUES ('Me')      1   1   0
3   INSERT INTO `intranet`.`cars` (`owner_id`, `color`) VALUES (3, 'red')       1   1   0
4   INSERT INTO `intranet`.`cars` (`owner_id`, `color`) VALUES (3, 'blue')      1   1   0
5   COMMIT      1   1   0
1
votes

Can you please try this:

$this->Owner->saveAll($data);

Instead of

$this->Owner->saveAssociated($data, array('deep' => true));

Thanks

0
votes

Not an actual answer but I've just tested your code and it works for me as well. (Cake 2.4.5)

(default) 5 queries took 2 ms
Nr  Query   Error   Affected    Num. rows   Took (ms)
1   BEGIN       0   0   0
2   INSERT INTO `cake_test_2`.`owners` (`name`, `updated`, `created`) VALUES ('Me', '2014-02-24 00:57:11', '2014-02-24 00:57:11')       1   1   0
3   INSERT INTO `cake_test_2`.`cars` (`owner_id`, `color`) VALUES (1, 'red')        1   1   0
4   INSERT INTO `cake_test_2`.`cars` (`owner_id`, `color`) VALUES (1, 'blue')       1   1   1
5   COMMIT      1   1   1

If you try with a fresh instance of CakePHP only with these necessary models and controller is it still not working?

0
votes

Note : For transactions to work correctly in MySQL your tables must use InnoDB engine. Remember that MyISAM tables do not support transactions.