为什么将 \Illuminate\Http\UploadedFile 的实例传递给自定义转换会导致无限循环?

问题描述 投票:0回答:1

我有一个配置模型,在其中实现了自定义转换,getter 工作完美,但让我们看看 setter。

为了清楚起见,我已将代码示例精简为仅与上下文相关的内容。

应用程序/模型/Configuration.php

use Illuminate\Database\Eloquent\Model;

class Configuration extends Model
{ 
    protected function casts(): array
    {
        return [
            'value' => \App\Casts\ConfigValue::class,
        ];
    }
}

应用程序/Casts/ConfigValue.php

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;

class ConfigValue implements CastsAttributes
{ 
    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
    { 
        $saveable = $value instanceof UploadedFile || 
                     (isset($value[0]) && $value[0] instanceof UploadedFile);

        echo 1; // Take note of this

        return match (true) {
            ...// Other conditions
            $saveable => $this->doUpload(UploadedFile|array $value),
            ...// Other conditions
            default => (string) $value,
        };
    }
}

我们也不必担心文件上传的逻辑,这里的问题是 X-Debug 在处理上传时终止脚本后抛出无限循环错误。但是,如果我直接返回输出,您可以从下图中看到,脚本在返回响应之前会运行多次,添加上传逻辑会使我们陷入无限循环。

example response from postman, here you can see that 1 is being outputed several times.

应用程序/控制器/ConfigController.php

$config = Configuration::where('key', $key)->first();

$config->value = $value;
$config->save();

在控制器上

$key
$value
来自
$request->configurations
的 foreach 循环,当
$value
不是
\Illuminate\Http\UploadedFile
的实例时,上面的方法效果很好,在这种情况下,我们会陷入无限循环。

我在这里做错了什么?

php laravel database eloquent laravel-11
1个回答
0
投票

发生无限循环是因为您的自定义转换的

set
方法在处理 UploadedFile 实例时导致对其自身的递归调用。

重写你的设置方法:

class ConfigValue implements CastsAttributes
{
    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        $saveable = $value instanceof UploadedFile ||
                    (is_array($value) && isset($value[0]) && $value[0] instanceof UploadedFile);

        if ($saveable) {
            $filePath = $this->doUpload($value);

            return $filePath;
        }


        return (string) $value;
    }

    private function doUpload(UploadedFile|array $value): string
    {
        if (is_array($value)) {
            $paths = [];
            foreach ($value as $file) {
                $paths[] = $file->store('uploads');
            }
            return json_encode($paths);
        } else {
            return $value->store('uploads');
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.