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

Model

In the model, override afterSave() to store image file to server, and its path in the database. Eg: @app/models/Organization.php:

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
        }
    }
}

Controller

Add action actionCreate() to controller. Eg: @app/controllers/OrganizationController.php:

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,
     ]);
  }
}

View

<?= $form->field($model, 'logo')->fileInput(['class'=>'form-control']) ?>

Source: StackOverflow: Yii 2 Upload and Save Image Files Locally and Image URL is Saved in DB

Example Image File Upload

Model

File @app/models/ImageFileUpload.php:

<?php
namespace app\models;
 
use yii;
use yii\base\Model;
use yii\web\UploadedFile;
 
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
 
class ImageUploadForm extends Model
{
    /**
     * @var UploadedFile
     */
    public $imageFile;
 
    public function rules()
    {
        return [
            // see '@vendor/yiisoft/yii2/validators/FileValidator.php' for all options
            [['imageFile'], 'file', 
                'skipOnEmpty'    => false, 
                'maxFiles'       => 1,
                'extensions'     => 'png, jpg', 
                //'mimeTypes'      => "image/png, image/jpg",  // must match the file type (extension) to be uploaded. Eg: png, jpg
                //'mimeTypes'      => "*",  // any MIME type
                'checkExtensionByMimeType' => false,  // false to stop verifying file extension to MIME types
                'wrongExtension' => '{attribute} = [{file}].  Only files with these extensions are allowed: {extensions}.'
            ],
        ];
    }
 
    public function upload()
    {
        if ($this->validate()) {
            //$this->imageFile->saveAs(
            //  strtolower("uploads/{$this->imageFile->baseName}.{$this->imageFile->extension}")
            //);  // save to [app]/web/uploads
            $this->imageFile->saveAs("uploads/" . $this->getFileName());  // save to [app]/web/uploads
            return true;
        } else {
            return false;
        }
    }
 
    public function getFileName()
    {
        return strtolower("{$this->imageFile->baseName}.{$this->imageFile->extension}");
    }
 
    public function getFilePath()
    {
        return Yii::$app->basePath . DS . 'web' . DS . 'uploads' . DS . $this->getFileName();
    }
 
    public function getFileUrl()
    {
        return Yii::$app->homeUrl . "/uploads/" . $this->getFileName();
    }
 
}
?>

File @app/models/Tool.php:

<?php
 
namespace app\models;
 
use Yii;
use yii\base\Model;         // parent for model classes not associated with database tables
//use yii\db\ActiveRecord;  // parent for model classes that do correspond to database tables
 
define('PNG_DPI',      75);
define('LABEL_DPI',    300);
define('LABEL_WIDTH',  6);
define('LABEL_HEIGHT', 4);
define('LABEL_MARGIN', 5);
define('PNG_FONT',     5);
 
/**
 * This is the model class for table "Tools".
 *
 * @property array $countries
 * @property array $countryNames
 * @property array $languages
 * @property array $states
 */
class Tool extends Model
{
    //...
 
    /**
     * @inheritdoc
     * Usage: 
     *   return '<img src="data:image/png;base64,' . base64_encode(\app\models\Tool::generateEmptyPng()) . '"/>';
     */
    public static function generateEmptyPng() 
    {
        $image  = imagecreate(PNG_DPI * LABEL_WIDTH, PNG_DPI * LABEL_HEIGHT);
        $canvas = imagecreate(PNG_DPI * LABEL_WIDTH, PNG_DPI * LABEL_HEIGHT);
 
        // Setup color
        //$color_background = imagecolorallocate($image, 255, 255, 255);  // Background = white
        $color_background = imagecolorallocate($image, 200, 200, 200);  // Background = gray
        $color_text       = imagecolorallocate($image, 0, 0, 0);        // Foreground = black
 
        // Background
        imagefill($image, 0, 0, $color_background);
 
        // Image border
        //imagerectangle($image, 
        //    LABEL_MARGIN, LABEL_MARGIN,
        //    (LABEL_WIDTH*PNG_DPI)-LABEL_MARGIN, 
        //    (LABEL_HEIGHT*PNG_DPI)-LABEL_MARGIN,
        //    $color_text
        //);
 
        // Text content
        imagestring($image, PNG_FONT, 20, 20, 'NO IMAGE FILE', $color_text);
 
        // Copy image to target canvas
        imagecopy(
            $canvas,                  // dst
            $image,                   // src
            0, 0,                     // dst x,y
            0, 0,                     // src x,y
            (LABEL_WIDTH  * PNG_DPI), // src width
            (LABEL_HEIGHT * PNG_DPI)  // src height
        );
        imagedestroy($image);
 
        // Capture the full image from canvas
        ob_start();
        imagepng($canvas);
        $imageData = ob_get_contents();
        ob_end_clean();
 
        return $imageData;
    }
}
?>

Controller

File @app/controllers/ItemController.php:

//...
use app\models\ImageUploadForm;
 
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
 
class ItemController extends Controller
{
    //...
 
    public function actionUploadImage($id)
    {
        $model = new ImageUploadForm();
        $item = $this->findModel($id);
 
        if (Yii::$app->request->isPost) {
            $model->imageFile = \yii\web\UploadedFile::getInstance($model, 'imageFile');
 
            if ($model->upload()) {
                // file is uploaded successfully
                Yii::$app->session->setFlash('success', 
                  "File <tt>{$model->imageFile}</tt> was uploaded successfully."
                );
 
                // Assign file to item 
                // (rename to item_code if available, otherwise keep original filename)
                if (!empty($item->code)) {
                    $targetFile = "{$item->code}.{$model->imageFile->extension}";
                } else {
                    $targetFile = $model->getFileName();
                }
                $baseWebPath   = Yii::$app->basePath . DS . 'web';
                //$sourcePath = $baseWebPath . DS . $model->getFileName();
                $sourcePath = $model->getFilePath();
                $targetDir  = $baseWebPath . DS . 'img' . DS . 'data' . DS . Yii::$app->controller->id;
                $targetPath = $targetDir . DS . $targetFile;
                if (!file_exists($targetDir)) {
                    mkdir($targetDir, 0755, true /* recursive */);
                }
                $resultsImport = move_uploaded_file($sourcePath, $targetPath);
                //$resultsImport = $model->imageFile->saveAs($targetPath);  // save to [app]/web/img/data/item
                // When move_uploaded_file() fails to work, we go old school and force it to do it.
                if (!$resultsImport) {
                    $resultsImport = copy($sourcePath, $targetPath);  // copy source file to target
                    unlink($sourcePath);                              // delete source file
                }
                $item->image_file = $targetFile;
                $item->save();
 
                // import data
                if ($resultsImport) {
                    Yii::$app->session->setFlash('success', 
                      "Item image <tt>{$model->imageFile}</tt> was uploaded successfully."
                    );
                } else {
                    $msg  = "Item image <tt>{$model->imageFile}</tt> failed to be moved.<br>";
                    $msg .= "- Source:<tt>{$sourcePath}</tt> <br>";
                    $msg .= "- Target:<tt>{$targetPath}</tt>";
                    Yii::$app->session->setFlash('error', $msg);
                }
 
                return $this->redirect(['view', 'id' => $id]);
            } else {
                Yii::$app->session->setFlash('error', "File {$model->imageFile} failed to be uploaded.");
            }
        }
 
        return $this->render('upload-image', [
            'model' => $model, 
            'item'  => $item,
        ]);
    }
}

View

File @app/views/item/upload-image:

<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
 
$this->title = Yii::t('app', 'Image Upload');
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Item'), 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => "{$item->code} {$item->description}", 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
 
?>
 
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
 
    <h2><?= $this->title ?></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>
 
    <?= $form->field($model, 'imageFile')->fileInput(['class'=>'form-control']) ?>
 
    <button class='btn btn-success'>Submit</button>
 
<?php ActiveForm::end() ?>

File @app/views/item/view.php to call @app/views/item/upload-image.php action:

<?php
  $itemImage = (!empty($model->image_file) ? 
     Yii::$app->homeUrl . 'img/data/' . Yii::$app->controller->id . "/{$model->image_file}" : 
     ''
  );
?>
 
<!-- Image Thumbnail -->
<?php if(!empty($itemImage)): ?>
    <img class="img-thumbnail" src="<?= $itemImage ?>" alt="Item Image">
<?php else: ?>
    <img class="img-thumbnail" src="data:image/png;base64,<?= base64_encode(\app\models\Tool::generateEmptyPng()) ?>"/>
<?php endif; ?>
 
<!-- Button trigger modal for Image -->
<?= Html::button('<i class="fa fa-image" aria-hidden="true"></i> ' . Yii::t('app', 'View Image'), [
    'class'       => 'btn btn-default', 
    'data-toggle' => "modal",
    'data-target' => "#modalImagePreview"
]) ?>
<?= Html::a('<i class="fa fa-plus" aria-hidden="true"></i> ' . Yii::t('app', 'Add Image'), ['upload-image', 'id' => $model->id], ['class' => 'btn btn-default']) ?>
 
<!-- Modal for Image -->
<div class="modal fade" id="modalImagePreview" tabindex="-1" role="dialog">
  <div class="modal-dialog modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Item Image</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <?php if(!empty($itemImage)): ?>
            <img class="img-fluid" src="<?= $itemImage ?>" alt="Item Image">
        <?php else: ?>
            <img class="img-fluid" src="data:image/png;base64,<?= base64_encode(\app\models\Tool::generateEmptyPng()) ?>"/>
        <?php endif; ?>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

Or simply use the Yii modal helper:

<?php
    $itemImage = (!empty($model->image_file) ? 
        Yii::$app->homeUrl . 'img/data/' . Yii::$app->controller->id . "/{$model->image_file}" : 
        ''
    );
 
    // Image Thumbnail
    if(!empty($itemImage)) {
        echo "<img class='img-thumbnail' src='{$itemImage}' alt='Item Image'>";
    } else {
        echo "<img class='img-thumbnail' src='data:image/png;base64," . base64_encode(\app\models\Tool::generateEmptyPng()) . "'/>";
    }
 
    echo Html::a('<i class="fa fa-plus" aria-hidden="true"></i> ' . Yii::t('app', 'Add Image'), 
        ['upload-image', 'id' => $model->id], 
        ['class' => 'btn btn-default']
    );
 
    // Modal Window
    yii\bootstrap\Modal::begin([
        'header' => '<h4>Item Image</h4>',
        'size'   => yii\bootstrap\Modal::SIZE_LARGE,
        'toggleButton' => [
            'label' => '<i class="fa fa-image" aria-hidden="true"></i> ' . Yii::t('app', 'View Image'), 
            'class' => 'btn btn-default'
        ],
    ]);
 
    if(!empty($itemImage)) {
        echo "<img class='img-fluid' src='{$itemImage}' alt='Item Image'>";
    } else {
        echo "<img class='img-fluid' src='data:image/png;base64," . base64_encode(\app\models\Tool::generateEmptyPng()) . "'/>";
    }
    echo '<div class="modal-footer">';
    echo '    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>';
    echo '</div>';
 
    yii\bootstrap\Modal::end();  
?>