Skip to content

[5.6] Cast model attribute to any class #24059

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

Closed
wants to merge 3 commits into from
Closed

[5.6] Cast model attribute to any class #24059

wants to merge 3 commits into from

Conversation

Whyounes
Copy link

Currently, you can cast a model attribute to a primitive type or JSON. This PR will let model attributes to cast to any class. Here's an example:

Class MyModel extends EloquentModel {
	protected $casts = [
		‘age’ => ‘int’,
		‘settings’ => Settings::class
	];
}

Class Settings implements Castable{
	protected $settings;

	public function __construct($value)
	{
		$value = json_decode($value);
		if ($value === false) {
			throw new InvalidArgumentException(“Invalid value”);
		}

		$this->settings = $value;
	}

	public static function fromModelValue($value) {
		return new static($value);
	}
}

$myModel = MyModel::first();

dump($myModel->settings);

// will return an instance of Settings class.

@GrahamCampbell GrahamCampbell changed the title Cast model attribute to any class [5.6] Cast model attribute to any class Apr 30, 2018
@antonkomarev
Copy link
Contributor

Casting model attributes to classes PRs raised couple of times already, but all the time they were rejected. This will be useful to create Value Objects.

@sisve
Copy link
Contributor

sisve commented May 1, 2018

How is this attempt better than anything previously attempted?

  1. This implementation assumes that I am the creator of the value objects and can change them. I cannot use this if I want someone else's value objects.
  2. How would this implement value objects that requires two columns? For example; a Money class that has both an amount and a currency.
  3. How would this handle the reverse; User::where('email', '=', $emailObject)?

@Whyounes
Copy link
Author

Whyounes commented May 1, 2018

@sisve

I wasn't following all the previous propositions. This is just something I did a couple times in a Laravel app and I thought it could be useful to have out of the box instead of falling back to mutators.

  1. If you're not the creator of the value object, create a factory class for the value. Which can build the value from different inputs.

  2. Normally, a model attribute is just one column not two. So in this case you use your factory method to decide how to build the value object.

  3. This can be in another PR maybe. We can just call toString on the object?

@RoccoHoward
Copy link
Contributor

@Whyounes My implementation of something similar can be found at https://github.com/hnhdigital-os/laravel-model-schema

It's more than just attribute casting though, I've worked in a different approach to casts/hidden etc into a single schema array.

@Whyounes
Copy link
Author

Whyounes commented May 1, 2018

@RoccoHoward I think this custom cast should be in the core. It's just one step from what we already have!

@sisve
Copy link
Contributor

sisve commented May 1, 2018

@Whyounes

  1. If you're not the creator of the value object, create a factory class for the value. Which can build the value from different inputs.

So I would use these changes to point to a factory class instead of the value object?

  1. Normally, a model attribute is just one column not two. So in this case you use your factory method to decide how to build the value object.

A model attribute, yes, but not a value object. Money consists of a number and a currency. An Address consists of street name, zip code, city, and other attributes. Even a parsed email address can have two attributes; the actual email address and the display name.

  1. This can be in another PR maybe. We can just call toString on the object?

I disagree with two points. I think this PR is incomplete without supporting the reverse, even it it were only for single-attribute value objects. And calling toString wouldn't really work if the value being wrapped was a non-string value, like an array or floating point number.

@sisve
Copy link
Contributor

sisve commented May 1, 2018

If you're interesting in catching up on previous discussions, start at #18229

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

Successfully merging this pull request may close these issues.

5 participants