Как сохранить связанные данные Yii

14 ноября 2013 г.

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

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

yii Необходимо создать две таблицы, для хранения данных о пользователе. Одна называется 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, наша форма заполнится данными

Кирилл aka kxxb

Теги:
рубрика PHP