-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
feat: add DataConverter to convert types #8230
Conversation
4cad258
to
885396e
Compare
21aba18
to
26dbad2
Compare
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.
Did not review this completely, but couple of thoughts:
- How is this different from Entity casts? By looking at the code, these seem to duplicate the code there.
- Would these qualify for generic typing, like
T
->U
for thefromDatabase
method andU
->T
fortoDatabase
method. That way we can have static analysers come into play? What do you think?
The current Entity has problems.
|
|
26dbad2
to
cd9967c
Compare
I agree. Entities could have been value objects only. |
I tried something like these: diff --git a/system/Database/DataConverter/Cast/CastInterface.php b/system/Database/DataConverter/Cast/CastInterface.php
index 540623ea33..b793b6ea20 100644
--- a/system/Database/DataConverter/Cast/CastInterface.php
+++ b/system/Database/DataConverter/Cast/CastInterface.php
@@ -12,27 +12,28 @@
namespace CodeIgniter\Database\DataConverter\Cast;
/**
- * Interface CastInterface
+ * @template TDbColumn
+ * @template TPhpValue
*/
interface CastInterface
{
/**
* Takes value from database, returns its value for PHP.
*
- * @param bool|float|int|string|null $value Data
- * @param array $params Additional param
+ * @param TDbColumn $value Data
+ * @param list<string> $params Additional param
*
- * @return array|bool|float|int|object|string|null
+ * @return TPhpValue
*/
- public static function fromDatabase($value, array $params = []);
+ public static function fromDatabase(mixed $value, array $params = []): mixed;
/**
* Takes the PHP value, returns its value for database.
*
- * @param array|bool|float|int|object|string|null $value Data
- * @param array $params Additional param
+ * @param TPhpValue $value Data
+ * @param list<string> $params Additional param
*
- * @return bool|float|int|string|null
+ * @return TDbColumn
*/
- public static function toDatabase($value, array $params = []);
+ public static function toDatabase(mixed $value, array $params = []): mixed;
} diff --git a/system/Database/DataConverter/Cast/BaseCast.php b/system/Database/DataConverter/Cast/BaseCast.php
index d35b761622..e8ef0e13ba 100644
--- a/system/Database/DataConverter/Cast/BaseCast.php
+++ b/system/Database/DataConverter/Cast/BaseCast.php
@@ -14,28 +14,25 @@ namespace CodeIgniter\Database\DataConverter\Cast;
use TypeError;
/**
- * Class BaseCast
+ * @template TDbColumn
+ * @template TPhpValue
+ *
+ * @implements CastInterface<TDbColumn, TPhpValue>
*/
abstract class BaseCast implements CastInterface
{
- /**
- * {@inheritDoc}
- */
- public static function fromDatabase($value, array $params = [])
+ public static function fromDatabase(mixed $value, array $params = []): mixed
{
return $value;
}
- /**
- * {@inheritDoc}
- */
- public static function toDatabase($value, array $params = [])
+ public static function toDatabase(mixed $value, array $params = []): mixed
{
return $value;
}
/**
- * Throws TypeError
+ * @throws TypeError
*/
protected static function invalidTypeValueError(mixed $value): never
{ diff --git a/system/Database/DataConverter/Cast/ArrayCast.php b/system/Database/DataConverter/Cast/ArrayCast.php
index 4b41d9367d..3f0e517289 100644
--- a/system/Database/DataConverter/Cast/ArrayCast.php
+++ b/system/Database/DataConverter/Cast/ArrayCast.php
@@ -15,13 +15,12 @@ namespace CodeIgniter\Database\DataConverter\Cast;
* Class ArrayCast
*
* DB column: string <--> PHP: array
+ *
+ * @extends BaseCast<string, mixed[]>
*/
class ArrayCast extends BaseCast implements CastInterface
{
- /**
- * {@inheritDoc}
- */
- public static function fromDatabase($value, array $params = []): array
+ public static function fromDatabase(mixed $value, array $params = []): array
{
if (! is_string($value)) {
self::invalidTypeValueError($value);
@@ -34,10 +33,7 @@ class ArrayCast extends BaseCast implements CastInterface
return (array) $value;
}
- /**
- * {@inheritDoc}
- */
- public static function toDatabase($value, array $params = []): string
+ public static function toDatabase(mixed $value, array $params = []): string
{
return serialize($value);
} and tested with this script: <?php
use CodeIgniter\Database\DataConverter\Cast\ArrayCast;
ArrayCast::fromDatabase([]); resulted in: $ vendor/bin/phpstan analyse -v test.php
Note: Using configuration file C:\Users\P\Desktop\Web Dev\CodeIgniter4\phpstan.neon.
1/1 [============================] 100% 2 secs
------ -------------------------------------------------------------------------------------------------------------------------------------
Line test.php
------ -------------------------------------------------------------------------------------------------------------------------------------
:5 Parameter #1 $value of static method CodeIgniter\Database\DataConverter\Cast\ArrayCast::fromDatabase() expects string, array given.
✏️ test.php:5
------ -------------------------------------------------------------------------------------------------------------------------------------
[ERROR] Found 1 error
Used memory: 114 MB |
Hmm... Looks like a truncated What is the plan for this? A part of the Entity class rewrite or an entirely separate thing? As it stands, it is moderately useful. Sure - it works, but if I have to manually declare types every time, I'll start pulling my hair out. I would like to declare types one time and then reuse them for the data I work with. Just like in the Entity class. I'm really curious about a plan for this class. |
cd9967c
to
694c4b5
Compare
@kenjis IMO the solution for Entity is one and simple, as long as everyone will agree on it. The values provided to the Entity class should be casted right away, when creating an instance and then treated as an original data. From what you're saying DataConverter class looks like an additional option that we will be able to use instead of the Entity class. Will the final Entity class deprecate the cast option and use the input values provided by the DataConverter class? Because if not, I don't see how this will help resolve the issues with an Entity class. I'd like to know a little more concrete plan for what changes you want to make before we add another layer to convert the data. Right now I can't decide if I like the potential level of complexity the new layer adds. |
I realize I'm out of touch here, but this does seem like an overly complex solution to the original issue. And definitely doesn't seem to follow the old CodeIgniter way of attempting to keep things simple. This problem has been solved before by other frameworks. Have we looked at them to see how they've solved it as inspiration? Sorry, without diving into the code deeper right now I don't have any advice on solutions but watching the email threads got me a little concerned we're slapping a complex bandaid on an issue that can be solved in a simpler fashion somewhere else. |
That is what I'm trying to do. |
The big picture at this point looks like this.
|
@lonnieezell Thank you for your comment! I don't think this is overly complex. The I have no idea what the solution in other frameworks means. I would appreciate it if you could give me some concrete examples. At the very least, in Laravel the model has casting functionality. |
Thank you @kenjis for this enhancement. I asked for this feature some time ago.
class ResourcesMeta
{
private static $resources = [
"user_activity" => [
"exposedFields" => ["*"],
"types" => [
"number" => ["id", "userId"],
"json" => ["data"],
],
],
"user_balance_history" => [
"exposedFields" => ["*"],
"types" => [
"number" => ["id", "userId", "balanceAfter", "balanceBefore", "walletId"],
"json" => ["data"],
],
],
|
7232b09
to
ddd9282
Compare
Psalm does not support class @template in static methods. And in PHPStan it does not protect. See - vimeo/psalm#2697 - phpstan/phpstan#2738
d97f05a
to
7199ac4
Compare
In my opinion, this PR is ready to merge. This PR alone does not change anything in behavior, but the next PR #8243 will allow casting in Model. The cast classes are in Entity and DataCaster, but both are needed because they are used in different purposes (timing) and Entity cast classes are loose, but DataCaster cast classes are strict. Review when you have time. |
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.
LGTM!
Description
Supersedes #7995
DataConverter
class to convert column valuesDataCaster
class to cast valuesEntity casting works at (1)(4), but DataConverter casting works at (2)(3).
Example:
Checklist: