-
Notifications
You must be signed in to change notification settings - Fork 2
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
Too eager validation - fails common uses. #9
Conversation
src/Models/Model.php
Outdated
@@ -149,6 +149,7 @@ protected function getPropertyDefault( string $key ) { | |||
protected function getPropertyDefaults() : array { | |||
$defaults = []; | |||
foreach ( array_keys( $this->properties ) as $property ) { | |||
// @todo This could be an edge case bug, we shouldn't assume `null` is always a valid default value. | |||
$defaults[ $property ] = $this->getPropertyDefault( $property ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An issue I can see happening here is a conflict between things that are explicitly being set to NULL
versus ones that have not been set at all. This muddies that water, and makes it hard to know which situation is happening.
We ought to embrace MySQL and their DEFAULT
and NULL
features, and a PHP null
would translate to a MySQL NULL
. And a non-defined value (e.g. not set to NULL
or any other value) should allow MySQL to leverage it's DEFAULT
definitions.
And of course if we define a PHP default, that should take precedence and happen here, but I don't believe null
in all other cases is safe.
https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
@@ -338,14 +377,13 @@ public static function propertyKeys() : array { | |||
*/ | |||
public function setAttribute( string $key, $value ) : ModelInterface { | |||
$this->validatePropertyExists( $key ); | |||
$this->validatePropertyType( $key, $value ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scenarios this would fail that we ought to support:
class Fish extends Model {
protected $properties = [
'eggs' => 'int',
];
...
And in use...
// This would fatal.
$fish = new Fish();
$fish->eggs = '10';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we don't want Fatal errors, it does not feel like deleting is a good solution. I wonder what @borkweb thinks about this approach.
case 'date': | ||
return is_string( $value ) ? $value : $value->format( 'Y-m-d' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case 'date':
return is_string( $value ) ? $value : $value->format( 'Y-m-d' );
Would like to start supporting date
values. Thoughts?
I'll weigh in simply because this was originally taken from the GiveWP codebase, which I helped build. This doesn't mean this can't deviate from that, but I felt it worth explaining our rationale at the time. We intentionally didn't want the models to work the way the Eloquent models do, wherein types are cast at the time of retrieval. The reason being we wanted to catch errors early in the process. Using the example you gave: $fish = new Fish();
$fish->eggs = '10'; If this were a $fish = new Fish();
$fish->eggs = 'buffalo'; That wouldn't be caught unless we now introduce some kind of This all comes down to the philosophy that errors should happen as soon as possible. Hope this helps! 😄 |
As far as the nullable behavior is concerned, I wouldn't include that in this PR, as that's a separate concept than JIT casting. I can certainly see value in declaring whether a given property is nullable. 👍 |
|
||
$type = $this->getPropertyType( $key ); | ||
|
||
switch ( $type ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe changing it with settype( $value, $type );
for multiple cases will be nicer, so you don't need to read all cases really as they are the same.
src/Models/Model.php
Outdated
* @param string $key The property name. | ||
* @param mixed $value The value to be cast. | ||
* | ||
* @return array|bool|int|string The $value after being cast to it's type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the default case on line 363
we can return mixed
still.
src/Models/Model.php
Outdated
case 'string': | ||
return (string) $value; | ||
case 'bool': | ||
return (bool) $value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding booleans, there is a tricky moment in PHP. I'm not sure if we want to support it here, but we had such case in LearnDash, so want to make sure you are aware.
if ( 'bool' === $type ) {
// We want to convert "false" string to false.
$value = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
}
… default value check, fixing a potential bug. WIP.
Thanks, I don't disagree with that mentality at all. I prefer the explicit definitions. Although the side effect would be we need to have some more formal type casting into a DTO or other request input which we are missing at the moment. We need to avoid a lot of the |
I think extending this to support object casting via interface would be awesome! |
This is to update our handling of type definitions in the
Model
. Some questions below.