# Wysyłanie plików ¶

Przesyłanie plików w Yii jest zazwyczaj wykonywane przy użyciu klasy UploadedFile, która hermetyzuje każdy przesłany plik jako obiekt `UploadedFile`. W połączeniu z ActiveForm oraz modelem, możesz w łatwy sposób zaimplementować bezpieczny mechanizm przesyłania plików.

## Tworzenie modeli ¶

Tak jak przy zwykłych polach tekstowych, aby przesłać pojedyńczy plik musisz utworzyć klasę modelu oraz użyć atrybutu tego modelu do przechowania instancji przesłanego pliku. Powinieneś również zadeklarować zasadę walidacji do zwalidowania przesłanego pliku. Dla przykładu:

    namespace app\models;

    use yii\base\Model;
    use yii\web\UploadedFile;

    class UploadForm extends Model
    {
        /**
         * @var UploadedFile
         */
        public $imageFile;

        public function rules()
        {
            return [
                [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
            ];
        }

        public function upload()
        {
            if ($this->validate()) {
                $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);
                return true;
            } else {
                return false;
            }
        }
    }

W powyższym kodzie, atrybut `imageFile` zostanie użyty do przechowania instancji przesłanego pliku. Jest połączony z zasadą walidacji `file`, która korzysta z walidatora FileValidator, aby upewnić się, że przesłany plik posiada rozszerzenie `png` lub `jpg`. Metoda `upload()` wywoła walidację oraz zapis przesłanego pliku na serwerze.

Walidator `file` pozwala na sprawdzenie rozszerzenia, wielkości, typu MIME, itp. Po więcej szczegółów zajrzyj do sekcji Podstawowe walidatory

> Wskazówka: Jeśli przesyłasz obrazek, możesz rozważyć użycie walidatora `image`. Walidator ten jest implementowany przez ImageValidator, który weryfikuje czy atrybut otrzymał prawidłowy obrazek który może być zapisany i przetworzony przez rozszerzenie Imagine.

## Renderowanie pola wyboru pliku ¶

Po zapisaniu modelu, utwórz pole wyboru pliku w widoku:

    <?php
    use yii\widgets\ActiveForm;
    ?>

    <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>

        <?= $form->field($model, 'imageFile')->fileInput() ?>

        <button>Wyślij</button>

    <?php ActiveForm::end() ?>

Należy pamiętać, aby dodać opcję `enctype` do formularza, przez co plik będzie mógł być prawidłowo przesłany. Wywołanie `fileInput()` spowoduje wyrenderowanie tagu `<input type="file">`, który pozwala użytkownikowi na wybranie oraz przesłanie pliku.

> Wskazówka: od wersji 2.0.8, fileInput dodaje automatycznie opcję `enctype` do formularza, kiedy pole typu 'file input' jest używane.

## Implementacja kontrolera ¶

W akcji kontrolera musimy połączyć model oraz widok aby zaimplementować przesyłanie plików:

    namespace app\controllers;

    use Yii;
    use yii\web\Controller;
    use app\models\UploadForm;
    use yii\web\UploadedFile;

    class SiteController extends Controller
    {
        public function actionUpload()
        {
            $model = new UploadForm();

            if (Yii::$app->request->isPost) {
                $model->imageFile = UploadedFile::getInstance($model, 'imageFile');
                if ($model->upload()) {
                    // plik został przesłany
                    return;
                }
            }

            return $this->render('upload', ['model' => $model]);
        }
    }

W powyższym kodzie, kiedy formularz jest wysłany, metoda getInstance() wywoływana jest do reprezentowania pliku jako instancji `UploadedFile`. Następnie przystępujemy do walidacji modelu, aby upewnić się, że przesłany plik jest prawidłowy, po czym zapisujemy go na serwerze.

## Przesyłanie wielu plików ¶

Możesz przesyłać wiele plików za jednym razem, modyfikując odrobinę kod wylistowany w powyższych sekcjach.

Najpierw powinieneś dostosować klasę modelu dodając opcję `maxFiles` do zasady walidacji `file`, aby określić dozwoloną maksymalną liczbę przesyłanych plików. Metoda `upload()` powinna również zostać zaktualizowana, aby zapisywać pliki jeden po drugim.

    namespace app\models;

    use yii\base\Model;
    use yii\web\UploadedFile;

    class UploadForm extends Model
    {
        /**
         * @var UploadedFile[]
         */
        public $imageFiles;

        public function rules()
        {
            return [
                [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],
            ];
        }

        public function upload()
        {
            if ($this->validate()) {
                foreach ($this->imageFiles as $file) {
                    $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);
                }
                return true;
            } else {
                return false;
            }
        }
    }

W pliku widoku, powinieneś dodać opcję `multiple` do wywołania `fileInput()`, aby pole wyboru pliku pozwalało na wybór wielu plików na raz:

    <?php
    use yii\widgets\ActiveForm;
    ?>

    <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>

        <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>

        <button>Submit</button>

    <?php ActiveForm::end() ?>

Na koniec, w akcji kontrolera musimy zmienić wywołanie `UploadedFile::getInstance()` na `UploadedFile::getInstances()`, aby przypisać tablicę instancji `UploadedFile` do `UploadForm::imageFiles`.

    namespace app\controllers;

    use Yii;
    use yii\web\Controller;
    use app\models\UploadForm;
    use yii\web\UploadedFile;

    class SiteController extends Controller
    {
        public function actionUpload()
        {
            $model = new UploadForm();

            if (Yii::$app->request->isPost) {
                $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');
                if ($model->upload()) {
                    // plik został przesłany
                    return;
                }
            }

            return $this->render('upload', ['model' => $model]);
        }
    }