Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eloquent Value Object are not casted back when converting models to json. #33257

Closed
rela589n opened this issue Jun 18, 2020 · 3 comments
Closed

Comments

@rela589n
Copy link

  • Laravel Version: 7.15.0
  • PHP Version: 7.3.7
  • Database Driver & Version: 10.3.16-MariaDB

Description:

The problem is that value objects are not casted back when call Client::toJson().
So, for example we have next model:

App\Models\Client {#4028
     id: 7,
     name: "asdf@asdf.com",
     email_verified_at: null,
   }

Accessing name property gives us Name value object:

App\ValueObjects\Name^ {#4027
  #name: "asdf@asdf.com"
}

When we call $client->toJson(), we get empty object instead of primitive value:
{"id":7,"name":{},"email_verified_at":null}

Steps To Reproduce:

Client Eloquent Model:

class Client extends Authenticatable
{
    use Notifiable;

    protected $table = 'users';

    protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'name'     => NameCast::class,
        'password' => PasswordCast::class,
    ];
}

Name Cast:

class NameCast implements CastsAttributes
{
    public function get($model, string $key, $value, $attributes)
    {
        return Name::create($value);
    }

    public function set($model, string $key, $setName, $attributes)
    {
        if (!$setName instanceof Name) {
            throw new \InvalidArgumentException('Parameter $setName must be instance of ' . Name::class);
        }

        return [
            $key => $setName->value()
        ];
    }
}

Name Value Object:

class Name
{
    public const MIN_LENGTH = 2;

    protected $name;

    private function __construct(string $name)
    {
        if (mb_strlen($name) < self::MIN_LENGTH) {
            throw new \InvalidArgumentException('Name must has at least ' . self::MIN_LENGTH . ' characters');
        }

        $this->name = $name;
    }

    public static function create(string $name): Name
    {
        return new static($name);
    }

    public function value()
    {
        return $this->name;
    }
}
@driesvints
Copy link
Member

I get the same result and was also a bit surprised at this. I'm suspecting that this is the expected behavior and that you should implement Illuminate\Contracts\Support\Arrayable on your value object. But it would be good to clarify this in the docs.

Gonna wait to see what @taylorotwell says about this.

@taylorotwell
Copy link
Member

Objects are expected to implement Arrayable / JsonSerializable to solve this. I thought I documented that but maybe not. If not we can update the documentation.

@driesvints
Copy link
Member

I sent in a PR here: laravel/docs#6123

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants