la-haute-societe / yii2-save-relations-behavior Goto Github PK
View Code? Open in Web Editor NEWValidate and save automatically related Active Record models.
License: MIT License
Validate and save automatically related Active Record models.
License: MIT License
Could it possible to add exception logging into saveRelatedRecords function?
If you catch [yii\db\Exception] PDOException, you will not find any information about it.
Further, \yii\db\ActiveRecord->getErrors() will not return any error.
I have array field with ids of 3 related items and hasMany relation. Then I delete one of them and
when I try to save the model it is deleted. I think the problem is in the method $model->unlink($relationName, $initialModels[$key], true). Since there is no via table and a parameter $delete is true it deletes the parent model.
Migration
$this->createTable('{{%project}}', [
'id' => $this->primaryKey(),
'name' => $this->string(32)->notNull(),
]);
$this->createTable('{{%project_company}}', [
'id' => $this->primaryKey(),
'project_id' => $this->integer()->notNull(),
'title' => $this->string(64)->notNull(),
'desc' => $this->text()->notNull(),
]);
Model
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
class Project extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'saveRelations' => [
'class' => SaveRelationsBehavior::className(),
'relations' => [ 'company'],
],
];
}
public function transactions()
{
return [
self::SCENARIO_DEFAULT => self::OP_ALL,
];
}
/**
* @return ActiveQuery
*/
public function getCompany()
{
return $this->hasOne(ProjectCompany::className(), ['project_id' => 'id']);
}
// other relations
}
This code don't work, because project_id don't set automatically
$project = Project::findOne(1);
$project->company = ['title' => 'new company'];
$project->save();
Thank you for your useful extension. I would like to give you feedback to enhance your extension. Version is 1.3.1.
I have some issue with saving new models Dummy and DummyBrother if I try to save with validation. There will be an SQL error anyway. Saving Dummy and DummyMany models works fine.
2017-10-29 22:07:15 [127.0.0.1][-][-][warning][lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior::saveRelatedRecords] yii\db\Exception was thrown while saving related records during beforeValidate event: SQLSTATE[HY000]: General error: 1364 Field 'dummy_id' doesn't have a default value
I have to save Dummy model without validation. It works if I do only like that.
I would be grateful if this case would be considered in tests.
...
class Dummy extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'saveRelations' => [
'class' => SaveRelationsBehavior::class,
'relations' => ['dummyBrother', 'dummyManies']
],
];
}
public function transactions()
{
return [
self::SCENARIO_DEFAULT => self::OP_ALL,
];
}
...
CREATE TABLE dummy (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
PRIMARY KEY (id)
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
CREATE TABLE dummy_brother (
dummy_id int(11) NOT NULL,
name varchar(50) NOT NULL,
PRIMARY KEY (dummy_id),
UNIQUE INDEX UK_dummy_brother_dummy_id (dummy_id),
CONSTRAINT FK_dummy_brother_dummy_id FOREIGN KEY (dummy_id)
REFERENCES dummy (id) ON DELETE NO ACTION ON UPDATE RESTRICT
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
CREATE TABLE dummy_many (
id int(11) NOT NULL AUTO_INCREMENT,
dummy_id int(11) NOT NULL,
name varchar(50) NOT NULL,
PRIMARY KEY (id),
CONSTRAINT FK_dummy_many_dummy_id FOREIGN KEY (dummy_id)
REFERENCES dummy (id) ON DELETE NO ACTION ON UPDATE RESTRICT
)
ENGINE = INNODB
AUTO_INCREMENT = 1
CHARACTER SET utf8
COLLATE utf8_general_ci
ROW_FORMAT = DYNAMIC;
Either by overriding the load() method or adding a loadWithRelations() method
Let the behavior start transactions automatically based on a behavior global property (see #38)
Is there such a functional?
For example, I need adjust access. So that only the admin
could save relation tags
public function scenarios()
{
$s = parent::scenarios();
if(Yii::$app->user->can('admin')){
$s[self::SCENARIO_DEFAULT][] = 'tags';
}
return $s;
}
var_dump($project->isAttributeSafe('tags')); //false
But relation tags
save anyway when $project->save();
I suggest to append the feature in the future. I think it would be useful.
I have a relation (has many) model and when saved common model I would like SaveRelationsBehavior only write new record to db. Now SaveRelationsBehavior every time rewriting relations data. This my behavior in model:
public function behaviors() {
return [
[
'class' => SaveRelationsBehavior::class,
'relations' => [
'vacancies',
'resumes'
],
],
];
}
So, how can I do to only writer SaveRelationsBehavior?
Hi!
You can make the functionality, that can configure relations through behavior?
Before:
public function getUser()
{
return $this->hasOne(User::class, ['user_id' => 'id']);
}
After:
...
private User $user
...
/**
* @var User $user
**/
public function getUser()
{
return $this->user;
}
...
public function behaviors()
{
return [
'saveRelations' => [
'class' => SaveRelationsBehavior::class,
'relations' => [
'users' => [
'class' => User::class,
'relation' => 'has-one',
'link' => ['user_id' => 'id']
],
],
],
];
}
...
Now we can call get User and we won't get AR in response.
Currently it the behavior code it is hard coded that the yii\db\BaseActiveRecord->link method is called without a possible third parameter.
The third parameter is used for passing additional columns data to be saved to the junction table in a many-to-many relationship through a junction table.
http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#link()-detail
It would be nice if the behavior would allow to pass and save/update such additional columns in the junction table utilizing this native Yii2 method.
For example it could be made with declaring a callback in the behavior configuration that returns an array of the junction table column values for each ActiveRecord instance being saved,
An example:
/** @inheritdoc */
public function behaviors()
{
return [
'saveRelations' => [
'class' => SaveRelationsBehavior::class,
'relations' => [
'photos'
],
'junctionTableProperties' => [
'photos' => function (\yii\db\ActiveRecord $record) :array {
return [
'order' => $record->order
];
}
]
]
];
}
Add getDirtyRelations
and markRelationDirty
methods in order to get modified relations since their loading and force a relation as dirty.
I have three level of models:
model1 {
id: id1,
prop1: value1,
nested1: [
model2 {
id: id2,
prop2: value2,
nested2: [
model3 {
id: id3,
prop3: value3
}
]
}
]
}
I want to change only prop3
of model3
, but can not do this, because parent model has no dirty attributes, so nested models does not saves:
564 if (count($relationModel->dirtyAttributes)) {
565 if ($relationModel->validate()) {
566 $relationModel->save();
567 } else {
568 $this->_addError($relationModel, $owner, $relationName, self::prettyRelationName($relationName));
569 throw new DbException('Related record ' . self::prettyRelationName($relationName) . ' could not be saved.');
570 }
571 }
This JSON can't be loaded on $model->load(Yii::$app->request->post())... Just with : $model->attributes, but this doesn't load the relations
{ "id":"6", "idEscolaridade":"16", "descricao":"teste", "salario":5500.00, "Escolaridades":{ "id":"16", "descricao":"testando" } }
I'm using 'jaacoder/yii2-activated' to mapping the attributes, works for the parent model, but doesn't work for related models (ID column only)
My ActiveRecord class has the attribute
class Profile extends ActiveRecord
{
/**
* @var bool
*/
public $agree;
public function rules()
{
return [
[['agree'], 'required', 'on' => 'insert'],
];
}
}
But i have got an error "Failed to set unsafe attribute 'agree' in 'app\models\user\Profile'."
I found out we have to set scenario here if scenario provided by behavior
Anyway it helps me to work/ Isn't it the right case?
if i use input form like
<?= $form->field($user, 'profile[opf_id]')->dropDownList(Opf::getDropDown(), ['prompt' => ''])
->label(Profile::instance()->getAttributeLabel('opf_id')); ?>
i didn't watch validation errors.
i found out that your method has to be like
private function _addError($relationModel, $owner, $relationName, $prettyRelationName)
{
foreach ($relationModel->errors as $attribute=>$attributeErrors) {
foreach ($attributeErrors as $error) {
$owner->addError("{$relationName}[{$attribute}][]", "{$prettyRelationName}: {$error}");
}
}
}
it helps to render any error for related inputs.
How is the right way to build form-field statement and validate input data in my case?
i have a UniquIdBehavior for id (primary key)
this reset populated relations, after yiisoft/yii2#13618 pr merged:
public function __set($name, $value)
{
if ($this->hasAttribute($name)) {
if (
!empty($this->_relationsDependencies[$name])
&& (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)
) {
$this->resetDependentRelations($name);
}
$this->_attributes[$name] = $value;
} else {
parent::__set($name, $value);
}
}
It happens at afterSave, when it compareing 2 active recrods.
right there:
https://github.com/la-haute-societe/yii2-save-relations-behavior/blob/master/src/SaveRelationsBehavior.php#L315
the problem described here: http://www.php.net/manual/en/language.oop5.object-comparison.php
can you use === operator here instead of == ?
Hi,
i have a scenario where i would like to work on huge form saved by this behavior. My only problem is i need to put the ids on the form for multi input if i would like to keep old records, and not always delete and create new ones.
what if somebody is overwriting id-s in hidden input than this behaviour will try to load it from db with that id. It could happen the user receives data which they should not see, and will linked to him.
i would suggest a small extra here, there could be a mode where it can only work with existing models, it checks was the model already in hasmany relation, if not then it will be created.
what do you think?
Hello,
In short words:
If my model has more than one PK like 'id' and 'timestamp'.
Then your code is failing here:
private function _prepareHasOneRelation(BaseActiveRecord $model, $relationName, ModelEvent $event)
{
Yii::debug("_prepareHasOneRelation for {$relationName}", __METHOD__);
$relationModel = $model->{$relationName};
$this->validateRelationModel(self::prettyRelationName($relationName), $relationName, $model->{$relationName});
$relation = $model->getRelation($relationName);
$p1 = $model->isPrimaryKey(array_keys($relation->link)); <<<<HERE only 'id' is passed to isPrimaryKey()
$p2 = $relationModel::isPrimaryKey(array_values($relation->link));
if ($relationModel->getIsNewRecord() && $p1 && !$p2) {
// Save Has one relation new record
if ($event->isValid && (count($model->dirtyAttributes) || $model->{$relationName}->isNewRecord)) {
Yii::debug('Saving ' . self::prettyRelationName($relationName) . ' relation model', __METHOD__);
if ($model->{$relationName}->save()) {
$this->_savedHasOneModels[] = $model->{$relationName};
}
}
}
}
But frankly speaking I'm not sure that bug on your side.
Thats why I've rised same question on YII2 project
And where I described my problem in all details.
If I where you, I would avoid usage of function BaseActiveRecord::isPrimaryKey()
And use your own implementaion instead.
Hello!
Thank you for your work!
I found a little issue. If i defined relation as hasOne, then child relation don't fire before validate event. Change relation to hasMany and all is ok.
Hi,
do you see a solution where it could work with 1:1 connections as well?
i have a table: product (primary key product_id)
and product_coupon (primary key product_id)
and product_room (primary key product_id)
product_coupon, product_room are extending product base info.
what do you think?
Hi,
did you know about this issue with link unlink not working as expected on via tables?
do you have, maybe a solution for this? i don't think so that it will be fixed in yii2 anytime soon.
thanks Péter
If the relation is given as array and not involved in a junction table, related records are are inserted then deleted instead of being updated.
If a relation is explicitly set to null, a new empty model instance is created.
Model relation should be unset instead.
I have
class Project extends \yii\db\ActiveRecord
{
use SaveRelationsTrait; // Optional
public function behaviors()
{
return [
'saveRelations' => [
'class' => SaveRelationsBehavior::className(),
'relations' => ['videos', 'company']
],
];
}
/**
* @return ActiveQuery
*/
public function getCompany()
{
return $this->hasOne(Company::className(), ['id' => 'company_id']);
}
/**
* @return ActiveQuery
*/
public function getVideos()
{
return $this->hasMany(Videos::className(), ['project_id' => 'id']);
}
}
and form
$form->field($project, 'company[name]')->textarea(); // value is it, but not saved
$form->field($project, 'videos[0][name]')->textarea(); // saved good!
$form->field($project, 'videos[1][name]')->textarea(); // saved good!
In ProjectController I see
$_POST[Project][company][name] = 'Company name'; // This value not saved
$_POST[Project][videos][0][name] = 'Video 1 name'; // // This value saved
public function actionUpdate($id)
{
$model = Project::findOne($id);
$request = Yii::$app->request;
$load = $model->load($request->post());
$model->save(); // relation video saved, relation company - not saved
}
Company name not saved in my controller.
Hello!
I don't know exactly is this a bug or not but will try explain my problem.
Suppose we have Orders and OrderItems.
Orders:
class Order ... {
....
public function behaviors()
{
return [
.....
[
'class' => SaveRelationsBehavior::class,
'relations' => [
'orderItems'
]
],
.....
];
}
public function getOrderItems()
{
return $this->hasMany(OrderItem::class, ['order_id' => 'order_id']);
}
public function afterSave($insert, $changedAttributes)
{
var_dump(count($this->orderItems)); die();
retrun parent::afterSave($insert, $changedAttributes);
}
....
}
$order = new Order();
$order->orderItems = OrderItem[]; // simplified code
$order->save();
after that i see that afterSave method: count($this->orderItems) = 0.
but
$order = Order::findOne(1);
$order->orderItems = OrderItem[]; // simplified code
$order->save();
after that i see that afterSave: count($this->orderItems) > 0.
When i insert a new Order, before insert OrderItems i want to modify them. I can modify only when updating the Order, not when inserting.
When i save a model with "hasMany" relation it does validate each of relations from an array and then saving them with ->save(false)
. But i have the unique validation rule in relation model, which shouldn't let it save both of them in this example:
ParentModel [
'property' => 'value',
'relation' => [
[
'name' => 'ex1',
'uniqueField' => 1,
],
[
'name' => 'ex2',
'uniqueField' => 1,//shouldn't be saved
],
],
]
Instead of SELECT /ex1/, INSERT /ex1/, SELECT /ex2/, addError
Like ActiveRecord getOldAttribute
and getOldAttributes
methods, add getOldRelations
and getOldRelation
methods to retrieve relations values prior to their most recent modification.
it creates new linking all the time, if i am populating it from a POST.
becouse of this: (line 279)
$relationModel = null; if (!empty($fks)) { $relationModel = $modelClass::findOne($fks); }
it creates a new instance
than (line 664)
if ($this->_oldRelationValue[$relationName] !== $owner->{$relationName}) {
will be false all the time. it links again, whenever it was a existing relation or not.
i am using php7.1
So I have a relation between user and documents (one to many), linked by conjunction user_documents. If I create new documents, it will trigger afterSave event on User model. Is that expected?
Scenario :
$scenarios[self::SCENARIO_SAVE_STEP_2] = ['company', 'address', 'vehicles', 'id'];
Behavior :
public function behaviors() {
return [
'saveRelations' => [
'class' => SaveRelationsBehavior::className(),
'relations' => [
'company' => ['scenario' => Company::SCENARIO_COMPANY],
'address' => ['scenario' => Address::SCENARIO_PROFILE_COMPANY],
'vehicles' => ['scenario' => Vehicle::SCENARIO_PROFILE_VEHICLE]
]
],
];
}
example : In User Model,
$user = User::findOne($this->id);
$user->scenario = User::SCENARIO_SAVE_STEP_2;
$user->load(Yii::$app->getRequest()->getBodyParams(), '');
Payloads,
{
id:1,
name : "adam",
company : {
id : "2",
userid : "1",
name : "My Company",
logo : {
id : "1",
url : "http://myimage.com/logo.png"
}
},
address : {
id : "1",
userid : "1",
name : "My Company",
},
vehicles : [
{
id : 2,
type : "two wheeler"
licenseImages : [{
id : "3",
url : "http://myimage.com/license.png"
}]
},
{
id : 3,
type : "four wheeler"
licenseImages : [{
id : "4",
url : "http://myimage.com/license.png"
}]
}
]
}
This is working:
1. New record save for hasOne relation, when primary key is given blank.
2. New data for Nested relation hasMany, data is saving.
This is NOT working:
1. Existing record update for hasOne relation, when primary key is given.
2. Existing data for Nested relation hasMany, data is not saving, with primary key given in payload.
3. New data is not saving for hasMany relation when data is given in array (ref. vehicles in above example).
4. Existing data is not saving for hasMany relation when data is given in array (ref. vehicles in above example), with primary key i.e id.
5. New or Existing data for Nested relation hasMany, data is not saving, with primary key given in payload. (ref. vehicles->licenseImages in above example)
My system configuration :
Yii2 = Version 2.0.14
PHP = Version 7.1.20
Mysql Server = Version 5.7.23
Extenstion = Version ^1.5
I have a Profile Model. The model saved into database and has an ID. It has relation
public function getPerson()
{
return $this->hasOne(Person::className(), ['user_id' => 'user_id']);
}
Both user_id are primary key (One to One). But no person record exists.
Of course a have no
line in the view file
Then i save the Profile. It throws an constrait error.
sets primary key of Profile model to null because $model->{$modelAttribute} !== $model->{$relationName}->{$relatedAttribute} where $model->{$relationName}->{$relatedAttribute} is nullIt happens because Person model has not been saved because
Line return false because $p2 is primary key. And person model has been validated only. Why are so difficult conditions (i am not allowed to have both as pk)?#29 has a similar issue. Possibly we don't unserstand how to manipulate hasOne relations..
writes me "Setting foreign keys for person" but sets Profile property to null in my case.It will be great if related models will be sorted and user can swap the related models
Main has many Child, Child has one SubChild.
<?php
namespace api\models;
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
.....
class Main extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public function behaviors()
{
return [
.....
[
'class' => SaveRelationsBehavior::className(),
'relations' => [
'childs'
]
],
.....
];
}
/**
* @return array
*/
public function transactions()
{
return [
self::SCENARIO_DEFAULT => [
self::OP_ALL
]
];
}
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%main}}';
}
/**
* @inheritdoc
*/
public function rules()
{
.....
}
/**
* @return \yii\db\ActiveQuery
*/
public function getChilds()
{
return $this->hasMany(Child::className(), ['child_id' => 'id']);
}
/**
* @inheritdoc
* @return \api\models\scopes\MainQuery the active query used by this AR class.
*/
public static function find()
{
return new \api\models\scopes\MainQuery(get_called_class());
}
}
<?php
namespace api\models;
.....
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
class Child extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public function behaviors()
{
return [
......
[
'class' => SaveRelationsBehavior::className(),
'relations' => [
'subChild'
]
],
.....
];
}
/**
* @return array
*/
public function transactions()
{
return [
self::SCENARIO_DEFAULT => [
self::OP_ALL
]
];
}
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%child}}';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
....
];
}
public static function create(/** some params **/)
{
$iteration = new static();
$iteration->subChild = SubChild::create(/** some params **/);
return $child;
}
/**
* @return \yii\db\ActiveQuery
*/
public function getSubChild()
{
return $this->hasOne(SubChild::className(), ['sub_child_id' => 'id']);
}
public static function find()
{
return new \api\models\scopes\ChildQuery(get_called_class());
}
}
<?php
namespace api\models;
class SubChild extends \yii\db\ActiveRecord
{
/**
* @return array
*/
public function transactions()
{
return [
self::SCENARIO_DEFAULT => [
self::OP_ALL
]
];
}
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%sub_child}}';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
....
];
}
/**
* @return static
*/
public static function create(/** some params **/)
{
$subChild = new static();
return $subChild;
}
}
$banner = new Main();
$banner->load($form->attributes,'');
$banner->child = Child::create(/** some params**/);
$banner->save();
In this case relations must saves recursively, but in beforeValidate behavior method raises exception:
[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 (`schema`.`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` (`state`, `created_at`) VALUES ('skipped', UNIX_TIMESTAMP())
in /dd_main/web/sites/chulakov/bander/api/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:300
in /dd_main/web/sites/chulakov/bander/api/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:233
in /dd_main/web/sites/chulakov/bander/api/vendor/la-haute-societe/yii2-save-relations-behavior/src/SaveRelationsBehavior.php:340
2017-11-06 12:33:16 [192.168.1.1][1][-][error][yii\db\Exception] exception 'yii\db\Exception' with message 'Failed to commit transaction: transaction was inactive.' in /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/db/Transaction.php:151
Stack trace:
#0 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/db/ActiveRecord.php(486): yii\db\Transaction->commit()
#1 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/db/BaseActiveRecord.php(646): yii\db\ActiveRecord->insert(true, NULL)
#2 /dd_main/web/sites/chulakov/bander/api/api/repositories/BannerRepository.php(17): yii\db\BaseActiveRecord->save()
#3 /dd_main/web/sites/chulakov/bander/api/api/services/BannerService.php(64): api\repositories\BannerRepository->save(Object(api\models\Banner))
#4 /dd_main/web/sites/chulakov/bander/api/api/controllers/BannerController.php(95): api\services\BannerService->upload(Object(api\forms\BanderUpload))
#5 [internal function]: api\controllers\BannerController->actionUpload()
#6 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#7 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#8 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/base/Module.php(528): yii\base\Controller->runAction('upload', Array)
#9 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/web/Application.php(103): yii\base\Module->runAction('banner/upload', Array)
#10 /dd_main/web/sites/chulakov/bander/api/vendor/yiisoft/yii2/base/Application.php(386): yii\web\Application->handleRequest(Object(yii\web\Request))
#11 /dd_main/web/sites/chulakov/bander/api/api/web-test/index.php(18): yii\base\Application->run()
#12 {main}
Foreign key value not append to insert query. This error appears in saving with validation. If save the main instance without validation no errors raised.
There is no SaveRelationsTrait.php file, when I install this extension throw composer.
"src" folder contains only SaveRelationsBehavior.php
Hello!
https://monosnap.com/file/BV4PyFdUKuOLvhoj6K3a06wNKcqpPS
I have a problem with saving parent model. When I specifying relation on line SaveRelationBehavior:253 behavior replace primary key of main model and set foreign key of related model.
foreach ($relation->link as $relatedAttribute => $modelAttribute) { if ($model->{$modelAttribute} !== $model->{$relationName}->{$relatedAttribute}) { $model->{$modelAttribute} = $model->{$relationName}->{$relatedAttribute}; } }
but I have related attribute value is NULL and it's breaking saving process;
I think, solution is check NOT EMPTY related attribute ($model->{$relationName}->{$relatedAttribute})
You have an error from 384 to 386 lines.
$p1 = $model->isPrimaryKey(array_keys ($relation->link));
$p2 = $relationModel::isPrimaryKey(array_values($relation->link));
if ($relationModel->getIsNewRecord () & & $p1 && !$p2) {
...
What's the point? With such a relations
public function getCompany()
{
return $this->hasOne(Company:: className (), ['id' = > 'company_id']);
}
if the parent class has properties primarykey (in your example on the main page Project ) the same as the relation class (in your example on the main page Company ) then your behavior will work.
But if in Project establish primaryKey as project_id the will error
Unable to link models: the primary key of ...
because relation class not save
as for me it is more correct to write the following code
$p1 = $model->isPrimaryKey(array_values($relation->link));
$p2 = $relationModel:: isPrimaryKey(array_keys($relation->link));
if ($relationModel->getIsNewRecord() && !$p1 & & $p2) {
почему так для relation
$this->hasOne(Company::className(), ['id' => 'company_id']);
array_keys($relation->link)='id'
array_values($relation->link)='company_id'
Now some properties and methods are declared as "private". It does not allow to expand the behavior.
I suggest instead of "private" to use "protected".
hi,
i just cannot find out how this is working. i did put it in my project, did set up as an document, but it is just not working. i did recognised some parts which could be buggy, but of course if it works for everyone else, than the problem will be with me.
if i add new line here :
protected function _setRelationForeignKeys($relationName)
{
/** @var BaseActiveRecord $owner /
$owner = $this->owner;
/* @var ActiveQuery $relation */
$relation = $owner->getRelation($relationName);
if ($relation->multiple === false && !empty($owner->{$relationName})) {
Yii::debug("Setting foreign keys for {$relationName}", METHOD);
foreach ($relation->link as $relatedAttribute => $modelAttribute) {
if ($owner->{$modelAttribute} !== $owner->{$relationName}->{$relatedAttribute}) {
if ($owner->{$relationName}->isNewRecord) {
$owner->{$relationName}->{$relatedAttribute} = $owner->{$modelAttribute};
$owner->{$relationName}->save();
}
$owner->{$modelAttribute} = $owner->{$relationName}->{$relatedAttribute};
}
}
}
}
than it starts working for me as well. i just could not find out where will be set all the foreign keys.
the another one is:
_prepareHasOneRelation
in my mind this one is wrong:
$p1 = $model->isPrimaryKey(array_keys($relation->link));
$p2 = $relationModel::isPrimaryKey(array_values($relation->link));
the first one should be array_values, second array_keys. but maybe i don't understand the porpuse of the method.
my setup is really strait forward:
'saveRelations' => [
'class' => SaveRelationsBehavior::class,
'relations' => [
'personDetail',
'personAddress',
'personEmails',
'personPhones',
'personSocials',
'user',
],
]
but it cannot handle new records, it deals perfect with existing ones. My db should be ok, gii can generate model properly.
The transaction object may not be defined if an exception occurs in the _saveRelatedRecords method and no transactions are declared in the owner model.
The hasOne relationships are saved and inserted in beforeValidate and if owner model not inserted due to validation error hasOne inserted relations not rollback.
How to Test:
In model Project, name attribute is required and if you don't set name, $project->save() will be failed, but company record willl be inserted in database.
public function testSaveNewHasOneRelationShouldSucceed()
{
$project = new Project();
//$project->name = "Java";
$company = new Company();
$company->name = "Oracle";
$project->company = $company;
$this->assertTrue($company->isNewRecord, 'Company should be a new record');
$this->assertTrue($project->save(), 'Project could not be saved');
$this->assertNotNull($project->company_id, 'Company ID should be set');
$this->assertEquals($project->company_id, $company->id, 'Company ID is not the one expected');
}
After updating Yii2 to 2.0.14 my Many-to-many relation stopped working.
I returned my yii2 to 2.0.13.1 (as I used to work before), and relation continued to work.
For hasMany relations not using a junction table with via() method, records are not saved during the beforeValidate event if the foreign key is set as 'NOT NULL'.
These kind of record should be saved during the afterSave event of the owner model.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.