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

в 8:21, , рубрики: Без рубрики

Intro

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

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

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

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

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

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

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

При помощи генератора кода 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

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

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

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

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

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

Outro

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

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

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

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

Автор: kxxb

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js