This is an old revision of the document!
Yii 2 File Upload
Example CSV File Upload
Model
Create DataUploadForm
model:
<?php namespace app\models; use yii\base\Model; use yii\web\UploadedFile; class DataUploadForm extends Model { const OVERWRITE_NO = 0; const OVERWRITE_YES = 1; const IMPORT_METHOD_UPDATE_INSERT = 0; // Update existing data (update, or insert if not exist) const IMPORT_METHOD_UPDATE = 1; // Update existing data (update only, no insert if not exist) const IMPORT_METHOD_APPEND_ALL = 2; // Append all to existing data (insert only, but all) const IMPORT_METHOD_APPEND_NEW = 3; // Append only new to existing data (insert only, but only new) const IMPORT_METHOD_REPLACE = 4; // Replace existing data (delete all, then insert) public static $importMethods = [ DataUploadForm::IMPORT_METHOD_UPDATE_INSERT => 'Update existing / Insert new content', DataUploadForm::IMPORT_METHOD_UPDATE => 'Update only existing content', DataUploadForm::IMPORT_METHOD_APPEND_ALL => 'Append all content', DataUploadForm::IMPORT_METHOD_APPEND_NEW => 'Append only new content', DataUploadForm::IMPORT_METHOD_REPLACE => 'Replace content', ]; /** * @var UploadedFile */ public $dataFile; public $overwriteExistingContent = self::OVERWRITE_NO; public $importMethod = self::IMPORT_METHOD_UPDATE_INSERT; public function rules() { return [ // see '@vendor/yiisoft/yii2/validators/FileValidator.php' for all options [['dataFile'], 'file', 'skipOnEmpty' => false, 'maxFiles' => 1, // Extensions: For CSV files, use uppercase CSV extension in actual file, otherwise it fails. 'extensions' => 'txt, CSV, xls', // MimeTypes: must match file type (extension) to be uploaded. Eg: txt, csv, png, csv (in excel). //'mimeTypes' => "text/plain, text/CSV, image/png, application/vnd.ms-excel", //'mimeTypes' => "*", // any MIME type 'checkExtensionByMimeType' => false, // false to stop verifying file extension to MIME types 'wrongExtension' => '{attribute} = [{file}]. Only these extensions are allowed: {extensions}.' ], ]; } public function upload() { if ($this->validate()) { $this->dataFile->saveAs('uploads/' . strtolower($this->dataFile->baseName) . '.' . strtolower($this->dataFile->extension) ); // save to [app]/web/uploads return true; } else { return false; } } //public function attributeLabels() //{ // return ['file' => Yii:t('app', 'Archivo')]; // es translation //} } ?>
Controller
Add these actions to your controller. Eg: [app]/controllers/ItemController
:
namespace app\controllers; use Yii; use app\models\Item; use app\models\ItemSearch; use app\models\DataUploadForm; use yii\web\Response; use yii\web\Request; use yii\web\UploadedFile; use yii\filters\AccessControl; use yii\filters\VerbFilter; //... defined('DS') or define('DS', DIRECTORY_SEPARATOR); // set const if not defined class ItemController extends Controller { public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'actions' => [ 'index', 'create', 'view', 'update', 'delete', 'report', 'download-sample', 'batch-create', ], 'allow' => true, 'roles' => ['@'], // @ = Authenticated users ], //[ // 'allow' => true, // 'actions' => ['index', 'view'], // 'roles' => ['?'], // ? = Guest user //], ], ], //... ]; } //... /** * Creates a downloadable sample CSV file to be used in batch-create. * It return processing messages. * @return mixed */ public function actionDownloadSample($file_name = 'file.csv') { $data = [ ["code", "description", "quantity"], ["AHI-POUCH", "Audina Navy Blue Pouch", "800"], ["NOLOGO-SDELIVERYCASE", "Audina S-Delivery White Case No Logo", "96"], ]; $this->actionOutputCSV($data, $file_name); } // Usage: // actionOutputCSV(array( // array("Volvo", "BMW", "Toyota"), // array("Volvo 1", "BMW 1", "Toyota 1"), // array("Volvo 2", "BMW 2", "Toyota 2"), // array("Volvo 3", "BMW 3", "Toyota 3"), // ),'download.csv'); // Source: See more at: https://arjunphp.com/create-download-csv-files-php/#sthash.f6JCTy4J.dpuf function actionOutputCSV($data, $fileName = 'file.csv', $is_csv_excel=false) { # output headers so that the file is downloaded rather than displayed header("Content-Type: text/csv"); header("Content-Disposition: attachment; filename=$fileName"); # Disable caching - HTTP 1.1 header("Cache-Control: no-cache, no-store, must-revalidate"); # Disable caching - HTTP 1.0 header("Pragma: no-cache"); # Disable caching - Proxies header("Expires: 0"); # Start the ouput $output = fopen("php://output", "w"); if ($is_csv_excel) { fwrite($output, "sep=\t"); // set default separator to TAB, so Excel can import CSV as tab-separated } # Then loop through the rows foreach ($data as $row) { # Add the rows to the body //fputcsv($output, $row); // here you can change delimiter/enclosure fputcsv($output, $row, "\t"); // here you can change delimiter/enclosure } # Close the stream off fclose($output); } /** * Creates Item models in batch. * If create is successful, the browser will be redirected to the 'index' page. * @return mixed */ public function actionBatchCreate() { $model = new DataUploadForm(); if (Yii::$app->request->isPost) { $model->dataFile = UploadedFile::getInstance($model, 'dataFile'); $model->overwriteExistingContent = ( isset($_POST['DataUploadForm']['overwriteExistingContent']) ? $_POST['DataUploadForm']['overwriteExistingContent'] : DataUploadForm::OVERWRITE_NO ); $model->importMethod = ( isset($_POST['DataUploadForm']['importMethod']) ? $_POST['DataUploadForm']['importMethod'] : DataUploadForm::IMPORT_METHOD_APPEND ); if ($model->upload()) { // file is uploaded successfully Yii::$app->session->setFlash('success', "File {$model->dataFile} was uploaded successfully."); // clear table if ($model->overwriteExistingContent) { Item::deleteAll(); } // import data $resultsImport = $this->importDataFromCSV($model->dataFile, $model->importMethod); if ($resultsImport) { $panelCollapsed = ' <button class="btn btn-primary" type="button" '; $panelCollapsed .= ' data-toggle="collapse" '; $panelCollapsed .= ' data-target="#collapseExample" '; $panelCollapsed .= ' aria-expanded="false" '; $panelCollapsed .= ' aria-controls="collapseExample"> View Import Results </button>'; $panelCollapsed .= '<div class="collapse" id="collapseExample">'; $panelCollapsed .= ' <div class="well">' . $resultsImport . '</div>'; $panelCollapsed .= '</div>'; Yii::$app->session->setFlash('success', "Item data was imported successfully. " . $panelCollapsed ); } else { Yii::$app->session->setFlash('error', "Item data failed to be imported. "); } return $this->redirect(['index']); } else { Yii::$app->session->setFlash('error', "File {$model->dataFile} failed to be uploaded."); } } return $this->render('batch-create', ['model' => $model]); } /** * Updates an existing Item model. * If update is successful, the browser will be redirected to the 'index' page. * @return mixed */ public function actionBatchUpdate() { // // Retrieve items to be updated in a batch mode, // // assuming each item is of model class 'Item' // $items=$this->getItemItemsToUpdate(); // if (Model::loadMultiple($items, Yii::$app->request->post()) && Model::validateMultiple($items)) { // $count = 0; // foreach ($items as $item) { // // populate and save records for each model // if ($item->save()) { // // do something here after saving // $count++; // } // } // Yii::$app->session->setFlash('success', "Processed {$count} records successfully."); // return $this->redirect(['index']); // redirect to your next desired page // } else { // return $this->render('update', [ // 'order-items' => $items, // ]); // } } /** * Imports a CSV file to be used in batch-create. * It return processing messages. * @return mixed */ private function importDataFromCSV($fileName, $importMethod=DataUploadForm::IMPORT_METHOD_UPDATE_INSERT) { $output = "<pre>\n"; $output .= "Processing data\n"; // Preprocess based on Import Method switch($importMethod) { case DataUploadForm::IMPORT_METHOD_APPEND_ALL: // Append content (insert all) case DataUploadForm::IMPORT_METHOD_APPEND_NEW: // Append only new to existing data (insert only, but only new) case DataUploadForm::IMPORT_METHOD_UPDATE_INSERT: // Update content (update, or insert if new) case DataUploadForm::IMPORT_METHOD_UPDATE: // Update content (update only, no insert if new) default: break; case DataUploadForm::IMPORT_METHOD_REPLACE: // Replace existing data (delete all, then insert) Item::deleteAll(); break; } // open the source data file //$dataFilename = Yii::$app->basePath . DS . 'commands' . DS . 'data' . DS . 'Item-data.csv'; $dataFilename = Yii::$app->basePath . DS . 'web' . DS . 'uploads' . DS . strtolower($fileName); if (!file_exists($dataFilename)) { //echo "Missing data file: [" . $dataFilename . "]\n"; Yii::$app->session->setFlash('error', "Missing data file: [{$dataFilename}]"); return false; //return "Missing data file: [" . $dataFilename . "]\n"; } $rowCnt = 0; $dataFile = fopen($dataFilename, "r") or die("Unable to open file!"); while(!feof($dataFile)) { // grab one line from file $data = fgets($dataFile); // parse data $row_fields = explode("\t", $data); // Skip header if ((trim($row_fields[0]) == '') || (trim($row_fields[0]) == 'sep=') || (trim($row_fields[0]) == 'code') || (trim($row_fields[0]) == '------') ) { $output .= "### Skipped: {$data}. \n"; continue; // skip this header row } // Get row data if (count($row_fields)>0) { $code = trim(trim($row_fields[0]), '"'); $description = trim(trim($row_fields[1]), '"'); $quantity = trim(trim($row_fields[2]), '"'); } else { break; // stop processing rows } switch($importMethod) { case DataUploadForm::IMPORT_METHOD_APPEND_ALL: // Append content (insert all) case DataUploadForm::IMPORT_METHOD_UPDATE_INSERT: // Update content (update, or insert if new) default: if (($model = Item::findOne(['code'=>$code])) === null) { $model = new Item(); // if existing row not found, we add as new record. } break; case DataUploadForm::IMPORT_METHOD_APPEND_NEW: // Append only new to existing data (insert only, but only new) if (($model = Item::findOne(['code'=>$code])) !== null) { $processThisRow = false; // Skip this existing row. We only want to insert new records. } else { $model = new Item(); } break; case DataUploadForm::IMPORT_METHOD_UPDATE: // Update content (update only, no insert if new) if (($model = Item::findOne(['code'=>$code])) === null) { $processThisRow = false; // Skip this new row. We only want to update existing records. } break; case DataUploadForm::IMPORT_METHOD_REPLACE: // Replace existing data (delete all, then insert) $model = new Item(); // we only add as new record (any previous records were deleted at beginning of this function) break; } // insert data if ($processThisRow) { $model->code = $code; $model->description = $description; $model->quantity = $quantity; } $rowCnt++; // for printing, it is always one ahead if ($model->save()) { $entry_id = $model->id; $output .= "{$rowCnt}. ### Inserted Item [{$entry_id}] ###\n"; $row_fieldsData = "[".implode(',', [ $code, $description, $quantity ])."]"; $output .= "{$rowCnt}. Insert Item {$row_fieldsData}\n"; } else { $row_fieldsData = "[".implode(',', [ $code, $description, $quantity ])."]"; $output .= "{$rowCnt}. Failed to insert Item {$row_fieldsData}. " . print_r($model->getErrors(), true) . "\n"; } } $output .= "</pre>\n"; fclose($dataFile); return $output; } }
View
Add a button to do a Batch Upload. Update view index
under views. Eg: [app]/views/items/index
<?php //... <?= Html::a(Yii::t('app', 'Batch Upload'), ['batch-create'], ['class' => 'btn btn-success']) ?> //... ?>
Create view batch-create
under views. Eg: [app]/views/items/batch-create
:
<?php use yii\helpers\Html; use yii\helpers\Url; use yii\widgets\ActiveForm; use yii\helpers\ArrayHelper; $this->title = Yii::t('app', 'Batch Upload'); $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Item'), 'url' => ['index']]; //$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = Yii::t('app', 'Batch Upload'); ?> <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> <h2>Item (Batch Upload)</h2> <div class="col-lg-12"> <?php if(Yii::$app->session->hasFlash('success')): ?> <div class="alert alert-success" role="alert"> <?= Yii::$app->session->getFlash('success'); ?> </div> <?php endif; ?> <?php if(Yii::$app->session->hasFlash('error')): ?> <div class="alert alert-danger" role="alert"> <?= Yii::$app->session->getFlash('error'); ?> </div> <?php endif; ?> </div> <div class="alert alert-info" role="alert"> <h4>Data File</h4> Create a CSV (<kbd>tab</kbd>-delimited) file with the following format. Eg: <table class="table table-bordered table-striped table-hover table-condensed" border="1"> <tr> <td>code</td><td>description</td><td>quantity</td> </tr> <tr class="warning"> <td>ACME-POUCH</td><td>Branded Pouch</td><td>800</td> </tr> <tr> <td>NOLOGO-POUCH</td><td>Unbranded Pouch</td><td>96</td> </tr> </table> <?= Html::a('<i class="glyphicon glyphicon-download d-icon"></i>' . ' Download Sample ', ['item/download-sample', 'file_name'=>'sample.csv'], ['class'=>'btn btn-default']) ?> </div> <?= $form->field($model, 'dataFile')->fileInput(['class'=>'form-control']) ?> <?= $form->field($model, 'overwriteExistingContent')->dropDownList( ['0'=>'No', '1'=>'Yes'] ) ?> <button class='btn btn-success'>Submit</button> <?php ActiveForm::end() ?>
Web
Create an uploads
directory in web folders. Eg: [app]/web/uploads
Example Image Upload and Save Filename in DB
Controller
Add action actionCreate()
to model (here it is model organization
), and override afterSave()
to store image file to server, and its path in the database:
public function actionCreate() { $model = new Organization(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } public function afterSave($insert, $changedAttributes) { if(isset($this->logo)) { $this->logo = UploadedFile::getInstance($this, 'logo'); if(is_object($this->logo)) { $path = Yii::$app->basePath . '/images/'; // set directory path to save image $this->logo->saveAs($path.$this->id."_".$this->logo); // saving image in folder $this->logo = $this->id."_".$this->logo; // appending id to image name \Yii::$app->db->createCommand() ->update('organization', ['logo' => $this->logo], 'id = "'.$this->id.'"') ->execute(); // manually update image name to db } } }
Source: StackOverflow: Yii 2 Upload and Save Image Files Locally and Image URL is Saved in DB