Я работаю над yii2. На мой взгляд, я пытаюсь загрузить изображение. Но я не могу его загрузить.
Модель
class MeterAcceptanceHeader extends \yii\db\ActiveRecord
{
public static $status_titles =[
0 => 'Prepared',
1 => 'Created',
2 => 'Printed',
3 => 'Canceled',
];
/**
* @inheritdoc
*/
public static function tableName()
{
return 'meter_acceptance_header';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['sub_div', 'prepared_by'], 'required'],
[['prepared_by', 'updated_by'], 'integer'],
[['prepared_at', 'updated_at'], 'safe'],
[['sub_div', 'meter_type', 'status'], 'string', 'max' => 100],
[['images'], 'string', 'max' => 255],
[['images'], 'file', 'skipOnEmpty' => true, 'extensions' => 'png,jpg,pdf', 'maxFiles' => 4],
[['sub_div'], 'exist', 'skipOnError' => true, 'targetClass' => SurveyHescoSubdivision::className(), 'targetAttribute' => ['sub_div' => 'sub_div_code']],
[['prepared_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['prepared_by' => 'id']],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'sub_div' => 'Sub Div',
'meter_type' => 'Meter Type',
'prepared_by' => 'Prepared By',
'prepared_at' => 'Prepared At',
'updated_at' => 'Updated At',
'status' => 'Status',
'updated_by' => 'Updated By',
'images' => 'Document Snap',
];
}
/**
* @return \yii\db\ActiveQuery
*/
public function getMeterAcceptanceDetails()
{
return $this->hasMany(MeterAcceptanceDetails::className(), ['accpt_id' => 'id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getSubDiv()
{
return $this->hasOne(SurveyHescoSubdivision::className(), ['sub_div_code' => 'sub_div']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getPrepared()
{
return $this->hasOne(User::className(), ['id' => 'prepared_by']);
}
}
MeterAcceptanceHeader Таблица
Таблица приемочных изображений счетчика
Существует form1, в котором пользователю предлагается выбрать из раскрывающегося списка.
Форма 1 Просмотр
<div class = "meter-acceptance-header-form">
<?php $model->status = common\models\MeterAcceptanceHeader::$status_titles[0]; ?>
<?php $form = ActiveForm::begin(['id'=>'acceptance-form','options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->field($model, 'sub_div')->dropDownList([''=>'Please Select'] + \common\models\SurveyHescoSubdivision::toArrayList()) ?>
<?= $form->field($model, 'meter_type')->dropDownList([''=>'Please Select','Single-Phase' => 'Single-Phase', '3-Phase' => '3-Phase', 'L.T.TOU' => 'L.T.TOU']) ?>
<?= $form->field($model, 'status')->textInput(['maxlength' => true,'readonly' => true]) ?>
<div class = "form-group">
<a class = "btn btn-default" onclick = "window.history.back()" href = "javascript:;"><i
class = "fa fa-close"></i>
Cancel</a>
<a class = "<?= $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ?>" onclick = "
$('#acceptance-form').submit();" href = "javascript:">
<?= $model->isNewRecord ? 'Create' : 'Update' ?></a>
</div>
<?php ActiveForm::end(); ?>
После нажатия на кнопку Create пользователю предлагается вторая форма, в которой пользователь загружает изображение.
Ниже мой код для контроллера, с которого я пытаюсь его загрузить.
public function actionSetpdf($id)
{
$model = $this->findModel($id);
$m = 0;
$accpt_id = $model->id;
$meter_type = $model->meter_type;
$ogp_sub_div = $model->sub_div;
$images=[];
$ic=0;
$files_uploaded = false;
if (Yii::$app->request->isAjax && Yii::$app->request->post())
{
$data = explode(',',$_POST['data']);
foreach($data as $value)
{
$m = new MeterAcceptanceDetails;
$m -> load(Yii::$app->request->post());
$m->accpt_id = $accpt_id;
$m->meter_type = $meter_type;
$m->created_at = date('Y-m-d H:i:s');
$m->created_by = Yii::$app->user->id;
$m->meter_id = $value;
$m->meter_msn = \common\models\Meters::idTomsn($value);
$m->flag = 1;// 1 means created
$m->ogp_sub_div = $ogp_sub_div;
if ($m->save())
{
// Here the upload image code starts
if ($ic==0)
{
$model->images = UploadedFile::getInstances($model, 'images');
foreach ($model->images as $file)
{
if (file_exists($file->tempName))
{
$img_s = new MeterAcceptanceImages;
$file_name = rand(0, 1000) . time().date('his') . '.' . $file->extension;
$file->saveAs('uploads/meter_acceptance/' . $file_name);
$img_s->file_path = $file_name;
$img_s->accpt_id = $accpt_id;
if ($img_s->save()) {
$images[] = $img_s;
} else {
print_r($img_s->getErrors());
}
}
}
}else{
foreach($images as $image){
$img_s = new MeterAcceptanceImages;
$img_s->file_path = $image->file_path;
$img_s->accpt_id = $accpt_id;
$img_s->save();
}
}
$model->status = MeterAcceptanceHeader::$status_titles[1];
$model->update();
}
else{
$this->renderAjax('viewcreated');
}
}
}
else{
$this->renderAjax('viewcreated');
}
return $this->redirect(Url::toRoute(['meteracceptanceheader/viewsetpdf','id' => $model->id,'model' => $this->findModel($id)]));
}
Просмотр формы 2
<div class = "map-meters-form" id = "doc">
<?php $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
'options' => ['enctype' => 'multipart/form-data']]) ?>
<section class = "content">
<div class = "box">
<div id = "chk" class = "box-body">
<?php Pjax::begin(); ?>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
[
'label'=>'Serial #',
'value' => function($d)
{
return $d->id;
}
],
[
'label' => 'Meter Type',
'value' => function ($d) {
if (is_object($d))
return $d->meter_type;
return ' - ';
},
],
'sub_div',
[
'label' => 'Sub Division Name',
'value' => function ($d) {
if (is_object($d))
return $d->subDiv->name;
return '-';
},
],
[
'label' => 'Prepared By',
'value' => function ($d) {
if (is_object($d))
return $d->prepared->name;
},
],
'prepared_at',
'status',
],
]) ?>
<br>
<div class = "pre-scrollable">
<?= GridView::widget([
'dataProvider' => $dataProvider,
//'ajaxUpdate' => true,
'filterModel' => false,
//'id'=>'gv',
'columns' => [
['class' => 'yii\grid\SerialColumn'],
['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d) {
return ['value' => $d['meter_id']];
}],
'Meter_Serial_Number',
'Meter_Type',
'Sub_Division_Code',
'Sub_Division_Name',
],
]); ?>
</div>
<?php Pjax::end(); ?>
<?= $form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*'])?>
<br>
<form>
<p>
<a href = "<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name = "redirect" class = "btn btn-primary" id = "myid">Submit</a>
<br/>
</p>
</form>
</div>
</div>
</section>
<?php ActiveForm::end(); ?>
</div>
<?php
$url = Url::toRoute(['/meteracceptanceheader/setpdf','id'=>$model->id]);
$script = <<< JS
$(document).ready(function () {
$(document).on('pjax:end', function() {
$("#chk").find("input:checkbox").prop("checked", true);
});
$("#chk").find("input:checkbox").prop("checked", true);
$('#myid').on('click',function(e) {
e.preventDefault();
var strValue = "";
$('input[name = "selection[]"]:checked').each(function() {
if (strValue!= = "")
{
strValue = strValue + " , " + this.value;
}
else
strValue = this.value;
});
$.ajax({
url: '$url',
type: 'POST',
dataType: 'json',
data: {data:strValue},
success: function(data) {
alert(data);
}
});
})
});
JS;
$this->registerJs($script, \yii\web\View::POS_END);
?>
Это позволит выбрать изображение. Теперь, когда я пытаюсь нажать кнопку отправки, я получаю сообщение об ошибке ниже.
PHP Notice 'yii\base\ErrorException' with message 'Undefined offset: 0'
in E:\xampp\htdocs\inventory-web\vendor\yiisoft\yii2\db\Command.php:330
Отлаживая код контроллера, я обнаружил, что ошибка возникает на foreach ($model->images as $file), поскольку print_r($model->images) возвращает пустой Array().
Сделав print_r($model), я получил
common\models\MeterAcceptanceHeader Object ( [_attributes:yii\db\BaseActiveRecord:private] => Array ( [id] => 1 [sub_div] => 37111 [meter_type] => L.T.TOU [prepared_by] => 12 [prepared_at] => 2018-08-20 12:41:27 [updated_at] => [status] => Prepared [updated_by] => [images] => Array ( ) ) [_oldAttributes:yii\db\BaseActiveRecord:private] => Array ( [id] => 1 [sub_div] => 37111 [meter_type] => L.T.TOU [prepared_by] => 12 [prepared_at] => 2018-08-20 12:41:27 [updated_at] => [status] => Prepared [updated_by] => [images] => ) [_related:yii\db\BaseActiveRecord:private] => Array ( ) [_errors:yii\base\Model:private] => [_validators:yii\base\Model:private] => [_scenario:yii\base\Model:private] => default [_events:yii\base\Component:private] => Array ( ) [_behaviors:yii\base\Component:private] => Array ( ) )
Я использовал тот же процесс в других модулях, и он работает правильно.
Как мне избавиться от этой проблемы?
Обновление 1
<?php
use yii\helpers\Html;
use yii\grid\GridView;
use yii\helpers\Url;
use app\models\User;
use yii\widgets\DetailView;
use yii\widgets\ActiveForm;
use yii\widgets\Pjax;
use kartik\select2\Select2;
use kartik\file\FileInput;
/* @var $this yii\web\View */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = $model->id;
$this->title = 'Meter Acceptance Form';
$this->params['breadcrumbs'][] = $this->title;
?>
<section class = "content-header">
<h1>Meter Acceptance</h1>
</section>
<div class = "map-meters-form" id = "doc">
<section class = "content">
<div class = "box">
<div id = "chk" class = "box-body">
<?php Pjax::begin(); ?>
<?=
DetailView::widget([
'model' => $model,
'attributes' => [
[
'label' => 'Serial #',
'value' => function($d){
return $d->id;
}
],
[
'label' => 'Meter Type',
'value' => function ($d){
if ( is_object($d) )
return $d->meter_type;
return ' - ';
},
],
'sub_div',
[
'label' => 'Sub Division Name',
'value' => function ($d){
if ( is_object($d) )
return $d->subDiv->name;
return '-';
},
],
[
'label' => 'Prepared By',
'value' => function ($d){
if ( is_object($d) )
return $d->prepared->name;
},
],
'prepared_at',
'status',
],
])
?>
<br>
<div class = "pre-scrollable">
<?=
GridView::widget([
'dataProvider' => $dataProvider,
//'ajaxUpdate' => true,
'filterModel' => false,
//'id'=>'gv',
'columns' => [
['class' => 'yii\grid\SerialColumn'],
['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d){
return ['value' => $d['meter_id']];
}],
'Meter_Serial_Number',
'Meter_Type',
'Sub_Division_Code',
'Sub_Division_Name',
],
]);
?>
</div>
<?php Pjax::end(); ?>
<?php
$form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
'options' => ['enctype' => 'multipart/form-data']])
?>
<?=$form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>
<br>
<p>
<a href = "<?=URL::toRoute(['meteracceptanceheader/setpdf', 'id' => $model->id]) ?>" name = "redirect" class = "btn btn-primary" id = "myid">Submit</a>
<br/>
</p>
<?php ActiveForm::end(); ?>
</div>
</div>
</section>
</div>
<?php
$url = Url::toRoute(['/meteracceptanceheader/setpdf','id'=>$model->id]);
$script = <<< JS
$(document).ready(function () {
$(document).on('pjax:end', function() {
$("#chk").find("input:checkbox").prop("checked", true);
});
$("#chk").find("input:checkbox").prop("checked", true);
$('#myid').on('click',function(e) {
e.preventDefault();
//START Append form data
var data = new FormData();
var files= $('input[name = "MeterAcceptanceHeader[images][]"]')[0].files;
//append files
$.each(files,function(index,file){
data.append("MeterAcceptanceHeader[images][]",file,file.name);
});
var strValue = "";
$('input[name = "selection[]"]:checked').each(function() {
if (strValue!= = "")
{
strValue = strValue + " , " + this.value;
}
else
strValue = this.value;
});
//alert(strValue);
//append your query string to the form data too
data.append('data',strValue);
//END append form data
$.ajax({
url: '$url',
type: 'POST',
dataType: 'json',
contentType: false,
processData: false,
data: {data:strValue},
success: function(data) {
alert(data);
}
});
})
});
JS;
$this->registerJs($script, \yii\web\View::POS_END);
?>
Любая помощь будет высоко оценен.






Похоже, что вы пытаетесь отправить изображение через вызов Ajax, когда вы нажимаете кнопку отправки на шаге 2 для загрузки изображения, когда вы привязываете click к #myid, который является кнопкой привязки
<a href = "<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name = "redirect" class = "btn btn-primary" id = "myid">Submit</a>
И если вы пытаетесь отправить изображение через ajax, вам необходимо использовать интерфейс FormData.
The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
Но прежде чем я расскажу, как это сделать, вам нужно изучить некоторые другие проблемы, связанные с представлением Form2.
Вы вкладываете 2 формы, что технически неправильно, и вы не можете этого сделать, см. Why.
Прежде всего, почему вы создаете отдельную форму для кнопки отправки?
увидеть эту строку сверху
<?php $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
'options' => ['enctype' => 'multipart/form-data']]) ?>
Здесь начинается ваша первая форма и поле ввода файла
<?= $form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*'])?>
находится внутри этой формы, и в самой следующей строке у вас есть кнопка привязки, о которой я упоминал выше в начале, но вы оборачиваете ее в отдельную форму, и это тоже, прежде чем вы закроете ActiveForm
<form>
<p>
<a href = "<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name = "redirect" class = "btn btn-primary" id = "myid">Submit</a>
<br/>
</p>
</form>
вместо этого вы закрываете ActiveForm после этой формы, вызывая <?php ActiveForm::end(); ?>. Как вы видели в предыдущем вопрос, вы столкнулись со странным поведением входов фильтра для GridVew, когда вы обертывали GridView внутри формы, и вы повторяете ту же ошибку и здесь, а также встраиваете в нее 2 формы.
Что я вам посоветую сделать в первую очередь
ActiveForm. И переместите ActiveForm::begin() непосредственно перед fileInput() и после Pjax::end().С учетом сказанного теперь вы должны использовать FormData для загрузки изображения через ajax, и для этого вам нужно добавить эти параметры contentType: false и processData: false в свой вызов ajax и использовать FormData.append() для добавления входных файлов в FormData.
Итак, ваш javascript для функции щелчка будет выглядеть так, я предполагаю, что модель, используемая для загрузки изображения, - MeterAcceptanceImages.
$('#myid').on('click',function(e) {
event.preventDefault();
//START Append form data
let data = new FormData();
let files= $("input[name='MeterAcceptanceImages[images][]']")[0].files;
//append files
$.each(files,function(index,file){
data.append('MeterAcceptanceImages[images][]',file,file.name);
});
var strValue = "";
$('input[name = "selection[]"]:checked').each(function() {
if (strValue!= = ""){
strValue = strValue + " , " + this.value;
}else{
strValue = this.value;
}
});
//append your query string to the form data too
data.append('data',strValue);
//END append form data
$.ajax({
url: '$url',
type: 'POST',
dataType: 'json',
contentType: false,
processData: false,
data: data,
success: function(data) {
alert(data);
}
});
});
Итак, в целом ваш вид Form2.php должен выглядеть так, как показано ниже.
<div class = "map-meters-form" id = "doc">
<section class = "content">
<div class = "box">
<div id = "chk" class = "box-body">
<?php Pjax::begin(); ?>
<?=
DetailView::widget([
'model' => $model,
'attributes' => [
[
'label' => 'Serial #',
'value' => function($d){
return $d->id;
}
],
[
'label' => 'Meter Type',
'value' => function ($d){
if ( is_object($d) )
return $d->meter_type;
return ' - ';
},
],
'sub_div',
[
'label' => 'Sub Division Name',
'value' => function ($d){
if ( is_object($d) )
return $d->subDiv->name;
return '-';
},
],
[
'label' => 'Prepared By',
'value' => function ($d){
if ( is_object($d) )
return $d->prepared->name;
},
],
'prepared_at',
'status',
],
])
?>
<br>
<div class = "pre-scrollable">
<?=
GridView::widget([
'dataProvider' => $dataProvider,
//'ajaxUpdate' => true,
'filterModel' => false,
//'id'=>'gv',
'columns' => [
['class' => 'yii\grid\SerialColumn'],
['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d){
return ['value' => $d['meter_id']];
}],
'Meter_Serial_Number',
'Meter_Type',
'Sub_Division_Code',
'Sub_Division_Name',
],
]);
?>
</div>
<?php Pjax::end(); ?>
<?php
$form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
'options' => ['enctype' => 'multipart/form-data']])
?>
<?=$form->field($model, 'images[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>
<br>
<p>
<a href = "<?=URL::toRoute(['meteracceptanceheader/setpdf', 'id' => $model->id]) ?>" name = "redirect" class = "btn btn-primary" id = "myid">Submit</a>
<br/>
</p>
<?php ActiveForm::end(); ?>
</div>
</div>
</section>
</div>
<?php
$url = Url::toRoute(['/meteracceptanceheader/setpdf', 'id' => $model->id]);
$script = <<< JS
$(document).ready(function () {
$(document).on('pjax:end', function() {
$("#chk").find("input:checkbox").prop("checked", true);
});
$("#chk").find("input:checkbox").prop("checked", true);
$('#myid').on('click',function(e) {
event.preventDefault();
//START Append form data
let data = new FormData();
let files= $("input[name='MeterAcceptanceImages[images][]']")[0].files;
//append files
$.each(files,function(index,file){
data.append('MeterAcceptanceImages[images][]',file,file.name);
});
var strValue = "";
$('input[name = "selection[]"]:checked').each(function() {
if (strValue!= = ""){
strValue = strValue + " , " + this.value;
}else{
strValue = this.value;
}
});
//append your query string to the form data too
data.append('data',strValue);
//END append form data
$.ajax({
url: '$url',
type: 'POST',
dataType: 'json',
contentType: false,
processData: false,
data: data,
success: function(data) {
alert(data);
}
});
});
});
JS;
$this->registerJs($script, \yii\web\View::POS_END);
?>
Теперь, если вы попытаетесь, print_r(UploadedFile::getInstances('images')) должен показать вам все изображения, которые вы выбрали и отправили для загрузки. Чтобы устранить неполадки в случае ошибок при загрузке вызова ajax, вы можете увидеть мой отвечать, который я опубликовал ранее, связанный с загрузкой файлов ajax.
let не поддерживается
Я использовал var data = new FormData(); var files= $('input[name = "MeterAcceptanceHeader[images][]"]')[0].files;
И применил предложенные вами изменения. Но при отправке я получаю PHP Notice 'yii\base\ErrorException' with message 'Undefined variable: model' in E:\xampp\htdocs\inventory-web\backend\views\meteracceptanceheader\viewcreated.php:18
Строка № 18 - $this->title = $model->id;.
находится ли строка выше $this->title = $model->id; внутри вида, я так не думаю? а что значит let не поддерживается? Я протестировал решение, и оно работает хорошо, не могли бы вы подтвердить то, о чем просили?
Определения Let не поддерживаются текущей версией JavaScript. Я получаю эту ошибку
@MrFaisal, какой браузер вы используете, который выдает исключение для оператора let, или это редактор, который показывает ошибку. и ошибка для строки $this->title = $model->id; не имеет смысла, поскольку вы переопределяете заголовок в следующей строке $this->title = 'Meter Acceptance Form';, чтобы вы могли удалить строку $this->title = $model->id; из поля зрения.
Я обновил свой JS, теперь let работает. Кроме того, если я удалю $this->title = $model->id, я снова получу ту же ошибку внутри gridview =>, которая находится в строке 'model' => $model,.
@MrFaisal, потому что вам нужно передать $model из controller/action в представление, это просто базовая вещь, поскольку вы увлечены $model виджетом DetailView
Я его уже прошел, и раньше он работал. Но после добавления contentType: false, processData: false, он дает мне эту ошибку
Позвольте нам продолжить обсуждение в чате.
добавил ответ, посмотрим, поможет ли это