-
Notifications
You must be signed in to change notification settings - Fork 2
2.0 — Feature: Built-in system for converting from query data #19
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
base: release/2.0.0
Are you sure you want to change the base?
Conversation
I'm really on the fence about the |
I like the additions you are suggestion, @jonwaldstein, however, the guts of this PR is still valid and needed. What I like about the approach in the PR is that it allows for more flexibility by not being overly opinionated about how a Model gets its data - which greatly enhances its ability to be used in scenarios where the DB library is and isn't in play. While the name Again, I think what you are proposing is a great addition where we want to be more explicit. |
case 'float': | ||
return (float) $value; | ||
default: | ||
Config::throwInvalidArgumentException( "Unexpected type: '$type'. To support additional types, implement a custom castValueForProperty() method." ); |
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.
I feel like this could happen up the chain before we get into casting, doing something like $this->isPropertyTypeValid($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.
I purposely want to do this here so all someone has to do is overload this function to support additional casting of types.
@borkweb I do like the idea of simply using the model definition to convert a SQL object to a model. Of course that means the alias's need to match the model attributes. I agree, this is less stuff for a dev to have to think about which i'm all for. However, i'm still just questioning what the model's responsibility is when it comes to converting itself from some object. |
The ModelQueryBuilder is centered around queries, whereas this implementation is a bit more base level and generic than that. I think a model knowing how to construct itself is perfectly reasonable - at the end of the day, we get to dictate what the Model's responsibility is. Supporting a baseline ingestion of an array or object in the Model is a great value add for simplicity. If we want a more complicated or nuanced approach for a model or set of models, the method can be overridden and deferred to other objects. |
@borkweb yeah true, it's a nice default to have and you can always do your own thing if need be. I was poking around Laravel, which obviously was huge inspiration for me (although I originally had to go back to one of the earliest iterations because of php support 😏 and of course simplify a lot) Some noteworthy concepts that are quite similar to us but still good inspiration:
I like the way they connect Models and QueryBuilder with a naming convention that's easy to understand. That whole concept of custom |
@jonwaldstein - yeah, I agree. All three of those look like implementations about what we're talking about above! I had to look up what the You are spot on about the But the point about supporting new Model creation in a couple of different ways lands us in a nicely flexible and usable spot. 🥳 |
Alright! A couple of topics to hit on here! I'm loving this discussion! Properties & Casting I'm a "break early" kind of thinker. So these models validate both the existence of properties as well as their type. As such, I merged Model vs ModelQueryBuilder Shaping On the other hand, Jon, I agree that requiring that the query returns the exact shape could be an unnecessary requirement. I like the idea that it will assume this to be the case, but it can be overridden. My plan, in a subsequent PR, is to add support for |
Co-authored-by: Jon Waldstein <Jpwaldstein@gmail.com>
Co-authored-by: Matthew Batchelder <borkweb@gmail.com>
@JasonTheAdams one more thought on the whole |
src/Models/Model.php
Outdated
* @param int $mode The level of strictness to take when constructing the object, by default it will ignore extra keys but error on missing keys. | ||
* @return static | ||
*/ | ||
public static function fromQueryData($queryData, $mode = self::BUILD_MODE_IGNORE_EXTRA): static { |
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.
@borkweb This is the mode I was talking about. I'm curious what you think the default mode should be.
Resolves #15
As noted in the Issue, models didn't have a first-class way of building from query data, which was a problem since relationships relied on this.
This PR introduces the ability to build from query data right into the
Model
class. For models that only have primitive properties, no extra work is needed. If the model has class properties, then the way to convert them is by overloading thecastValueForProperty
static method. It will probably look something like this:I decided to leave (although rename) the
ModelBuildsFromQueryData
interface. I'm really tempted to get rid of it. But I wanted to defer to @borkweb, who first added it, as to whether he thinks it's still necessary. I can't personally think of a use-case where we'd want to pass an interface whose only purpose is to be able to create models, when the underlying model is what's actually concretely passed. That sounds more like a factory.