Re: `PDO::FETCH_CONSTRUCTOR` fetch mode proposal
| From: | Larry Garfield | Date: | Fri, 19 Jan 2024 18:03:48 +0000 |
| Subject: | Re: `PDO::FETCH_CONSTRUCTOR` fetch mode proposal | ||
| References: | 1 2 3 4 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-122197@lists.php.net to get a copy of this message | ||
On Fri, Jan 19, 2024, at 9:47 AM, Lynn wrote:
> On Fri, Jan 19, 2024 at 1:51 AM Alexander Pravdin <alex.pravdin@interi.co>
> wrote:
>
>> I would also suggest supporting readonly classes and creating special
>> attributes to help map data fields to constructor arguments. Something like
>> this:
>>
>> readonly class User {
>> public function __construct(
>> #[PDOField('user_id')]
>> public string $userId,
>> #[PDOField('user_name')]
>> public string $userName,
>> #[PDOField('user_address')]
>> public ?string $userAddress = '', // Optional field with default
>> value
>> );
>> }
*snip*
>> > Now, I agree that it is often not possible to use
>> >
PDO::FETCH_CLASS
>> > because DB column names are typically snake case and variables and
>> > properties are camel case.
>> >
>> > IMHO, passing arguments to the constructor in order is a good approach,
>> > but I think another option would be to give attributes to properties and
>> > map data appropriately when using PDO::FETCH_CLASS.
>> >
>> > e.g.
>> > ```
>> > #[ColumnName('user_name')]
>> > private string $userName;
>> > ```
>> >
>> > In addition to the concerns mentioned in the pull request, I'm also
>> > concerned that more modes with similar functionality will confuse users.
>> >
>> > Regards.
>> >
>> > Saki
>> > --
>> > PHP Internals - PHP Runtime Development Mailing List
>> > To unsubscribe, visit: https://www.php.net/unsub.php
>> >
>> >
>>
>
> Would it be interesting to see if there's a way to make the hydration of
> objects more standard in PHP? PDO could then use that
> underlying implementation, but so could every existing library, or even PHP
> extension (SOAP/XML/JSON) that does anything related to object
> hydration/(de)serialization. I'm looking at Symfony, Doctrine, and all the
> other serialization/hydration/auto-mapper library maintainers) for this
> because I wouldn't be able to design this myself.
Hi, maintainer of Crell/Serde here, as requested. :-)
I am highly skeptical that any serious standardization could happen in core directly. Not because
of incompatibility between existing libraries really (we can adapt), but because the problem space
is a lot more complex than it first seems, and that complexity means there's many valid but
incompatible ways to go about it.
Just to use the name example, for Serde, I support a serializedName declaration on a
given property, but also a renameWith declaration that specifies different rules for
processing the name. There's built-in options like Prefix('some_string_'), case
folding via enum objects (like Cases::snake_case), and you can put an arbitrary object on it as long
as it's all compile-time defined. (Eg, you can do a suffix one if you want, but I don't
provide it.) The renameWith directive can also be put on the class level, and that
will apply to all properties unless otherwise specified. Then I'm in the process of adding
renaming capability that's tied to when flattening nested objects.
And that's just for naming things. How much of that should be standardized? How much should
PHP do itself, and how much is better left for either de-facto standardization or FIG? If we do
just a little bit (such as a strict #[SerializedName('some_string')]
attribute and nothing else), does that make it more difficult to do more complex things like I do?
Something like #[SerializedName] is probably safe enough to do that it wouldn't
cause problems, but then what does it actually do *in core* rather than just be a common name for
Serde, Symfony, etc. to use? Does serialize() use it? What then happens on unserialize()? How
does that interact with __serialize()/__unserialize()?
And that's *just* naming things. That's before we even get into type mapping, flattening,
defaults, post-deserialize validation, and all the other stuff.
So it's possible we could do something here, but it will require a lot of thought and care to
not paint ourselves into a corner.
Regarding PDO hydration in particular, while a #[SerializedName] attribute or similar
seems nice, that immediately runs into a problem. What if you want to use that object for
populating from a DB, as well as for round-tripping from JSON? Are you locked into the same name in
both cases? If not, how do you differentiate them? I have a solution for that in Serde, Symfony
Serializer has something similar, but is that something that we want to bake into core? If not,
does that mean we cannot support multi-format serializing rules in the future?
Additionally, while we're talking mostly about names here, there's also the question of
types. Postgres, for instance, supports a much wider set of column types than PDO. For instance,
it supports typed array columns (array of ints, array of strings, etc.). PDO sees those as strings,
which you need to then parse back out into something, and that parsing is not trivial, because
reasons. So if doing a FETCH_CONSTRUCTOR approach, would you need to accept a string in the
constructor, then parse it to an array in the body? That precludes using CPP. That's before
we even get to custom types, which are sort of like object properties and would probably map to
that. But... Is that reasonable to built in by default? How, exactly?
All of which is to say that while I would love to make PDO better and the
serialization/deserialization story better, I think it's too complex to be really *solved* in
core. If anything, we should focus on asking what would make it easier to build user-space wrappers
*on* PDO that do that more complex stuff (renaming, type translation, etc.) in user-space. I'm
not convinced that FETCH_CONSTRUCTOR would be a net improvement or if it would get in the way of
that more complex stuff. I could still be convinced of it, though, if we can be reasonably
confident that it doesn't have unintended negative consequences to future design, the way
readonly did.
--Larry Garfield