我正在使用yii2-formwizard小部件开发应用。我已经使它在所有其他部分都能正常工作,甚至可以完美地提交单个图像。当我尝试提交多个图像时,问题就来了。我得到的$_POST
数组具有表单模型值,图像模型则作为$_POST
内的空数组,但是随后所有图像模型值都包装在$_FILES
数组中。我以前从未处理过该如何解决呢?以下是相关代码:
_form视图
<?php
use yii\helpers\Html;
use kartik\widgets\ActiveForm;
use kartik\builder\Form;
use kartik\datecontrol\DateControl;
use kartik\widgets\Select2;
use buttflattery\formwizard\FormWizard;
use yii\helpers\ArrayHelper;
use kartik\file\FileInput;
/**
* @var yii\web\View $this
* @var common\models\Listing $model
* @var yii\widgets\ActiveForm $form
*/
?>
<?php
echo FormWizard::widget([
'theme' => FormWizard::THEME_MATERIAL,
'labelFinish' => 'Submit',
// 'formOptions'=>[
// 'options'=>['enctype'=>'multipart/form-data'],
// ],
'steps' => [
[
'model'=>$listingModel,
'title'=>'Basic',
'description'=>'Give us the details of the list',
'formInfoText'=>'Fill all required fields',
'fieldConfig' => [
'created_by' => false, //hide a specific field
'updated_at' => false, //hide a specific field
'created_at' => false, //hide a specific field
'expires_on' => false, //hide a specific field
'status' => false, //hide a specific field
'listing_type_id' => false, //hide a specific field
'latitude' => false, //hide a specific field
'longitude' => false, //hide a specific field
'listing_title' => ['type' => Form::INPUT_TEXT, 'options' => ['placeholder' => 'Enter Listing Title...', 'maxlength' => 50]],
'country' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\Country::find()->all(), 'country_id', 'country_name'),
'options' => [
'prompt'=>'Select Country',
'onchange'=>'
$.post( "'.Yii::$app->urlManager->createUrl('listing/select-state?id=').'"+$(this).val(), function( data ) {
$( "select#state_id" ).html( data );
});
'
]
]
],
'states' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\States::find()->all(), 'state_id', 'state_name'),
'options' => [
'id'=>'state_id',
'prompt'=>'Select Select the Country First',
'onchange'=>'
$.post( "'.Yii::$app->urlManager->createUrl('listing/select-area?id=').'"+$(this).val(), function( data ) {
$( "select#area_code" ).html( data );
});
'
]
]
],
'area_id' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\Areas::find()->all(), 'area_id', 'area_name'),
'options' => [
'prompt'=>'Please Select the State/Region First',
'id' => 'area_code'
],
]
],
'physical_address' => ['type' => Form::INPUT_TEXT, 'options' => ['placeholder' => 'Enter The Actual Physical Address...', 'maxlength' => 50]],
'neighborhood' => ['type' => Form::INPUT_TEXT, 'options' => ['placeholder' => 'Enter The Nearby Landmark or Neighbourhood...', 'maxlength' => 50]],
'address' => [
'widget' => \kalyabin\maplocation\SelectMapLocationWidget::className(),
'options' => [
'attributeLatitude' => 'latitude',
'attributeLongitude' => 'longitude',
'googleMapApiKey' => 'AIzaSyDU30XgKi1ik7wpWteHUENKVH_d09sTqRg',
'draggable' => true,
],
]
]
],
[
'model'=>$model,
'title'=>'Prices and More',
'description'=>'Give us the details of the list',
'formInfoText'=>'Fill all required fields',
'fieldConfig' => [
// 'only' => ['property_category', 'sub_category_id', 'available_from', 'desc', 'price', 'currency_id', 'price_conditions', 'deposit', 'agent_commission', 'other_payments'],
'only' => ['property_category', 'sub_category_id', 'price', 'currency_id'],
'property_category' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\PropertyCategory::find()->all(), 'category_id', 'category_name'),
'options' => [
'id'=>'state_id',
'prompt'=>'Select Property Category',
'onchange'=>'
$.post( "'.Yii::$app->urlManager->createUrl('listing/select-property-category?id=').'"+$(this).val(), function( data ) {
$( "select#sub_category_value" ).html( data );
});
'
]
]
],
'sub_category_id' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\PropertySubCategory::find()->all(), 'sub_category_id', 'name'),
'options' => [
'prompt'=>'Please Select the Property Category First',
'id' => 'sub_category_value'
],
]
],
'currency_id' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\Currency::find()->all(), 'currency_id', 'title'),
]
],
'price' => ['type' => Form::INPUT_TEXT, 'options' => ['placeholder' => 'Enter Price...']],
]
],
[
'model'=>$model,
'title'=>'Features',
'description'=>'Give us the details of the list',
'formInfoText'=>'Fill all required fields',
'fieldConfig' => [
// 'only' => ['beds', 'baths', 'rooms', 'living_area', 'living_size', 'floor', 'total_floors', 'build_year', 'car_spaces', 'fully_furnished', 'property_features'],
'only' => ['property_features'],
'property_features' => [
'widget' => Select2::class, //widget class name
'options' => [
'data' => ArrayHelper::map(common\models\PropertyFeatures::find()->all(), 'feature_id', 'feature_name', 'featuresType.type_name'),
'options' => ['multiple' => true, 'placeholder' => 'Select Property Features ...']
]
],
]
],
[
'model'=>$imageModel,
'title'=>'Images',
'description'=>'Give us the details of the list',
'formInfoText'=>'Fill all required fields',
'fieldConfig' => [
'only' => ['image'],
'image' => [
'multifield'=>true,
'widget' => FileInput::classname(),
'options' =>[
'options' => [
'multiple' => true,
'accept' => 'image/*',
'pluginOptions' => [
'showCaption' => false,
'showRemove' => false,
'showUpload' => false,
'browseClass' => 'btn btn-primary btn-block',
'browseIcon' => '<i class="glyphicon glyphicon-camera"></i> ',
'browseLabel' => 'Attach Listing Images',
'allowedFileExtensions' => ['jpg','gif','png'],
'overwriteInitial' => false
],
],
],
]
]
],
]
]);
?>
控制器动作
/**
* Creates a new Property model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionProperty()
{
$listingModel = new Listing;
$imageModel = new ListingImages;
$model = new Property;
if ($listingModel->load(Yii::$app->request->post()) && $imageModel->load(Yii::$app->request->post()) && $model->load(Yii::$app->request->post())) {
$transaction = Yii::$app->db->beginTransaction();
try {
$listingModel->listing_type_id = $listingModel->listingType('Property');
$listingModel->created_by = Yii::$app->user->id;
if ($flag = $listingModel->save(false)) {
$model->listing_id = $listingModel->listing_id;
$model->physical_address = $listingModel->physical_address;
$model->neighborhood = $listingModel->neighborhood;
$model->area_id = $listingModel->area_code;
$model->address = $listingModel->physical_address;
$model->latitude = $listingModel->latitude;
$model->longitude = $listingModel->longitude;
$model->created_by = Yii::$app->user->id;
$flag = $model->save(false);
foreach ($_FILES['ListingImages']['name']['image'] as $key => $image) {
$image = $imageModel->uploadImage();
$imageModel->created_by = Yii::$app->user->id;
$imageModel->listing_id = $listingModel->listing_id;
$imageModel->active = 'Y';
if ($flag = $imageModel->save()) {
if ($image !== false) {
$path = $imageModel->getImageFile();
$image->saveAs($path);
}
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $listingModel->listing_id]);
} else {
$transaction->rollBack();
}
} catch (Exception $e) {
$transaction->rollBack();
}
} else {
return $this->render('create', [
'listingModel' => $listingModel, 'model' => $model, 'form' => '_property', 'imageModel' => $imageModel,
]);
}
}
ListingImages模型
<?php
namespace common\models;
use Yii;
use yii\web\UploadedFile;
use yii\helpers\FileHelper;
/**
* This is the model class for table "listing_images".
*
* @property int $image_id
* @property int $listing_id
* @property string $image_url_link generated filename on server
* @property string $updated_at
* @property string $created_at
* @property int $created_by
* @property string $active
* @property string $filename source filename from client
*
* @property Listing $listing
*/
class ListingImages extends \yii\db\ActiveRecord
{
const PERMISSIONS_PRIVATE = 10;
const PERMISSIONS_PUBLIC = 20;
public $filename;
public $image;
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'listing_images';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['listing_id', 'image_url_link', 'created_by', 'active'], 'required'],
[['listing_id', 'created_by'], 'integer'],
[['updated_at', 'created_at', 'filename'], 'safe'],
[['active'], 'string'],
[['image_url_link'], 'string', 'max' => 80],
[['listing_id'], 'exist', 'skipOnError' => true, 'targetClass' => Listing::className(), 'targetAttribute' => ['listing_id' => 'listing_id']],
// [['image'], 'file', 'extensions'=>'jpg, gif, png'],
// [['image'], 'file', 'maxSize'=>'2048000'],
// [['image'], 'file','maxFiles' => 30],
[['image'], 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 2048000, 'maxFiles' => 30],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'image_id' => Yii::t('app', 'Image ID'),
'listing_id' => Yii::t('app', 'Listing ID'),
'image_url_link' => Yii::t('app', 'Listing Image'),
'updated_at' => Yii::t('app', 'Updated At'),
'created_at' => Yii::t('app', 'Created At'),
'created_by' => Yii::t('app', 'Created By'),
'active' => Yii::t('app', 'Active'),
];
}
/**
* @return \yii\db\ActiveQuery
*/
public function getListing()
{
return $this->hasOne(Listing::className(), ['listing_id' => 'listing_id']);
}
/**
* fetch stored image file name with complete path
* @return string
*/
public function getImageFile()
{
$directory = Yii::$app->params['uploadPath'];
if (!is_dir($directory)) {
FileHelper::createDirectory($directory);
}
return isset($this->image_url_link) ? $directory . '/' . $this->image_url_link : null;
}
/**
* fetch stored image url
* @return string
*/
public function getImageUrl()
{
$directory = Yii::$app->params['uploadUrl'];
// return a default image placeholder if your source image_url_link is not found
$image_url_link = isset($this->image_url_link) ? $this->image_url_link : 'default_user.jpg';
return $directory . $image_url_link;
}
/**
* Process upload of image
*
* @return mixed the uploaded image instance
*/
public function uploadImage() {
// get the uploaded file instance. for multiple file uploads
// the following data will return an array (you may need to use
// getInstances method)
$image = UploadedFile::getInstance($this, 'image');
// if no image was uploaded abort the upload
if (empty($image)) {
return false;
}
// store the source file name
$tmp = explode(".", $image->name);
$ext = end($tmp);
// generate a unique file name
$this->image_url_link = Yii::$app->security->generateRandomString().".{$ext}";
// the uploaded image instance
return $image;
}
/**
* Process deletion of image
*
* @return boolean the status of deletion
*/
public function deleteImage() {
$file = $this->getImageFile();
// check if file exists on server
if (empty($file) || !file_exists($file)) {
return false;
}
// check if uploaded file can be deleted on server
if (!unlink($file)) {
return false;
}
// if deletion successful, reset your file attributes
$this->image_url_link = null;
$this->filename = null;
return true;
}
}
请注意,代码的另一部分工作正常。我只扭曲了一些部分以获得满意的结果。这是通过表单提交的数据。真正令我困惑的部分是该代码末尾的$ _FILES数组中包含了所有已上传文件的信息。
$_POST = [
'_csrf-backend' => '_ioSvkoWdYDTEG_L4AHgnyQSEe7pZOqEWwQfPANPbM2uGkbIKHo8y5ZAGriqT4XsQCBkl5sOnvweT31wUx01kg==',
'Listing' => [
'listing_title' => 'tyguhijokpl[',
'country' => '2',
'states' => '4',
'area_id' => '1537',
'physical_address' => 'yghbunjikmol,',
'neighborhood' => 'gvybhunjimko,l',
'address' => 'Dandora phase 4, Nairobi, Kenya',
'latitude' => '-1.2423923',
'longitude' => '36.90438449999999',
],
'Property' => [
'property_category' => '1',
'sub_category_id' => '2',
'currency_id' => '1',
'price' => '897465123',
'property_features' => [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
],
],
'ListingImages' => [
'image' => [
'',
],
],
];
$_FILES = [
'ListingImages' => [
'name' => [
'image' => [
'coins-1015125_1920.jpg',
'computer-768608_1920.jpg',
'content-is-king-1132259_1920.jpg',
'content-marketing.jpg',
'contentpyramid.png',
'cup-of-coffee-1280537_1920.jpg',
'ecommerce-3546296_1920.jpg',
'email-3249062_1280.png',
],
],
'type' => [
'image' => [
'image/jpeg',
'image/jpeg',
'image/jpeg',
'image/jpeg',
'image/png',
'image/jpeg',
'image/jpeg',
'image/png',
],
],
'tmp_name' => [
'image' => [
'C:\\xampp\\tmp\\php8AF1.tmp',
'C:\\xampp\\tmp\\php8B02.tmp',
'C:\\xampp\\tmp\\php8B03.tmp',
'C:\\xampp\\tmp\\php8B23.tmp',
'C:\\xampp\\tmp\\php8B34.tmp',
'C:\\xampp\\tmp\\php8B35.tmp',
'C:\\xampp\\tmp\\php8B46.tmp',
'C:\\xampp\\tmp\\php8B47.tmp',
],
],
'error' => [
'image' => [
0,
0,
0,
0,
0,
0,
0,
0,
],
],
'size' => [
'image' => [
364796,
275881,
310313,
301511,
119458,
508911,
219479,
242737,
],
],
],
];
答案 0 :(得分:1)
几个月前,我创建了此扩展程序。您提交的文件将位于$_FILES
数组中,而不是$_POST
中,您需要调用UploadedFile::getInstances('image')
来获取所有选择上传的图像,然后对其进行迭代以上传,您可以访问每个图像here列出的所有属性。
并且每次在foreach ($_FILES['ListingImages']['name']['image'] as $key => $image) {
内插入图像时都不会创建新对象,该图像仅显示最后插入的图像,$imageModel
在操作开始时被初始化,而您也应该在foreach
内
$imageInstances=UploadedFile::getInstances('image');
foreach ($imageInstances as $instance) {
$imageModel=new ListingImages();
$image = $imageModel->uploadImage($instance);
$imageModel->created_by = Yii::$app->user->id;
$imageModel->listing_id = $listingModel->listing_id;
$imageModel->active = 'Y';
if ($flag = $imageModel->save()) {
if ($image !== false) {
$path = $imageModel->getImageFile();
$image->saveAs($path);
}
}
}
在您的内部uploadImage()
使用此实例来访问图像的name
,type
,size
和extension
以分配给特定字段< / p>
/**
* Process upload of image
*
* @return mixed the uploaded image instance
*/
public function uploadImage($image) {
// get the uploaded file instance. for multiple file uploads
// the following data will return an array (you may need to use
// getInstances method)
// store the source file name
$tmp = explode(".", $image->name);
$ext = end($tmp);
// generate a unique file name
$this->image_url_link = Yii::$app->security->generateRandomString().".{$ext}";
// the uploaded image instance
return $image;
}