# Subir Archivos ¶

Subir archivos en Yii es normalmente realizado con la ayuda de yii\web\UploadedFile, que encapsula cada archivo subido en un objeto `UploadedFile`. Combinado con yii\widgets\ActiveForm y modelos, puedes fácilmente implementar un mecanismo seguro de subida de archivos.

## Crear Modelos ¶

Al igual que al trabajar con entradas de texto plano, para subir un archivo debes crear una clase de modelo y utilizar un atributo de dicho modelo para mantener la instancia del archivo subido. Debes también declarar una regla para validar la subida del archivo. Por ejemplo,

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

En el código anterior, el atributo `imageFile` es utilizado para mantener una instancia del archivo subido. Este está asociado con una regla de validación `file`, que utiliza yii\validators\FileValidator para asegurarse que el archivo a subir tenga extensión `png` o `jpg`. El método `upload()` realizará la validación y guardará el archivo subido en el servidor.

El validador `file` te permite chequear las extensiones, el tamaño, el tipo MIME, etc. Por favor consulta la sección Validadores del Framework para más detalles.

> Consejo: Si estás subiendo una imagen, podrías considerar el utilizar el validador `image`. El validador `image` es implementado a través de yii\validators\ImageValidator, que verifica que un atributo haya recibido una imagen válida que pueda ser tanto guardada como procesada utilizando la Extensión Imagine.

## Renderizar Campos de Subida de Archivos ¶

A continuación, crea un campo de subida de archivo en la vista:

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

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

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

        <button>Enviar</button>

    <?php ActiveForm::end() ?>

Es importante recordad que agregues la opción `enctype` al formulario para que el archivo pueda ser subido apropiadamente. La llamada a `fileInput()` renderizará un tag `<input type="file">` que le permitirá al usuario seleccionar el archivo a subir.

> Consejo: desde la versión 2.0.8, fileInput agrega la opción `enctype` al formulario automáticamente cuando se utiliza una campo de subida de archivo.

## Uniendo Todo ¶

Ahora, en una acción del controlador, escribe el código que una el modelo y la vista para implementar la subida de archivos:

    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()) {
                    // el archivo se subió exitosamente
                    return;
                }
            }

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

En el código anterior, cuando se envía el formulario, el método yii\web\UploadedFile::getInstance() es llamado para representar el archivo subido como una instancia de `UploadedFile`. Entonces dependemos de la validación del modelo para asegurarnos que el archivo subido es válido y entonces subirlo al servidor.

## Uploading Multiple Files ¶

También puedes subir varios archivos a la vez, con algunos ajustes en el código de las subsecciones previas.

Primero debes ajustar la clase del modelo, agregando la opción `maxFiles` en la regla de validación `file` para limitar el número máximo de archivos a subir. Definir `maxFiles` como `0` significa que no hay límite en el número de archivos a subir simultáneamente. El número máximo de archivos permitidos para subir simultáneamente está también limitado por la directiva PHP `max_file_uploads`, cuyo valor por defecto es 20. El método `upload()` debería también ser modificado para guardar los archivos uno a uno.

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

En el archivo de la vista, debes agregar la opción `multiple` en la llamada a `fileInput()` de manera que el campo pueda recibir varios archivos:

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

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

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

        <button>Enviar</button>

    <?php ActiveForm::end() ?>

Y finalmente en la acción del controlador, debes llamar `UploadedFile::getInstances()` en vez de `UploadedFile::getInstance()` para asignar un array de instancias `UploadedFile` a `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()) {
                    // el archivo fue subido exitosamente
                    return;
                }
            }

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