Ho to use transactions in AR when using save aproach from blog tutorial

I've following AR two definitions

News.php (only important part)



	public function rules()


	{


		return array(


			array('title','length','max'=>100),


			array('author_desc','length','max'=>20),


			array('title, author_desc, date, time', 'required'),


			array('total_comments', 'numerical', 'integerOnly'=>true),


		);


	}





	/**


	 * @return array relational rules.


	 */


  public function relations()


  {


    return array(


      'author'=>array(self::BELONGS_TO, 'User', 'author_id'),


      'comments'=>array(self::HAS_MANY, 'NewsComment', 'news_id'),


      'category'=>array(self::HAS_MANY, 'NewsCategory', 'news_id'),


      'txt'=>array(self::HAS_ONE, 'NewsTxt', 'news_id'),


    );


NewsTxt, only important part



	public function rules()


	{


		return array(


			array('text', 'required'),


		);


	}


I've generated News controller and News create view and _form.php. Some of them I've modified a little bit. Below you will find changes:

NewsController



	public function actionCreate()


	{


		$news=new News;


		$news->txt=new NewsTxt();


		if(isset($_POST['News'])&&isset($_POST['NewsTxt']))


		{


			$news->attributes=$_POST['News'];


			$news->txt->attributes=$_POST['NewsTxt'];


			if($news->save())


			{


			  $this->redirect(array('show','id'=>$news->id));


			}


		}


		$this->render('create',array('news'=>$news));


	}


_form.php



    <div class="simple">


      <?php echo CHtml::activeLabelEx($news->txt,'text'); ?>


      <?php echo CHtml::activeTextArea($news->txt,'text'); ?>


    </div>


</fieldset>


afterSave in News model






  protected function afterSave()


  {


    if ($this->isNewRecord)


    {


      $this->txt->news_id=$this->id;


      $this->txt->save();


    }


  }


Questions

  • How can I use in this approach transactions to rollBack entries saved in News table when saving in afterSave will  fails?
  • Should I use another aproach to achieve my coal?
  • When in blog tutorial/definitive guide will appear extended description containing transactions (current description is really minimalistic)

Did you read the last section in this page? http://www.yiiframew…ide/database.ar

I read this section. But my scenario is a little bit different. I've two tables: news and news_txt represented by two models: News and NewsTxt.

When I'm creating new news I'm displaying $news->txt->text coming from news_txt  table (txt is defined in relation part of News model class).

If you will look careful on above example, you will see that after save I'm saving content of the news in another DB table. This means that in case that update on news will fail, news_txt will be not updated, because afterSave will not be called. This is fine and expected. Problem comes when first save of news table will finish with success and save in afterSave will fail. Then I'll inconsistency in DB table. I have to avoid such  situation. Therefore I need to use somehow transaction. Question is how I can use it, when I'm saving data in second table in afterSave?

I know there are two tables here, and that's why you need transaction. The code needed should be like the following:



<?php


$transaction=Yii::app()->db->beginTransaction();


try


{


    $model1->save();


    $model2->save();  // or if this is called in model1's afterSave()


    $transaction->commit();


    // redirect...


}


catch(Exception $e)


{


    $transaction->rollBack();


}


Hm… I do not follow you… where I should put this code? In my controller, in action create? If yes, this may be answer on my 2nd question.

I'll try to clarify my scenario.

I've two models. First is News second is NewsTxt. In first News model there is relation called txt which point to table NewsTxt and foreign key is news_id. In NewsController I've returned (after submit button is pressed) following data in $_POST:

  • $_POST['News']

  • $_POST['NewsTxt]

I'm passing this data inyo action create following way:



$news->attributes=$_POST['News'];


$news->txt->attributes=$_POST['NewsTxt'];


Now I'm saving data coming from NewsTxt in afterSave();

I'm wandering if I can somehow this part of from afterSave put as a part of transaction?

This is becaus I do not wan't to replicate code from create action into update action.

Summarizing: I want to achieve following scenario (in pseudocode)

for createAction

  • startTransaction()

  • createDataOfNewsMode()

  • callAfterSaveWhenNewsTxtModelIsSavedAndCreateNewEntry()

  • commitOrRollBackTransaction();

for UpdateAction

  • startTransaction()

  • updateDataOfNewsMode()

  • callAfterSaveWhenNewsTxtModelIsSavedAndUpdateData()

  • commitOrRollBackTransaction();

I really like afterSave() method because it suggest real flow in the process. If I can somehow say that afterSave() is a part of transaction then I'll have a really powerful solution. I'll be able to "concatenate" all saves coming from relations into chain (by passing/doing something with transaction in afterSave());

The code is in controller action, but you can also put it in a new method in model1 so that your controller doesn't contain business logic like this. It is perfectly fine that you save model2 in afterSave of model1.