我正在研究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表
MeterAcceptanceImages表
有一个form1
,提示用户从下拉菜单中进行选择。
Form1视图
<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)]));
}
Form2视图
<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通知'yii \ base \ ErrorException'并显示消息'Undefined offset:0'
在 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);
?>
任何帮助将不胜感激。
答案 0 :(得分:8)
当您将click
绑定到#myid
到<a href="<?= URL::toRoute(['meteracceptanceheader/setpdf', 'id'=>$model->id])?>" name="redirect" class="btn btn-primary" id="myid">Submit</a>
上时,当您在步骤2中单击“提交”按钮以上传图像时,您试图通过Ajax调用提交图像。定位按钮
FormData
如果您尝试通过ajax发送图像,则需要使用Form2
界面。
FormData接口提供了一种轻松构造一组 表示表单字段及其值的键/值对,可以 然后可以使用XMLHttpRequest.send()方法轻松发送。它用 如果将编码类型设置为,则表单将使用相同的格式 “ multipart / form-data”。
但是在我介绍如何做之前,您需要研究与<?php $form = ActiveForm::begin(['id' => 'map-form', 'enableClientValidation' => true, 'enableAjaxValidation' => false,
'options' => ['enctype' => 'multipart/form-data']]) ?>
视图相关的其他问题。
您正在嵌套这两种形式,这在技术上是错误的,您无法这样做,请参见Why
。
首先,为什么要为“提交”按钮创建单独的表单?
请首先查看此行
<?= $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
。正如您在上一个question中所见,在将GridView
包装在表单内时,您遇到了ActiveForm
的过滤器输入的怪异行为,并且在这里重复了同样的错误并在其中嵌套2种形式。
我将建议您先做什么
ActiveForm::begin()
中即可。然后将fileInput()
移动到Pjax::end()
之前和FormData
之后。话虽如此,您现在应该使用contentType: false
来通过ajax上传图像,然后您必须在ajax调用中添加这些选项processData: false
和FormData.append()
并使用{{ 1}},将输入文件附加到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调用时出错时进行故障排除,您可以查看我之前发布的answer与ajax文件上传相关的内容。