Giter Site home page Giter Site logo

Comments (38)

juban avatar juban commented on May 27, 2024 1

Hi @toreonify,

Looks pretty good to me.
I will test that and add related unit tests.

Thank you for your contribution.

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024 1

@toreonify,

Works great. I've added related unit tests and everything seams fine.
I'll push a fix soon.

Thank you ๐Ÿ‘

from yii2-save-relations-behavior.

mythicallage avatar mythicallage commented on May 27, 2024

Hi, @sem-soft .

Could you provide your table definitions, specially the foreign key part please?

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Hi, @mythicallage. Here is

CREATE TABLE `main` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  ....
 `state` varchar(12) COLLATE utf8_unicode_ci NOT NULL,
 `created_at` int(11) NOT NULL,
 `updated_at` int(11) DEFAULT NULL,
 `deleted_at` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `child` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `main_id` int(11) NOT NULL,
 .....
 `state` varchar(12) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'new',
 `created_at` int(11) NOT NULL,
 `updated_at` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `fk-child-main` (`main_id`),
 .....
 CONSTRAINT `fk-child-main` FOREIGN KEY (`main_id`) REFERENCES `main` (`id`) ON UPDATE CASCADE,
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `sub_child` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `child_id` int(11) NOT NULL,
 ....
 `created_at` int(11) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `fk-sub_child-child` (`child_id`),
 ....
 CONSTRAINT `fk-sub_child-child` FOREIGN KEY (`child_id`) REFERENCES `child` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

from yii2-save-relations-behavior.

mythicallage avatar mythicallage commented on May 27, 2024

Hi @sem-soft .

I am sorry to inform you that I did not reproduce the bug. I used the extension version 1.3.2 and above-mentioned definition from you, the code and tables.

In my opinion, The main problem is here:

'Failed to commit transaction: transaction was inactive.'

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@mythicallage, can you show your code please?

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Failed to commit transaction: transaction was inactive.

This error is an consequence of bad sub_child data insertion.
Thx

from yii2-save-relations-behavior.

mythicallage avatar mythicallage commented on May 27, 2024

Sure, you're welcome.

I use PHP 7.1

/**
 * This is the model class for table "main".
 *
 * @property integer $id
 * @property string $name
 *
 * @property Child[] $children
 */
class Main extends \yii\db\ActiveRecord
{
    public static function tableName()
    {
        return 'main';
    }

    public function behaviors()
    {
        return [
            [
                'class' => SaveRelationsBehavior::className(),
                'relations' => [
                    'children'
                ]
            ],
        ];
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => [
                self::OP_ALL
            ]
        ];
    }

    public function rules()
    {
        return [
            [['name'], 'required'],
            [['name'], 'string', 'max' => 255],
        ];
    }

    public function getChildren()
    {
        return $this->hasMany(Child::className(), ['main_id' => 'id']);
    }
}


/**
 * This is the model class for table "child".
 *
 * @property integer $id
 * @property integer $main_id
 *
 * @property Main $main
 * @property SubChild[] $subChildren
 */
class Child extends \yii\db\ActiveRecord
{
    public static function tableName()
    {
        return 'child';
    }

    public function behaviors()
    {
        return [
            [
                'class' => SaveRelationsBehavior::className(),
                'relations' => [
                    'subChildren'
                ]
            ],
        ];
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => [
                self::OP_ALL
            ]
        ];
    }

    public function rules()
    {
        return [
//            [['main_id'], 'required'],
            [['main_id'], 'integer'],
            [['main_id'], 'exist', 'skipOnError' => true, 'targetClass' => Main::className(), 'targetAttribute' => ['main_id' => 'id']],
        ];
    }

    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'main_id' => 'Main ID',
        ];
    }

    public function getMain()
    {
        return $this->hasOne(Main::className(), ['id' => 'main_id']);
    }
    
    public function getSubChildren()
    {
        return $this->hasMany(SubChild::className(), ['child_id' => 'id']);
    }

    public static function create()
    {
        $child = new static();
        $child->subChildren = SubChild::create();

        return $child;
    }
}


/**
 * This is the model class for table "sub_child".
 *
 * @property integer $id
 * @property integer $child_id
 *
 * @property Child $child
 */
class SubChild extends \yii\db\ActiveRecord
{
    public static function tableName()
    {
        return 'sub_child';
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => [
                self::OP_ALL
            ]
        ];
    }

    public function rules()
    {
        return [
//            [['child_id'], 'required'],
            [['child_id'], 'integer'],
            [['child_id'], 'exist', 'skipOnError' => true, 'targetClass' => Child::className(), 'targetAttribute' => ['child_id' => 'id']],
        ];
    }

    public function getChild()
    {
        return $this->hasOne(Child::className(), ['id' => 'child_id']);
    }

    public static function create()
    {
        return new static();
    }
}

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@mythicallage, i'm using PHP 5.6, lastest Yii2 and lastest yii2-save-relations-behavior.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@mythicallage look at my relation:

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getSubChild()
    {
        return $this->hasOne(SubChild::className(), ['sub_child_id' => 'id']);
    }

I'm specially change default gii code from hasMany to hasOne. I have the right to do this for my logic case.
Please, change your relation type to hasOne and try tests.

    public function getSubChildren()
    {
        return $this->hasMany(SubChild::className(), ['child_id' => 'id']);
    }

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban @mythicallage if you need direct code and direct samples, i can model this. My previous code is pseudo and may have some issues. But first @mythicallage change relation type for getSubChildren to hasOne.

Thanks, Guys!!!!

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

@sem-soft
From what I could experiment using your models definitions, things are working as expected as long as transactions are disabled for the related models (child), no matter what relation type is used.
I guess there's an issue in the way transactions are started from the owner model.
I will investigate that as soon as possible.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Hi, @juban.
Here is a direct code of models, controller action and DB structure. I was reproduce this problem.

Db Migration

<?php

use yii\db\Migration;

/**
 * Class m171107_095527_create_save_bahaviour_test_structure
 */
class m171107_095527_create_save_bahaviour_test_structure extends Migration
{

    public function up()
    {
        $this->createTable('{{%main}}', [
            'id' => $this->primaryKey(),
            'name' => $this->string(64)->notNull(),
        ]);
        $this->createTable('{{%child}}',[
            'id' => $this->primaryKey(),
            'main_id' => $this->integer()->notNull(),
            'name' => $this->string(64)->notNull(),
        ]);
        $this->createTable('{{%sub_child}}', [
            'id' => $this->primaryKey(),
            'child_id' => $this->integer()->notNull(),
            'name' => $this->string(64)->notNull(),
        ]);

        $this->addForeignKey('fk-child-main', '{{%child}}', 'main_id', '{{%main}}', 'id', 'RESTRICT', 'CASCADE');
        $this->addForeignKey('fk-sub_child-child', '{{%sub_child}}', 'child_id', '{{%child}}', 'id', 'RESTRICT', 'CASCADE');
    }

    public function down()
    {
        $this->dropTable('{{%sub_child}}');
        $this->dropTable('{{%child}}');
        $this->dropTable('{{%main}}');
    }
}

ActiveRecord models with static constructors

<?php

namespace app\models;

use Yii;
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;

/**
 * This is the model class for table "{{%main}}".
 *
 * @property integer $id
 * @property string $name
 *
 * @property Child[] $children
 */
class Main extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%main}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['name'], 'required'],
            [['name'], 'string', 'max' => 64],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            [
                'class' => SaveRelationsBehavior::className(),
                'relations' => [
                    'children'
                ]
            ]
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => [
                self::OP_ALL
            ]
        ];
    }

    /**
     * @param $name
     * @return static
     */
    public static function create($name)
    {
        $main = new static();
        $main->name = $name;
        $main->children = Child::create('Child of ' . $name);
        return $main;
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getChildren()
    {
        return $this->hasMany(Child::className(), ['main_id' => 'id']);
    }
}
<?php

namespace app\models;

use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
use Yii;

/**
 * This is the model class for table "{{%child}}".
 *
 * @property integer $id
 * @property integer $main_id
 * @property string $name
 *
 * @property Main $main
 * @property SubChild[] $subChildren
 */
class Child extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%child}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [[/*'main_id',*/ 'name'], 'required'],
            [['main_id'], 'integer'],
            [['name'], 'string', 'max' => 64],
            [['main_id'], 'exist', 'skipOnError' => true, 'targetClass' => Main::className(), 'targetAttribute' => ['main_id' => 'id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            [
                'class' => SaveRelationsBehavior::className(),
                'relations' => [
                    'subChildren'
                ]
            ]
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => [
                self::OP_ALL
            ]
        ];
    }

    /**
     * @param $name
     * @return static
     */
    public static function create($name)
    {
        $child = new static();
        $child->name = $name;
        $child->subChildren = SubChild::create('Sub child of ' . $name);

        return $child;
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getMain()
    {
        return $this->hasOne(Main::className(), ['id' => 'main_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getSubChildren()
    {
        return $this->hasOne(SubChild::className(), ['child_id' => 'id']);
    }
}
<?php

namespace app\models;

use Yii;

/**
 * This is the model class for table "{{%sub_child}}".
 *
 * @property integer $id
 * @property integer $child_id
 * @property string $name
 *
 * @property Child $child
 */
class SubChild extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%sub_child}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [[/*'child_id', */'name'], 'required'],
            [['child_id'], 'integer'],
            [['name'], 'string', 'max' => 64],
            [['child_id'], 'exist', 'skipOnError' => true, 'targetClass' => Child::className(), 'targetAttribute' => ['child_id' => 'id']],
        ];
    }

    /**
     * @param $name
     * @return static
     */
    public static function create($name)
    {
        $subChild = new static();
        $subChild->name = $name;

        return $subChild;
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getChild()
    {
        return $this->hasOne(Child::className(), ['id' => 'child_id']);
    }
}

Controller action with example to execute saving

    public function actionTest()
    {
        $m = Main::create('ะ“ะปะฐะฒะฝะฐั ะผะพะดะตะปัŒ 1');
        if (!$m->save()) {
            throw  new BadRequestHttpException(ActiveRecordHelper::firstErrorText($m));
        }

        return $m;
    }

Please, try code above to reproduce error.

Thanks for your attention to this problem.

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

Hi @sem-soft

I'm sorry to say I still can't reproduce your specific error, even using the code you provided. However, I was able to reproduce the transaction related error mentioned by @mythicallage.
I have pushed a fix to the dev-master branch which should fix the issue.
Could you try again using the latest unstable commit (3409c65) please and let me know if your issue is fixed?

Thanks.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Hi, @juban. Please show me your models code. I will try using unstable branch.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Dear @juban.

2017-11-09 13:08:12 [192.168.1.1][-][-][warning][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::saveRelatedRecords] yii\db\IntegrityException was thrown while saving related records during beforeValidate event: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`chulakov_bander_test`.`sub_child`, CONSTRAINT `fk-sub_child-child` FOREIGN KEY (`child_id`) REFERENCES `child` (`id`) ON UPDATE CASCADE)
The SQL being executed was: INSERT INTO `sub_child` (`name`) VALUES ('Sub child of Child of ะ“ะปะฐะฒะฝะฐั ะผะพะดะตะปัŒ 1')
    in .../vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:306
    in .../vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:234
    in .../vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:346
2017-11-09 13:08:12 [192.168.1.1][-][-][info][application] $_SERVER = [
    'REDIRECT_STATUS' => '200'
    'HTTP_HOST' => '-------------'
    'HTTP_USER_AGENT' => 'Symfony BrowserKit'
    'PATH' => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
    'SERVER_SIGNATURE' => '<address>Apache/2.4.18 (Ubuntu) Server at -------- Port 80</address>
'
    'SERVER_SOFTWARE' => 'Apache/2.4.18 (Ubuntu)'
    'SERVER_NAME' => '------'
    'SERVER_ADDR' => '192.168.1.100'
    'SERVER_PORT' => '80'
    'REMOTE_ADDR' => '192.168.1.1'
    'DOCUMENT_ROOT' => '---------'
    'REQUEST_SCHEME' => 'http'
    'CONTEXT_PREFIX' => ''
    'CONTEXT_DOCUMENT_ROOT' => '------'
    'SERVER_ADMIN' => '[email protected]'
    'SCRIPT_FILENAME' => '------'
    'REMOTE_PORT' => '47628'
    'REDIRECT_URL' => '/test'
    'GATEWAY_INTERFACE' => 'CGI/1.1'
    'SERVER_PROTOCOL' => 'HTTP/1.1'
    'REQUEST_METHOD' => 'GET'
    'QUERY_STRING' => ''
    'REQUEST_URI' => '/test'
    'SCRIPT_NAME' => '/index.php'
    'PHP_SELF' => '/index.php'
    'REQUEST_TIME_FLOAT' => 1510232892.222
    'REQUEST_TIME' => 1510232892
]

With unstable repo have the same error. Please, show me your code which you using to reproduce my "specific" error.

@mythicallage copy-paste my code with errors and can't reproduce error too..

Thanks

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban Yes, the next error about inactive transaction was fixed in 3409c65. But my error is not)

Failed to commit transaction: transaction was inactive.

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

@sem-soft
Well, I'm puzzled. I'm using the exact same code as the one you provided here (migration and models). I've copy-pasted everything twice to be sure. Still no errors except for transactions which is fixed now.
In a way, your error makes sense because the child_id should not be null. In your case, if that's an option, I would set the child_id to allow null value to be able to save the record prior to the link operation.
Anyway, I'm trying hard to figure out why you get the error, and I don't. I will let you know.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban did you look in log-file? Because exception not raised direct (you catch it in try section and Yii::trace) and all records are saving in DB now, but in log-file appears record.

Thank U

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

@sem-soft Got it! Was able to workaround the issue by setting field child_id as null in DB definition. However, the behavior should have raised an exception. I will try to figure out a way to handle that properly. Thanks.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban Great! Take a look at my previous records about that. If we use hasMany relation (as gii generated by default for db tables relation) type there is not error raised. But if U change type to hasOne.... I think the point at this...

Thanks, i'l be wait for solution.

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

@sem-soft The SQL error is now handled and the saving of the main model will return false on errors during the saving of nested models. I'm working to solve a related issue because in your use case, we should be able to save the related record anyway.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban Nice work! Thx!

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

Hi @sem-soft
The latest commit (master branch) should fix your issue.
Could you confirm?

Thx.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban I ran some autotests and my logs is clear. looks like this issue was resolved by your commits.
More detail tests i will hold as soon as possible. Waiting for release to packagegist.

Good work and thank you!

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

@sem-soft That's good news. I'm waiting for one more feedback and version 1.4.0 will be good to go very soon I hope. Thank you for your help and support.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban did you test an update cases? I catch this error when updates my hasOne.

[error][yii\base\InvalidCallException] exception 'yii\base\InvalidCallException' with message 'Unable to link models: the primary key of api\models\Child is null.' in .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php:1539
Stack trace:
#0.../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(1317): yii\db\BaseActiveRecord->bindModels(Array, Object(api\models\SubChild), Object(api\models\Child))
#1 .../api/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php(426): yii\db\BaseActiveRecord->link('SubChild', Object(api\models\SubChild))
#2 [internal function]: lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior->afterSave(Object(yii\db\AfterSaveEvent))
#3 .../api/vendor/yiisoft/yii2/base/Component.php(557): call_user_func(Array, Object(yii\db\AfterSaveEvent))
#4 .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(971): yii\base\Component->trigger('afterUpdate', Object(yii\db\AfterSaveEvent))
#5 .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(793): yii\db\BaseActiveRecord->afterSave(false, Array)
#6 .../api/vendor/yiisoft/yii2/db/ActiveRecord.php(587): yii\db\BaseActiveRecord->updateInternal(NULL)
#7 .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(649): yii\db\ActiveRecord->update(true, NULL)
#8 .../api/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php(389): yii\db\BaseActiveRecord->save()
#9 [internal function]: lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior->afterSave(Object(yii\db\AfterSaveEvent))
#10 .../api/vendor/yiisoft/yii2/base/Component.php(557): call_user_func(Array, Object(yii\db\AfterSaveEvent))
#11 .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(971): yii\base\Component->trigger('afterUpdate', Object(yii\db\AfterSaveEvent))
#12 .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(767): yii\db\BaseActiveRecord->afterSave(false, Array)
#13 .../api/vendor/yiisoft/yii2/db/ActiveRecord.php(587): yii\db\BaseActiveRecord->updateInternal(NULL)
#14 .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(649): yii\db\ActiveRecord->update(true, NULL)
#15 .../api/api/repositories/MainRepository.php(18): yii\db\BaseActiveRecord->save()
#16 .../api/api/services/MainService.php(130): api\repositories\MainRepository->save(Object(api\models\Main))
#17 .../api/api/controllers/MainController.php(158): api\services\MainService->revision('5', Object(api\forms\ManualMistakesForm))
#18 [internal function]: api\controllers\MainController->actionUpdate('5')
#19 .../api/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#20 .../api/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#21 .../api/vendor/yiisoft/yii2/base/Module.php(528): yii\base\Controller->runAction('update', Array)
#22 .../api/vendor/yiisoft/yii2/web/Application.php(103): yii\base\Module->runAction('main/update', Array)
#23 .../api/vendor/yiisoft/yii2/base/Application.php(386): yii\web\Application->handleRequest(Object(yii\web\Request))
#24 .../api/api/web/index.php(17): yii\base\Application->run()
#25 {main}
2017-11-11 16:41:31 [87.117.15.178][2][-][info][application] $_GET = [
    'id' => '5'
]

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

I got it when tried to ubpdate subChild dependency throw main object.

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

@sem-soft Could you show me the code please?

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban Main - Child - SubChild case is the same. I do this:
$main = Main::findOne(1);

$childrens = $main->childrens;

$child = $main->childrens[0];
$child->subChild->name = 'Some new Name';

$childrens[] = $child;

$main->childrens = $childrens;
$main->save();

Try to change $subChild instance throw dependencies from $main instance

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Or do (in my case):
$main = Main::findOne(1);

$childrens = $main->childrens;

$child = $main->childrens[0];
$child->name = 'New Child Name';
$child->subChild = SubChild::create(/* params*/);

$childrens[] = $child;

$main->childrens = $childrens;
$main->save();

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

@juban I see this line in info log:

-11-12 17:08:12 [87.117.15.178][2][-][info][yii\db\Command::execute] UPDATE `child` SET `id`=NULL, `name`='New Child Name' WHERE `id`=9
    in .../api/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:389
    in .../api/api/repositories/BannerRepository.php:18
    in .../api/api/services/BannerService.php:130
2017-11-12 17:08:12 [87.117.15.178][2][-][error][yii\base\InvalidCallException] exception 'yii\base\InvalidCallException' with message 'Unable to link models: the primary key of api\models\Iteration is null.' in .../api/vendor/yiisoft/yii2/db/BaseActiveRecord.php:1539

In some case, behaviour sets null for primary key...

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

Hi @sem-soft

The nested model update bug is a bit more tricky than I first thought. I donโ€™t have much time right now to work on it but I will keep you informed as soon as I can.

Thx.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Hi @juban

I will be wait it

Thx.

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

Hi @sem-soft,

Sorry for the loooooooong delay here ;)
The latest release implements some enhancements regarding nested relations saving.
You will find some examples in the test cases named testSaveNestedModels, testUpdateHasOneNestedModels and testUpdateHasManyNestedModels.
I'm aware that's far from perfect but I will try to figure a better way to handle that in the future.

Thank you for your patience.

from yii2-save-relations-behavior.

sem-soft avatar sem-soft commented on May 27, 2024

Hi @juban,

Thanx for answer, i'l see this tests.

from yii2-save-relations-behavior.

toreonify avatar toreonify commented on May 27, 2024

Hi everyone!
I've been struggling with the same problem, trying to save nested models in one query. And I found some way to deal with it.

So, here's what I found:
When Yii receives REST query or query from Form model, it loads model data with load() method. And because SaveRelationsTrait overrides load() method, it then loads relations with loadRelations() method and fills related models with data that ignored by validate().
But when SaveRelationsBehavior loads first model (ex. Orders), it loads model relations (ex. OrdersPhotos, OrdersSpecial), validates them and sets attributes of related models. But ignored attributes for nested relations (ex. OrdersSpecialPhotos in OrdersSpecial) are not applied, because SaveRelationsTrait only hooks to load() method, not validate().

By adding SaveRelationsTrait logic to this method, it successfully saves nested models:

    private function _loadOrCreateRelationModel($data, $fks, $modelClass, $relationName)
    {

        /** @var BaseActiveRecord $relationModel */
        $relationModel = null;
        if (!empty($fks)) {
            $relationModel = $modelClass::findOne($fks);
        }
        if (!($relationModel instanceof BaseActiveRecord) && !empty($data)) {
            $relationModel = new $modelClass;
        }
        // If a custom scenario is set, apply it here to correctly be able to set the model attributes
        if (array_key_exists($relationName, $this->_relationsScenario)) {
            $relationModel->setScenario($this->_relationsScenario[$relationName]);
        }
        if (($relationModel instanceof BaseActiveRecord) && is_array($data)) {
            $relationModel->setAttributes($data);

                // This will load relations for related models if they have them
	        if ($relationModel->hasMethod('loadRelations')) {
	            $relationModel->loadRelations($data);
	        }
        }
        return $relationModel;
    }

Trace log after modification:

2018-11-03 09:40:03 [::1][-][-][trace][yii\web\Application::handleRequest] Route requested: 'orders/create'
2018-11-03 09:40:03 [::1][-][-][trace][yii\base\Controller::runAction] Route to run: orders/create
2018-11-03 09:40:03 [::1][-][-][trace][yii\base\Action::runWithParams] Running action: yii\rest\CreateAction::run()
2018-11-03 09:40:03 [::1][-][-][trace][yii\base\Model::onUnsafeAttribute] Failed to set unsafe attribute 'OrdersItems' in 'app\models\Orders'.
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsTrait.php:10
2018-11-03 09:40:03 [::1][-][-][trace][yii\base\Model::onUnsafeAttribute] Failed to set unsafe attribute 'OrdersSpecial' in 'app\models\Orders'.
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsTrait.php:10
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::__set] Setting ordersItems relation value
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:131
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:723
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsTrait.php:12
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::__set] Setting ordersSpecial relation value
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:131
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:723
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsTrait.php:12
2018-11-03 09:40:03 [::1][-][-][trace][yii\base\Model::onUnsafeAttribute] Failed to set unsafe attribute 'OrdersSpecialPhotos' in 'app\models\OrdersSpecial'.
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:291
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:216
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:197
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::__set] Setting ordersSpecialPhotos relation value
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:131
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:723
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:294
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::validateRelationModel] Validating Orders Items #0 relation model using default scenario
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:397
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:439
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:344
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::validateRelationModel] Validating Orders Special #0 relation model using default scenario
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:397
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:439
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:344
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::validateRelationModel] Validating Orders Special Photos #0 relation model using default scenario
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:397
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:439
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:344
2018-11-03 09:40:03 [::1][-][-][trace][yii\db\Transaction::begin] Begin transaction
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::afterSave] Linking ordersItems relation
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:498
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::afterSave] Linking ordersSpecial relation
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:498
2018-11-03 09:40:03 [::1][-][-][trace][yii\db\Transaction::begin] Set savepoint 1
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:502
2018-11-03 09:40:03 [::1][-][-][trace][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::afterSave] Linking ordersSpecialPhotos relation
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:498
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:502
2018-11-03 09:40:03 [::1][-][-][trace][yii\db\Transaction::begin] Set savepoint 2
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:502
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
2018-11-03 09:40:03 [::1][-][-][trace][yii\db\Transaction::commit] Release savepoint 2
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:502
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
2018-11-03 09:40:03 [::1][-][-][trace][yii\db\Transaction::commit] Release savepoint 1
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:548
    in /Users/toreonify/Documents/Clean/gorodok/yii2-backend/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:502
2018-11-03 09:40:03 [::1][-][-][trace][yii\db\Transaction::commit] Commit transaction

Maybe this will give you some idea how to implement this feature correctly!

from yii2-save-relations-behavior.

juban avatar juban commented on May 27, 2024

Hi @sem-soft. I will close that issue.
Please, feel free to reopen it if the latest fix doesn't work in your use case.

from yii2-save-relations-behavior.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.