/**
  * Like insert but we will never need to recompute the key
  *
  * @return void
  * @since  1.0.0
  */
 public function beforeUpdate()
 {
     foreach ($this->attributes as $attribute => $config) {
         $aliasPath = $this->getAliasPath($attribute, true);
         if (is_array($this->owner->{$attribute}) === false) {
             if (empty($this->owner->{$attribute}) === false) {
                 $this->owner->{$attribute} = [$this->owner->{$attribute}];
             } else {
                 $this->owner->{$attribute} = [];
             }
         }
         // clean up attributes
         $this->owner->{$attribute} = $this->cleanUpProperty($this->owner->{$attribute});
         $selectedFiles = $this->owner->{$attribute};
         $savedFiles = [];
         $attributeName = Html::getInputName($this->owner, $attribute);
         $uploadedFiles = UploadedFile::getInstancesByName($attributeName);
         foreach ($uploadedFiles as $instance) {
             if (in_array($instance->name, $selectedFiles) === true) {
                 $fileName = static::sanitize($instance->name);
                 if (empty($instance->tempName) === true) {
                     // image was uploaded earlier
                     // we should probably check if image is always available
                     $savedFiles[] = $fileName;
                 } else {
                     $targetFile = Yii::getAlias($aliasPath . '/' . $fileName);
                     $targetPath = pathinfo($targetFile, PATHINFO_DIRNAME);
                     if (is_dir($targetPath) === false) {
                         if (mkdir($targetPath, 0755, true) === false) {
                             throw new Exception('Unable to create path : "' . $targetPath . '"');
                         }
                     }
                     if ($instance->saveAs($targetFile) === true) {
                         //TODO: saved files must be removed - correct place would be in UploadedFile
                         $savedFiles[] = $fileName;
                     }
                 }
             }
         }
         $this->owner->{$attribute} = $savedFiles;
         $this->serializeAttribute($attribute);
     }
 }
 /**
  * Create an asynchronous file upload.
  * Plupload specific configuration should be set in $options['config']
  * The $options['config']['ui'] can be :
  *  * false // no ui
  *  * true  // basic ui
  *  * Javascript object (passed in Jsexpression). Everything is handled by the developper
  *
  * @param  string       $name    name of the input (append [] for multifile upload)
  * @param  string|array $value   the file(s) already uploaded (array for multifile upload)
  * @param  array        $options @see static::input()
  *
  * @return string
  * @since  1.0.0
  */
 public static function asyncInput($name, $value = null, $options = [])
 {
     // prepare data
     $originalName = $name;
     if (is_array($value) === true) {
         $value = array_filter($value, function ($el) {
             return !empty($el);
         });
     }
     if (empty($value) === true) {
         $value = null;
     }
     // prepare option data
     $options['name'] = $name;
     $options['config']['multiSelection'] = false;
     if (isset($options['id']) === false) {
         $options['id'] = self::getIdByName($name);
     }
     // remove the trailing [] which is just annoying except to send multiple files
     // we rely on [] to define the multiple file upload
     if (substr($name, -2) === '[]') {
         $originalName = substr($name, 0, -2);
         $options['config']['multiSelection'] = true;
         if ($value !== null) {
             if (is_array($value) === false) {
                 // force array for multifile
                 $value = [$value];
             }
             $value = array_map(function ($el) {
                 // remove everything which is path related. It must be handled by the developper / model
                 return pathinfo(trim($el), PATHINFO_BASENAME);
             }, $value);
         }
     } else {
         if ($value !== null) {
             $value = pathinfo((string) $value, PATHINFO_BASENAME);
         }
     }
     if ($value !== null) {
         // we must retrieve original data
         $instances = UploadedFile::getInstancesByName($originalName);
         // force check with arrays
         $affectedFiles = is_array($value) === true ? $value : [$value];
         //translate everything into UploadedFile(s)
         $translatedValues = [];
         foreach ($instances as $instance) {
             if (in_array($instance->name, $affectedFiles) === true) {
                 $affectedFilesKeys = array_keys($affectedFiles, $instance->name);
                 unset($affectedFiles[$affectedFilesKeys[0]]);
                 $translatedValues[] = $instance;
             }
         }
         foreach ($affectedFiles as $remainingFile) {
             // we are looking at already saved files. Reinit as expected
             $translatedValues[] = new UploadedFile(['name' => $remainingFile, 'tempName' => null, 'type' => 'application/octet-stream', 'size' => null, 'error' => 0]);
         }
         $value = $translatedValues;
         if ($options['config']['multiSelection'] === false) {
             // get first value in case we are not multi
             $value = array_pop($value);
         }
     }
     $config = static::prepareAsyncInput($options);
     return static::renderAsyncInput($value, $options, $config);
 }