Uh oh!
There was an error while loading. Please reload this page.
feat: add DataConverter to convert types - #8230
Conversation
4cad258 to
885396eCompare21aba18 to
26dbad2Compare
paulbalandan
left a comment
There was a problem hiding this comment.
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->Ufor thefromDatabasemethod andU->TfortoDatabasemethod. That way we can have static analysers come into play? What do you think?
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
kenjis
commented
Nov 20, 2023
The current Entity has problems.
|
kenjis
commented
Nov 20, 2023
|
26dbad2 to
cd9967cComparepaulbalandan
commented
Nov 20, 2023
I agree. Entities could have been value objects only. |
paulbalandan
commented
Nov 20, 2023
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: <?phpuseCodeIgniter\Database\DataConverter\Cast\ArrayCast;
ArrayCast::fromDatabase([]);resulted in: $ vendor/bin/phpstan analyse -v test.phpNote: 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 |
michalsn
commented
Nov 20, 2023
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. |
Uh oh!
There was an error while loading. Please reload this page.
cd9967c to
694c4b5Comparekenjis
commented
Nov 22, 2023
michalsn
commented
Nov 22, 2023
@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. |
lonnieezell
commented
Nov 22, 2023
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. |
kenjis
commented
Nov 22, 2023
That is what I'm trying to do. |
The big picture at this point looks like this.
|
kenjis
commented
Nov 22, 2023
@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
{
privatestatic$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
ddd9282ComparePsalm does not support class @template in static methods. And in PHPStan it does not protect. See - vimeo/psalm#2697 - phpstan/phpstan#2738
d97f05a to
7199ac4Comparekenjis
commented
Jan 29, 2024
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. |
Description
Supersedes #7995
DataConverterclass to convert column valuesDataCasterclass to cast valuesEntity casting works at (1)(4), but DataConverter casting works at (2)(3).
Example:
Checklist: