yiisoft / yiisoft/active-record
ActiveRecord "link" requires additional "save" when linked "viaTable" otherwise "Unable to link models: both models must NOT be newly created" error
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 119
- Forks
- 38
- Avg merge
- 1h 11m
- Merged PRs (30d)
- 1
Description
usually i link 1:n models with its "link" method.
if i use a pivot table, i have to use on more "save" command on the related model.
i am not sure if this is a bug or a feature, but i would find it more conclusive to avoid the additonal save.
here the example scenario:
setup db, creates tables "test", "testline" and "test_testline" (attention: drop table) :
DROP TABLE IF EXISTS `test`;
CREATE TABLE IF NOT EXISTS `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date1` date NOT NULL,
`msg` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test` (`id`, `date1`, `msg`) VALUES
(1, '0000-00-00', 'parent item');
DROP TABLE IF EXISTS `testline`;
CREATE TABLE IF NOT EXISTS `testline` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`test_id` int(11) NOT NULL,
`info` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `testline` (`id`, `test_id`, `info`) VALUES
(1, 1, 'item #1'),
(2, 1, 'item #2');
DROP TABLE IF EXISTS `test_testline`;
CREATE TABLE IF NOT EXISTS `test_testline` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`test_id` int(11) NOT NULL,
`testline_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_test_idx` (`test_id`),
KEY `fk_testline_idx` (`testline_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
INSERT INTO `test_testline` (`id`, `test_id`, `testline_id`) VALUES
(1, 1, 1),
(2, 1, 2);
ALTER TABLE `test_testline`
ADD CONSTRAINT `fk_test` FOREIGN KEY (`test_id`) REFERENCES `test` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_testline` FOREIGN KEY (`testline_id`) REFERENCES `testline` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
Test-Model with two different relationships, doing almost the same thing:
class Test extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'test';
}
public function rules()
{
return [
[['msg'], 'string', 'max' => 255],
];
}
public function getTestlines()
{
return $this->hasMany(Testline::className(), ['id' => 'testline_id'])
->viaTable('test_testline', ['test_id' => 'id']);
}
public function getTestlinesWorking()
{
return $this->hasMany(Testline::className(), ['test_id' => 'id']);
}
}
Testline Model:
class Testline extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'testline';
}
public function rules()
{
return [
[['info'], 'required'],
];
}
public function getTest()
{
//return $this->hasOne(Test::className(), ['id' => 'test_id']);
return $this->hasOne(Test::className(), ['id' => 'test_id'])->viaTable('test_testline', ['testline_id' => 'id']);
}
}
in a test view:
$model = Test::findOne(1);
$newline = new Testline;
$newline->info = 'created by code (direct link)';
// working direct link, should return n+1 rows
$transaction = $model->getDb()->beginTransaction();
$model->link('testlinesWorking', $newline);
foreach ($model->testlinesWorking as $key => $item) {
var_dump($item->attributes);
}
$transaction->rollback();
// --- viaTable example
$newline = new Testline;
$newline->info = 'created by code (linked viaTable)';
// not working viaTable if you don't use save before
$transaction = $model->getDb()->beginTransaction();
// UNCOMMENT THIS LINE TO MAKE IT WORK:
// $newline->save();
$model->link('testlines', $newline);
foreach ($model->testlines as $key => $item) {
var_dump($item->attributes);
}
$transaction->rollback();
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the ActiveRecord link() path for relationships configured with viaTable(), using the supplied Test and Testline reproduction as the first check. Compare it with the direct hasMany link path and determine whether a newly created related model can be linked without an extra save. Done means the viaTable example succeeds with the additional save removed and existing linking behavior remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- database
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100