Пошаговое руководство сохранения связанных данных Yii

от автора

Intro

С недавних пор, я начал изучать замечательный фрэймворк Yii. При разработке, я столкнулся с задачей сохранения данных из одной формы, в несколько таблиц. Погуглив, я не нашел вменяемого руководство, которое объясняет полный смысл этого сохранения. На официальном, русскоязычном сайте, я нашел короткую статью от Александра Макарова, но она, опять же в общих чертах демонстрирует «соль» этого метода.
Я решил написать эту статью, в стиле tutorial, чтобы дать новичкам возможность наглядно увидеть полный цикл CRUD при работе с несколькими моделями, а тем кто по-опытней, покрикивать это решение, и объяснить «как делать не надо».

Постановка задачи

Необходимо создать две таблицы, для хранения данных о пользователе. Одна называется user — предназначена для хранения логина и пароля пользователя, его статуса и его глобального идентификатора, который будет использоваться во всей системе. Вторая user_profile которая предназначена для хранения публичных данных о пользователе, его имени и фамилии и т.п. Таблица профиля связана с таблицей пользователей, при помощи внешнего ключа.
Необходимо сохранять и редактировать данные о пользователя, из одной единой формы, которая включает в себя поля как таблицы user так и user_profile

Создание таблиц БД

Создадим две таблицы такого вида
user — родительская таблица, где создаётся id пользователя
user_profile — дочерняя таблица, имеет внешний ключ user_id на родительскую таблицу

Создание моделей

При помощи генератора кода gii, создадим модели этих таблиц и назовем их соответственно User и UserProfile.
Так же при помощи gii создадим CRUD для модели User (замечу, что для модели UserProfile, я намеренно не создаю CRUD, так как он нам не понадобиться)

Доработка родительской модели

В родительскую модель, нам необходимо добавить поля из дочерней модели:

<?php class User extends CActiveRecord {        ////добавление начато        public $name;         public $first_name;        public $description; ////добавление окончено ... public function attributeLabels() { 		return array( 			'id' => 'ID', 			'login' => 'Login', 			'password' => 'Password',  			'status' => 'Status', ////добавление начато                         'name'=>'Имя',                         'first_name'=>'Фамилия',                         'description'=>'Описание' ////добавление окончено 		); 	} 

А также определить метод AfterSave добавив код:

////добавление начато  protected function afterSave() {             parent::afterSave();             if($this->isNewRecord){       	  // если мы создаем нового пользователя, тогда нам необходимо создать            // для него запись в таблице профиля с ссылкой на родительскую таблицу              $user_profile = new UserProfile;              $user_profile->user_id =     $this->id;              $user_profile->name =        $this->name;              $user_profile->first_name =  $this->first_name;              $user_profile->description = $this->description;              $user_profile->save();             } else {  	// иначе неободимо обновить данные в таблице профиля              UserProfile::model()->updateAll(array( 'user_id' =>$this->id,                                                  'name' => $this->name,                                                     'first_name'=>$this->first_name,                                                 'description'=>$this->description                     ), 'user_id=:user_id', array(':user_id'=> $this->id));             }         } ////добавление окончено 

Теперь по шагам, что тут произошло:
1) добавили три публичные переменные, которые соответствуют полям модели UserProfile,
теперь это новые поля в модели User

       public $name;         public $first_name;        public $description; 

2) в методе attributeLabels(), создаем описания для новых полей

          'name'=>'Имя',           'first_name'=>'Фамилия',           'description'=>'Описание' 

3) Теперь создаем метод afterSave, который срабатывает после сохранения данных в модели User,
и в тут же будем сохранять данные в UserProfile.

