/** * 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); }