aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2024-08-19 16:43:17 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2024-10-17 18:31:42 +0200
commitee02e3246dc18a6390cd9fbfd7b66f441e71ccc1 (patch)
tree3ebd0a84988aa677e6d8ffc6a057079a4140c6fc /lib/public
parent40fd76f69e601ab5579e8ce9b81318d3924dd463 (diff)
downloadnextcloud-server-ee02e3246dc18a6390cd9fbfd7b66f441e71ccc1.tar.gz
nextcloud-server-ee02e3246dc18a6390cd9fbfd7b66f441e71ccc1.zip
feat(AppFramework): Add full support for date / time / datetime columns
This adds support for all Doctrine supported types, for the column types only the immutable variants needed to be added. But especially those types are the important ones, as our **Entity** class works by detecting changes through setters. Meaning if it is mutable, changes like `$entity->date->modfiy()` can not be detected, so the immutable types make more sense here. Similar the parameter types needed to be added. `Enity` and `QBMapper` needed to be adjusted so they support (auto map) those types, required when insert or update an entity. Also added more tests, especially to make sure the mapper really serializes the values correctly. Co-authored-by: Ferdinand Thiessen <opensource@fthiessen.de> Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'lib/public')
-rw-r--r--lib/public/AppFramework/Db/Entity.php49
-rw-r--r--lib/public/AppFramework/Db/QBMapper.php28
-rw-r--r--lib/public/DB/QueryBuilder/IQueryBuilder.php46
-rw-r--r--lib/public/DB/Types.php77
-rw-r--r--lib/public/Migration/Attributes/ColumnType.php48
5 files changed, 204 insertions, 44 deletions
diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php
index f37107ac128..882902a212e 100644
--- a/lib/public/AppFramework/Db/Entity.php
+++ b/lib/public/AppFramework/Db/Entity.php
@@ -7,6 +7,8 @@
*/
namespace OCP\AppFramework\Db;
+use OCP\DB\Types;
+
use function lcfirst;
use function substr;
@@ -102,33 +104,38 @@ abstract class Entity {
// if type definition exists, cast to correct type
if ($args[0] !== null && array_key_exists($name, $this->_fieldTypes)) {
$type = $this->_fieldTypes[$name];
- if ($type === 'blob') {
+ if ($type === Types::BLOB) {
// (B)LOB is treated as string when we read from the DB
if (is_resource($args[0])) {
$args[0] = stream_get_contents($args[0]);
}
- $type = 'string';
+ $type = Types::STRING;
}
- if ($type === 'datetime') {
- if (!$args[0] instanceof \DateTime) {
- $args[0] = new \DateTime($args[0]);
- }
- } elseif ($type === 'json') {
- if (!is_array($args[0])) {
- $args[0] = json_decode($args[0], true);
- }
- } else {
- $args[0] = match($type) {
- 'string' => (string)$args[0],
- 'bool', 'boolean', => (bool)$args[0],
- 'int', 'integer', => (int)$args[0],
- 'float' => (float)$args[0],
- 'double' => (float)$args[0],
- 'array' => (array)$args[0],
- 'object' => (object)$args[0],
- default => new \InvalidArgumentException()
- };
+ switch ($type) {
+ case Types::TIME:
+ case Types::DATE:
+ case Types::DATETIME:
+ case Types::DATETIME_TZ:
+ if (!$args[0] instanceof \DateTime) {
+ $args[0] = new \DateTime($args[0]);
+ }
+ break;
+ case Types::TIME_IMMUTABLE:
+ case Types::DATE_IMMUTABLE:
+ case Types::DATETIME_IMMUTABLE:
+ case Types::DATETIME_TZ_IMMUTABLE:
+ if (!$args[0] instanceof \DateTimeImmutable) {
+ $args[0] = new \DateTimeImmutable($args[0]);
+ }
+ break;
+ case Types::JSON:
+ if (!is_array($args[0])) {
+ $args[0] = json_decode($args[0], true);
+ }
+ break;
+ default:
+ settype($args[0], $type);
}
}
$this->$name = $args[0];
diff --git a/lib/public/AppFramework/Db/QBMapper.php b/lib/public/AppFramework/Db/QBMapper.php
index ef4516221e6..c0667d27111 100644
--- a/lib/public/AppFramework/Db/QBMapper.php
+++ b/lib/public/AppFramework/Db/QBMapper.php
@@ -10,6 +10,7 @@ namespace OCP\AppFramework\Db;
use Generator;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
use OCP\IDBConnection;
/**
@@ -218,18 +219,33 @@ abstract class QBMapper {
switch ($types[ $property ]) {
case 'int':
- case 'integer':
+ case Types::INTEGER:
+ case Types::SMALLINT:
return IQueryBuilder::PARAM_INT;
- case 'string':
+ case Types::STRING:
return IQueryBuilder::PARAM_STR;
case 'bool':
- case 'boolean':
+ case Types::BOOLEAN:
return IQueryBuilder::PARAM_BOOL;
- case 'blob':
+ case Types::BLOB:
return IQueryBuilder::PARAM_LOB;
- case 'datetime':
+ case Types::DATE:
return IQueryBuilder::PARAM_DATE;
- case 'json':
+ case Types::DATETIME:
+ return IQueryBuilder::PARAM_DATETIME;
+ case Types::DATETIME_TZ:
+ return IQueryBuilder::PARAM_DATETIME_TZ;
+ case Types::DATE_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATE_IMMUTABLE;
+ case Types::DATETIME_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATETIME_IMMUTABLE;
+ case Types::DATETIME_TZ_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE;
+ case Types::TIME:
+ return IQueryBuilder::PARAM_TIME;
+ case Types::TIME_IMMUTABLE:
+ return IQueryBuilder::PARAM_TIME_IMMUTABLE;
+ case Types::JSON:
return IQueryBuilder::PARAM_JSON;
}
diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php
index 72b2ccbecff..1ff2d4959c5 100644
--- a/lib/public/DB/QueryBuilder/IQueryBuilder.php
+++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php
@@ -42,10 +42,54 @@ interface IQueryBuilder {
* @since 9.0.0
*/
public const PARAM_LOB = ParameterType::LARGE_OBJECT;
+
+ /**
+ * For passing a \DateTime instance when only interested in the time part (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_TIME = Types::TIME_MUTABLE;
+
/**
+ * For passing a \DateTime instance when only interested in the date part (without timezone support)
* @since 9.0.0
*/
- public const PARAM_DATE = 'datetime';
+ public const PARAM_DATE = Types::DATE_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME = Types::DATETIME_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance with timezone support
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_TZ = Types::DATETIMETZ_MUTABLE;
+
+ /**
+ * For passing a \DateTimeImmutable instance when only interested in the time part (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_TIME_IMMUTABLE = Types::TIME_MUTABLE;
+
+ /**
+ * For passing a \DateTime instance when only interested in the date part (without timezone support)
+ * @since 9.0.0
+ */
+ public const PARAM_DATE_IMMUTABLE = Types::DATE_IMMUTABLE;
+
+ /**
+ * For passing a \DateTime instance (without timezone support)
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_IMMUTABLE = Types::DATETIME_IMMUTABLE;
+
+ /**
+ * For passing a \DateTime instance with timezone support
+ * @since 31.0.0
+ */
+ public const PARAM_DATETIME_TZ_IMMUTABLE = Types::DATETIMETZ_IMMUTABLE;
/**
* @since 24.0.0
diff --git a/lib/public/DB/Types.php b/lib/public/DB/Types.php
index 414d81a24c8..969ec5e6611 100644
--- a/lib/public/DB/Types.php
+++ b/lib/public/DB/Types.php
@@ -41,18 +41,77 @@ final class Types {
public const BOOLEAN = 'boolean';
/**
+ * A datetime instance with only the date set.
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `DATE_IMMUTABLE` instead.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
* @var string
* @since 21.0.0
*/
public const DATE = 'date';
/**
+ * An immutable datetime instance with only the date set.
+ * This will be (de)serialized into a \DateTimeImmutable instance,
+ * It is recommended to use this over the `DATE` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATE_IMMUTABLE = 'date_immutable';
+
+ /**
+ * A datetime instance with date and time support.
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `DATETIME_IMMUTABLE` instead.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
* @var string
* @since 21.0.0
*/
public const DATETIME = 'datetime';
/**
+ * An immutable datetime instance with date and time set.
+ * This will be (de)serialized into a \DateTimeImmutable instance,
+ * It is recommended to use this over the `DATETIME` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATETIME_IMMUTABLE = 'datetime_immutable';
+
+
+ /**
+ * A datetime instance with timezone support
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `DATETIME_TZ_IMMUTABLE` instead.
+ *
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATETIME_TZ = 'datetimetz';
+
+ /**
+ * An immutable timezone aware datetime instance with date and time set.
+ * This will be (de)serialized into a \DateTimeImmutable instance,
+ * It is recommended to use this over the `DATETIME_TZ` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * @var string
+ * @since 31.0.0
+ */
+ public const DATETIME_TZ_IMMUTABLE = 'datetimetz_immutable';
+
+ /**
* @var string
* @since 21.0.0
*/
@@ -89,12 +148,30 @@ final class Types {
public const TEXT = 'text';
/**
+ * A datetime instance with only the time set.
+ * This will be (de)serialized into a \DateTime instance,
+ * it is recommended to instead use the `TIME_IMMUTABLE` instead.
+ *
+ * Warning: When deserialized the timezone will be set to UTC.
* @var string
* @since 21.0.0
*/
public const TIME = 'time';
/**
+ * A datetime instance with only the time set.
+ * This will be (de)serialized into a \DateTime instance.
+ *
+ * It is recommended to use this over the `DATETIME_TZ` type because
+ * out `Entity` class works detecting changes through the setter,
+ * changes on mutable objects can not be detected.
+ *
+ * @var string
+ * @since 31.0.0
+ */
+ public const TIME_IMMUTABLE = 'time_immutable';
+
+ /**
* @var string
* @since 24.0.0
*/
diff --git a/lib/public/Migration/Attributes/ColumnType.php b/lib/public/Migration/Attributes/ColumnType.php
index 23445e822b6..57bea920763 100644
--- a/lib/public/Migration/Attributes/ColumnType.php
+++ b/lib/public/Migration/Attributes/ColumnType.php
@@ -8,6 +8,8 @@ declare(strict_types=1);
*/
namespace OCP\Migration\Attributes;
+use OCP\DB\Types;
+
/**
* enum ColumnType based on OCP\DB\Types
*
@@ -16,31 +18,45 @@ namespace OCP\Migration\Attributes;
*/
enum ColumnType : string {
/** @since 30.0.0 */
- case BIGINT = 'bigint';
- /** @since 30.0.0 */
- case BINARY = 'binary';
- /** @since 30.0.0 */
- case BLOB = 'blob';
+ case BIGINT = Types::BIGINT;
/** @since 30.0.0 */
- case BOOLEAN = 'boolean';
+ case BINARY = Types::BINARY;
/** @since 30.0.0 */
- case DATE = 'date';
+ case BLOB = Types::BLOB;
/** @since 30.0.0 */
- case DATETIME = 'datetime';
+ case BOOLEAN = Types::BOOLEAN;
+ /**
+ * A column created with `DATE` can be used for both `DATE` and `DATE_IMMUTABLE`
+ * on the `\OCP\AppFramework\Db\Entity`.
+ * @since 30.0.0
+ */
+ case DATE = Types::DATE;
+ /**
+ * A column created with `DATETIME` can be used for both `DATETIME` and `DATETIME_IMMUTABLE`
+ * on the `\OCP\AppFramework\Db\Entity`.
+ * @since 30.0.0
+ */
+ case DATETIME = Types::DATETIME;
+ /**
+ * A column created with `DATETIME_TZ` can be used for both `DATETIME_TZ` and `DATETIME_TZ_IMMUTABLE`
+ * on the `\OCP\AppFramework\Db\Entity`.
+ * @since 31.0.0
+ */
+ case DATETIME_TZ = Types::DATETIME_TZ;
/** @since 30.0.0 */
- case DECIMAL = 'decimal';
+ case DECIMAL = Types::DECIMAL;
/** @since 30.0.0 */
- case FLOAT = 'float';
+ case FLOAT = Types::FLOAT;
/** @since 30.0.0 */
- case INTEGER = 'integer';
+ case INTEGER = Types::INTEGER;
/** @since 30.0.0 */
- case SMALLINT = 'smallint';
+ case SMALLINT = Types::SMALLINT;
/** @since 30.0.0 */
- case STRING = 'string';
+ case STRING = Types::STRING;
/** @since 30.0.0 */
- case TEXT = 'text';
+ case TEXT = Types::TEXT;
/** @since 30.0.0 */
- case TIME = 'time';
+ case TIME = Types::TIME;
/** @since 30.0.0 */
- case JSON = 'json';
+ case JSON = Types::JSON;
}