Custom cast ignored after updating model

Viewed 129

After I update my model, the cast is ignored and an old value that comes from classCastCache is shown.

I'm getting a Illuminate\Http\Testing\File instead of 'GETTING'.

  1. Create a custom cast:
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Image implements CastsAttributes
{
    /**
     * Cast the given value.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return mixed
     */
    public function get($model, $key, $value, $attributes)
    {
        return 'GETTING';
    }

    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return mixed
     */
    public function set($model, $key, $value, $attributes)
    {
        return 'SETTING';
    }
}
  1. Apply it to a model:
<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Casts\Image;

class Product extends Model
{
    use HasFactory;

    /**
     * Mass assignable attributes.
     *
     * @var string[]
     */
    protected $fillable = [
        'name',
        'image',
    ];

    /**
     * Model's attribute casting.
     *
     * @var array
     */
    protected $casts = [
        'image' => Image::class,
    ];
}
  1. Create a test for it:
    /** @test */
    public function it_can_change_product_image()
    {
        Storage::fake();

        $business = Business::factory()->create();
        $product = Product::factory()->for($business)->create();

        $this->authenticate($business->owner); // Helper to authenticate with Sanctum. Ignore.

        // The important part here, sending a fake image.
        $response = $this->putJson(route('v1.products.update', $product), [
            'image' => UploadedFile::fake()->image('example.jpg'),
        ]);

        $response->assertOk(); // Among other tests that are hidden due demo purposes.
    }
  1. Create a Controller action to update the value of it.
    /**
     * Update the specified resource in storage.
     *
     * @param UpdateRequest $request
     * @param Product $product
     * @return ProductResource
     */
    public function update(UpdateRequest $request, Product $product): ProductResource
    {
        // The custom request validator checks for:
        // 'image' => ['sometimes', 'file', 'max:1024', 'image', 'mimes:jpg,bmp,png']
        // This should update the product image based on the cast.
        // The current DB value should be `SETTING`.
        $product->update($request->validated());
        // This should be `GETTING`.
        // Instead, it is: Illuminate\Http\Testing\File
        dd($product->image);
        // a BAD workaround is to:
        $product->refresh();
        // This is the correct value `GETTING`.
        dd($product->image);
    }
0 Answers
Related