Reputation: 595
One function in my model use the transaction to save a row in two distinct tables, table_1 and table_2. In table_2 one foreign key refers to table_1.id and the validation rule, auto generate by gii, is type "exist". When I need to save rows the first step is to begin a database transaction, the second is to set and save a table_1 row and at the end set and save the table_2 rows related with table_1 row, if both inserts are ok the transactions are committed otherwise they are rolled back.
The problem is when I pass to table_2 the id of table_1 row and the validation fails because the id of table_1 is not valid, but the id is generated in the same script, is this a problem with transaction?
Edit 1:
The operations which generates error:
$order = new OrdersToImport();
$transaction = OrdersToImport::getDb()->beginTransaction();
... //operations on $order
if($order->save()){
$detail = new OrdersToImportD();
... //operations on $detail
$detail->id_order = $order->id;
if(!$detail->save()){
$transaction->rollback();
return -1;
}
}
The code for data validation:
[['id_order'], 'exist', 'skipOnError' => true, 'targetClass' => OrdersToImport::className(), 'targetAttribute' => ['id_order' => 'id']]
Edit 2:
The result of:
if(!$detail->save()){
echo "$order->id";
echo "$detail->id_order";
var_dump($detail->errors);
die();
}
is:
187
187
array(1) { ["id_order"]=> array(1) { [0]=> string(20) "Id Order is invalid." } }
Upvotes: 0
Views: 1823
Reputation: 2382
These was generated from gii, I suppose that all the rules thus generated are right
Yes the rules are correct, and they are good in most use cases.
that does not mean that they will work in every situation;
i assume from the comments that your structure is something like this:
(and if i'm wrong please update your question with the appropiate details):
i'm gonna call them Order and OrderDetail for the sake of simplicity
generated models: these contain the existance
rule you mentioned
common\models\Order
common\models\OrderDetail
models with custom db: these contain a different definition of getDb()
and extend the two generated modules above
common\modules\samplemodule\models\Order
common\modules\samplemodule\models\OrderDetail
now the models in samplemodule
will inherit the rules of the generated models.
note the targetClass
of this generated rule in common\models\OrderDetail
:
[['id_order'], 'exist', 'skipOnError' => true, 'targetClass' => Order::className(), 'targetAttribute' => ['id_order' => 'id']]
Order::className()
means common\models\Order::className()
that means that all child classes (regardless of namespace) will have a existance rule that refferences common\models\Order
.
and in your case: modules\samplemodule\models\OrderDetail
(which uses a different db) will validate against the existence of a common\models\Order
(an order from the default database)
so here's my proposed solution:
for common\models\OrderDetail
(the generated class) remove the existence
rules, and define them in a separate method
namespace common\models;
class OrderDetail extends ActiveRecord {
//..
public function rules(){
return ArrayHelper::merge([
// ..
// all the default generated rules except the existance ones
], static::existenceRules());
}
protected static function existenceRules(){
return [
[['id_order'], 'exist', 'skipOnError' => true,
// fqn not required it's just here to highlight the difference
'targetClass' => common\models\Order::className(),
'targetAttribute' => ['id_order' => 'id']]
];
}
// ..
}
for common\modules\samplemodule\models\OrderDetail
overwrite the existanceRules()
method we created previously an link the correct target class
namespace common\modules\samplemodule\models;
class OrderDetail extends common\models\OrderDetail {
//..
// custom db:
public static function getDb(){
return Yii::$app->moduleDatabase;
}
// optional (if you need more rules here):
public function rules(){
return ArrayHelper::merge( parent::rules(), [
// rules that apply only in this context (this db)
]);
}
// this is required if to reference the correct `targetClass`
protected static function existenceRules(){
return [
[['id_order'], 'exist', 'skipOnError' => true,
'targetClass' => common\modules\samplemodule\models\Order::className(),
'targetAttribute' => ['id_order' => 'id']]
];
}
// ..
}
in both cases, i've used the full name of the target class to help highlight the difference, since they use different databases the same rule cannot work in both parent and child class.
hope this is helpfull to you. best of luck.
Upvotes: 0