How to clone an active record with all attribute values


(smohadjer) #1

When I try to clone a row from my database table using an ActiveRecord model I notice that most attributes in cloned model have no values. Any idea why the following snippet doesn’t copy value of all attributes? I even went ahead a defined a scenario in which I have specified all attributes as safe, but that didn’t help either.

$model = Events::findOne($id);
$model->scenario = 'scenarioDuplicate';
if ($model->load($request->post()) && $model->validate()) {
	$clone = new Events;
	$clone->attributes = $model->attributes;
	$clone->isNewRecord = true;
	$clone->event_id = null;
	$clone->save();
}

(smohadjer) #2

My bad. I should have assigned my safe scenario to cloned model, not the original model.


(smohadjer) #3

Isn’t using setAttributes($values, false) a better approach for cloning an ActiveRecord model? My approach above requires me to create additional scenarios to define all attributes as safe before I can clone them and this results in unnecessary complexity in my validation rules. Invoking setAttributes with false as 2nd parameter saves me from creating additional scenarios just for the sake of cloning. What’s the best practice for cloning a row in database in Yii?


(Softark) #4

Hi @smohadjer,

I think you can do it without cloning a model.

$model = Events::findOne($id);
if ($model->load($request->post()) && $model->validate()) {
	$model->isNewRecord = true;
	$model->event_id = null;
	$model->save(false);
}

(Softark) #5

This is a simplified code of actionCreate() controller method which is actually used in one of my projects.

    public function actionCreate($id = null)
    {
        if ($id === null) {
            $model = new Slip();
        } else {
            $model = $this->findModel($id);
            $model->id = null;
            $model->isNewRecord = true;
        }
        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['index']);
        }
        return $this->render('create', [
            'model' => $model,
        ]);
    }

As you see, the method takes $id parameter which defaults to null.
When $id is null the method works as same as the standard actionCreate method, creating a new record in DB.
But when $id holds the ID of an existing record, the method works differently. It loads an existing record and saves it as a new one, possibly with some modification by the end user. This is quite a handy trick for some use cases.


(smohadjer) #6

Thanks so much. It didn’t occur to me that it could be as simple as that! :slight_smile: