Задачка довольно простая, уверен многие из вас уже сталкивались с подобной проблемой, интересует поиск наиболее удачного и простого решения.
В общем, есть 2 таблицы с отношением "один ко многим", необходимо в рамках одной формы создания/редактирования сохранить данные для обоих моделей.
Раньше в Yii 1 я делал это так:
- в контроллере:
Код: Выделить всё
public function actionCreate() {
$model = new Order();
// пришли данные из основной модели
if ($orderData = Yii::app()->request->getPost('Order')) {
$model->setAttributes($orderData);
$orderItems = Yii::app()->request->getPost('orderItems', array());
if ($model->save()) {
// если пришли данные из связной модели - сохраняем их
if ($orderItems)
$model->saveOrderItems($orderItems);
//...
}
}
// ...
$this->render('create', ['model'=>$model]);
}
public function actionUpdate($id) {
$model = $this->loadModel($id);
// пришли данные из основной модели
if ($orderData = Yii::app()->request->getPost('Order')) {
$model->setAttributes($orderData);
$orderItems = Yii::app()->request->getPost('orderItems', array());
if ($model->save()) {
// если пришли данные из связной модели - сохраняем их
if ($orderItems)
$model->saveOrderItems($orderItems);
//...
}
}
// ...
$this->render('update', ['model'=>$model]);
}
Код: Выделить всё
public function relations()
{
return array(
'orderItems' => array(self::HAS_MANY, 'OrderItem', 'order_id'),
//...other relations
);
}
public function saveOrderItems($orderItems) {
if (!$orderItems)
return false;
$transaction = Yii::app()->getDb()->beginTransaction();
try {
foreach ($orderItems as $item) {
$decodedArray = json_decode($item, true);
if (isset($decodedArray['id']) && !empty($decodedArray['id']))
$orderItem = OrderItem::model()->findByPk($decodedArray['id']);
else
$orderItem = new OrderItem();
$orderItem->position_title = $decodedArray['position_title'];
$orderItem->position_price = $decodedArray['position_price'];
//...
$orderItem->order_id = $this->id;
$orderItem->save();
}
$transaction->commit();
return true;
} catch (Exception $e) {
$transaction->rollback();
return false;
}
}
Код: Выделить всё
<?php $form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array('id'=>'order-form')); ?>
<!-- some code here -->
<?php if(!$model->isNewRecord && $model->orderItems): ?>
<?php $i=1; ?>
<?php foreach($model->orderItems as $orderItem): ?>
<div id="order-item-group" style="width: 50%; border: 1px solid #ccc; border-radius: 3px; padding: 10px; margin-bottom: 10px;" data-tnum="<?=$i?>">
<?= CHtml::hiddenField('orderItemId', $orderItem->id) ?>
<div class="row-fluid control-group <?= $orderItem->hasErrors('position_title') ? 'error' : ''; ?>">
<?= CHtml::activeLabelEx($orderItem, 'position_title') ?>
<?= CHtml::textField('position_title', $orderItem->position_title, ['class'=>'span5', 'maxlength'=>13]) ?>
</div>
<div class="row-fluid control-group <?= $orderItem->hasErrors('position_price') ? 'error' : ''; ?>">
<?= CHtml::activeLabelEx($orderItem, 'position_price') ?>
<?= CHtml::textField('position_price', $orderItem->position_price, ['class'=>'span5', 'maxlength'=>13]) ?>
</div>
<!-- other attributes... -->
<div style="text-align: right;"><?= CHtml::button('Удалить', ['id'=>'delete_order_item_group', 'class'=>'btn btn-danger btn-sm', 'data-id'=>$orderItem->id]) ?></div>
</div>
<?php $i++; ?>
<?php endforeach; ?>
<?php else: ?>
<?php $orderItem = OrderItem::model() ?>
<div id="order-item-group" style="width: 50%; border: 1px solid #ccc; border-radius: 3px; padding: 10px; margin-bottom: 10px;" data-tnum="0">
<?= CHtml::hiddenField('orderItemId'); ?>
<div class="row-fluid control-group">
<?= CHtml::activeLabelEx($orderItem, 'position_title') ?>
<?= CHtml::textField('position_title', '', ['class'=>'span7', 'maxlength'=>13]) ?>
</div>
<div class="row-fluid control-group">
<?= CHtml::activeLabelEx($orderItem, 'position_price') ?>
<?= CHtml::textField('position_price', '', ['class'=>'span7', 'maxlength'=>13]) ?>
</div>
<!-- other attributes... -->
<div style="text-align: right;"><?= CHtml::button('Удалить', ['id'=>'delete_order_item_group', 'class'=>'btn btn-danger btn-sm', 'data-id'=>'']) ?></div>
</div>
<?php endif; ?>
<?= CHtml::button('Добавить еще одну позицию', ['id'=>'add_order_item_group', 'class'=>'btn']) ?>
<!-- some code here -->
<a id="save-btn" class="btn btn-primary"><?= $model->isNewRecord ? 'Добавить : 'Сохранить'; ?></a>
<?php $this->endWidget(); ?>
<script type="text/javascript">
$(function() {
$('#save-btn').on('click', function(e) {
e.preventDefault();
setOrderItems();
$('form#order-form').submit();
});
function setOrderItems() {
$('div#order-item-group').each(function() {
let id = $(this).find('#orderItemId').val(),
position_title = $(this).find('#position_title').val(),
position_price = $(this).find('#position_price').val(),
obj;
if (position_title && position_price) {
obj = JSON.stringify({
'id': id,
'position_title': position_title,
'position_price': position_price
});
$(this).append('<input id="orderItems" type="hidden" name="orderItems[]" value="">');
$(this).find('#orderItems').val(obj);
}
});
}
$('#add_order_item_group').on('click', function() {
let i = $(this).prev().attr('data-tnum'),
orderItemHtml = $(this).prev('#order-item-group[data-tnum=\"'+i+'\"]').clone();
orderItemHtml.find('#orderItemId').val('');
orderItemHtml.find('#position_title').val('');
orderItemHtml.find('#position_price').val('');
orderItemHtml.attr('data-tnum', i+1);
$('#order-item-group[data-tnum=\"'+i+'\"]').after(orderItemHtml);
});
$(document).delegate('#delete_order_item_group', 'click', function() {
let orderItemGroup = $(this).parents('#order-item-group'), id = ordeItemGroup.find('#orderItemId').val();
if (id) {
$.ajax({
type: 'post',
url: '/ajax/deleteOrderItem',
data: {id: id, '<?= $csrfTokenName; ?>': '<?= $csrfToken; ?>'},
success: function() {
orderItemGroup.hide('slow', function() {
$(this).remove();
});
}
});
} else {
orderItemGroup.hide('slow', function() {
$(this).remove();
});
}
});
}
</script>
Пожалуйста, поделитесь хорошим и элегантным решением как это у себя в проекте реализовали вы (желательно на примере Yii2).
Спасибо!