Таким образом, мы проверяем, что сейчас происходит: создание новой записи или редактирование существующей.

  if($this->isNewRecord){   

Если создался новый пользователь, то:

  • Создаю экземпляр модели UserProfile
  • Получаем ID созданного пользователя, и присваиваем это значение полю
    $user_profile->user_id = $this->id;
  • Присваиваю полям модели UserProfile, значения пришедшие из формы (то как мы получаем эти данные из формы, смотрим действие actionCreate в котнтроллере UserController)
  • Выполняем метод save() у модели UserProfile

Если это была операция редактирования, то:

  • необходимо выполнить метод updateAll для модели UserProfile

UserProfile::model()->updateAll(array( 'user_id' =>$this->id,                                                  'name' => $this->name,                                                     'first_name'=>$this->first_name,                                                 'description'=>$this->description                     ), 'user_id=:user_id', array(':user_id'=> $this->id)); 

Здесь значения заполняются из действия actionUpdate контроллера UserController

Доработка контроллера

Теперь открываем свеже-сгенирированный контроллер UserController.
В нём, нам предстоит поправить два действия actionCreate и actionUpdate,
и одну функцию loadModel

actionCreate

Присваиваем значения, публичным переменным, которые мы добавили в модели.
Вот отсюда используются данные, в методе afterSave

public function actionCreate() 	{  	$model=new User;    	   if(isset($_POST['User'])) 		{        			$model->attributes=$_POST['User']; ////добавление начато                         $model->name = $_POST['User']['name'];                         $model->first_name = $_POST['User']['first_name'];                         $model->description = $_POST['User']['description']; ////добавление оконченно 			if($model->save()) 				$this->redirect(array('view','id'=>$model->id)); 		} 		$this->render('create',array( 			'model'=>$model, 		)); 	} 
actionUpdate

Здесь, происходит аналогичный процесс, что и при создании пользователя

	public function actionUpdate($id) 	{ 		$model=$this->loadModel($id);     	      if(isset($_POST['User'])) 		{ 			$model->attributes=$_POST['User']; ////добавление начато                         $model->name = $_POST['User']['name'];                         $model->first_name = $_POST['User']['first_name'];                         $model->description = $_POST['User']['description']; ////добавление оконченно			if($model->save()) 				$this->redirect(array('view','id'=>$model->id)); 		}  		$this->render('update',array( 			'model'=>$model, 		)); 	} 
loadModel

Тут мы добавляем такие строки.
Нам необходимо загрузить данные из таблицы профиля пользователя, найденные по его user_id.

	public function loadModel($id) 	{ 		$model=User::model()->findByPk($id); ////добавление начато                 $modelprofile=UserProfile::model()->find('user_id=:user_id', array(':user_id'=> $id));                 $model->name =  $modelprofile->name;                 $model->first_name = $modelprofile->first_name;                 $model->description = $modelprofile->description; ////добавление оконченно 		if($model===null) 			throw new CHttpException(404,'The requested page does not exist.'); 		return $model; 	} 

Это необходимо для того, чтобы данные загружались в форму, когда мы нажимаем на ссылку Update User,
и для отображении информации в просмотровом представлении

Доработка родительской формы

В форме, которая находиться по адресу protected/views/user/_form.php
нам необходимо добавить элементы для ввода имени, фамилии и описания пользователя

<?php <div class="form"> <?php $form=$this->beginWidget('CActiveForm', array( 	'id'=>'user-form', 	'enableAjaxValidation'=>false, )); ?> 	<p class="note">Fields with <span class="required">*</span> are required.</p> 	<?php echo $form->errorSummary($model); ?> 	<div class="row"> 		<?php echo $form->labelEx($model,'login'); ?> 		<?php echo $form->textField($model,'login',array('size'=>45,'maxlength'=>45)); ?> 		<?php echo $form->error($model,'login'); ?> 	</div> ..... ////добавление начато         <div class="row"> 		<?php echo $form->labelEx($model,'name'); ?> 		<?php echo $form->textField($model,'name',array('size'=>45,'maxlength'=>45)); ?> 		<?php echo $form->error($model,'name'); ?> 	</div>        	<div class="row"> 		<?php echo $form->labelEx($model,'first_name'); ?> 		<?php echo $form->textField($model,'first_name',array('size'=>45,'maxlength'=>45)); ?> 		<?php echo $form->error($model,'first_name'); ?> 	</div> 	<div class="row"> 		<?php echo $form->labelEx($model,'description'); ?> 		<?php echo $form->textField($model,'description',array('size'=>45,'maxlength'=>45)); ?> 		<?php echo $form->error($model,'description'); ?> 	</div> ////добавление оконченно         	<div class="row buttons"> 		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?> 	</div> <?php $this->endWidget(); ?> </div><!-- form --> 

Доработка представления

Файл представления, который находиться по адресу protected/views/user/view.php, мы, так же добаляем наши новые поля в виджет детального отображения

 <?php  .... <?php $this->widget('zii.widgets.CDetailView', array( 	'data'=>$model, 	'attributes'=>array( 		'id', 		'login', 		'password', 		'status', ////добавление начато                 'name',                 'first_name',                 'description' ////добавление оконченно         	), )); ?> 

а во вспомогательном файле protected/views/user/_view.php добавим следующее:

<?php /* @var $this UserController */ /* @var $data User */ ?>  <div class="view"> 	<b><?php echo CHtml::encode($data->getAttributeLabel('id')); ?>:</b> 	<?php echo CHtml::link(CHtml::encode($data->id), array('view', 'id'=>$data->id)); ?> 	<br /> ... ////добавление начато  <b><?php echo CHtml::encode($data->getAttributeLabel('name')); ?>:</b> 	<?php echo CHtml::encode($data->name); ?> 	<br />         <b><?php echo CHtml::encode($data->getAttributeLabel('first_name')); ?>:</b> 	<?php echo CHtml::encode($data->first_name); ?> 	<br />         <b><?php echo CHtml::encode($data->getAttributeLabel('descrption')); ?>:</b> 	<?php echo CHtml::encode($data->descrption); ?> <br /> ////добавление оконченно         </div> 

Проверка результата

Теперь, если всё сделанно как описанно выше, переходим по адресу
localhost/YourProjectName/index.php?r=user/create

Заполняем все поля, и нажимаем Create.
После чего, должны увидеть такой результат:

Если мы хотим редактировать эту запись, нажимаем на ссылку Update User,
наша форма заполнится данными

Outro

Я надеюсь, что статья будет полезна начинающим разработчикам, и сэкономит время при поиске аналогичного решения.
Принимается любая конструктивная критика, опытных разработчиков, сдобренная личными примерами.

Спасибо за внимание!

Используемая литература

Статья из раздела рецепты "Сохранение связанных данных".

ссылка на оригинал статьи http://habrahabr.ru/post/200478/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *