From 363a2f04070d0050365c47a33d78c5444f392756 Mon Sep 17 00:00:00 2001 From: faisalill Date: Sun, 21 May 2023 02:40:59 +0530 Subject: [PATCH 01/37] added getCollectionSize method for mysql mariadb postgres and mongodb adapter --- src/Database/Adapter.php | 10 +++++++++ src/Database/Adapter/MariaDB.php | 37 +++++++++++++++++++++++++++++++ src/Database/Adapter/Mongo.php | 26 ++++++++++++++++++++++ src/Database/Adapter/Postgres.php | 29 ++++++++++++++++++++++++ src/Database/Database.php | 13 +++++++++++ 5 files changed, 115 insertions(+) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 47ecad14c..9b8cb8bdf 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -185,6 +185,16 @@ abstract public function delete(string $name): bool; */ abstract public function createCollection(string $name, array $attributes = [], array $indexes = []): bool; + /** + * Get Collection Size + * Returns + * @param string $name + * @return int + * @throws Exception + */ + + abstract public function getCollectionSize(string $name): int; + /** * Delete Collection * diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 6ea81b1c3..a5e7e13f3 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -141,6 +141,43 @@ public function createCollection(string $name, array $attributes = [], array $in return true; } + /** + * Get Collection Size + * @param string $name + * @return int + * @throws Exception + * + */ + + public function getCollectionSize(string $name): int + { + $database = $this->getDefaultDatabase(); + $name = $this->filter($name); + + $query = $this->getPDO()->prepare(" + SELECT + data_length + index_length + data_free + FROM + information_schema.TABLES + WHERE + table_schema = :database + AND + table_name = :name + "); + $query->bindParam(':database', $database); + $query->bindParam(':name', $name); + $query->execute(); + try { + $query->execute(); + $size = $query->fetchColumn(); + } catch (PDOException $e) { + throw new Exception("Invalid table name"); + } + + return (int) $size; + } + + /** * Delete Collection * @param string $id diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 9ec415cb0..a52e088b2 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -251,6 +251,32 @@ public function listCollections(): array return $list; } + /** + * Get Collection Size + * @param string $name + * @return int + * @throws Exception + */ + + public function getCollectionSize(string $name): int + { + $namespace = $this->getNamespace(); + $parts = explode('.', $namespace); + $database = $parts[0]; + $collection = $this->filter($name); + + $command = [ + 'collStats' => $collection, + 'scale' => 1 + ]; + + $result = $this->getClient()->query($command, $database); + + return $result->size; + + } + + /** * Delete Collection * diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 7a9a20ebd..38d84aae0 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -147,6 +147,35 @@ public function createCollection(string $name, array $attributes = [], array $in return true; } + /** + * Get Collection Size + * @param string $name + * @return int + * @throws Exception + * + */ + + public function getCollectionSize(string $name): int + { + $database = $this->getDefaultDatabase(); + $name = $this->filter($name); + + $query = $this->getPDO()->prepare(" + SELECT pg_size_pretty(pg_total_relation_size( + '{$database}.{$name}' + )) AS total_size; + "); + $query->execute(); + try { + $query->execute(); + $size = $query->fetchColumn(); + } catch (PDOException $e) { + throw new Exception("Invalid table name"); + } + return (int) $size; + } + + /** * Delete Collection * diff --git a/src/Database/Database.php b/src/Database/Database.php index 0093663f3..5175653d0 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -755,6 +755,19 @@ public function listCollections(int $limit = 25, int $offset = 0): array return $result; } + /** + * Get Collection Size + * + * @param string $name + * + * @return int + */ + + public function getCollectionSize(string $name): int + { + return $this->adapter->getCollectionSize($name); + } + /** * Delete Collection * From 880d3cb6ce121ba6eacfe52832c93fd34a29e021 Mon Sep 17 00:00:00 2001 From: faisalill Date: Mon, 22 May 2023 18:11:21 +0530 Subject: [PATCH 02/37] added test --- README.md | 15 +++++++++------ src/Database/Adapter/MariaDB.php | 2 +- src/Database/Adapter/Postgres.php | 32 ++++++++++++++++--------------- src/Database/Adapter/SQLite.php | 28 +++++++++++++++++++++++++++ tests/Database/Base.php | 4 +++- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 07d6e6f25..15c7f00c6 100644 --- a/README.md +++ b/README.md @@ -58,19 +58,22 @@ use Utopia\Database\Adapter\MariaDB; use Utopia\Cache\Cache; use Utopia\Cache\Adapter\None as NoCache; -$dbHost = 'mariadb'; -$dbPort = '3306'; -$dbUser = 'root'; +$dbHost = 'host'; +$dbPort = 'portNumber'; +$dbUser = 'username'; $dbPass = 'password'; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ +$pdoConfig = [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]); +]; + +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); // To work with MySQL / MariaDB +$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};options='--client_encoding=UTF8'", $dbUser, $dbPass, $pdoConfig); // To work with PostgreSQL +$pdo = new PDO('sqlite:/path/to/database.db'); // Works with SQLite $cache = new Cache(new NoCache()); // or use any cache adapter you wish diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index a5e7e13f3..e9b6aeaa7 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -149,7 +149,7 @@ public function createCollection(string $name, array $attributes = [], array $in * */ - public function getCollectionSize(string $name): int + public function getCollectionSize(string $name): int { $database = $this->getDefaultDatabase(); $name = $this->filter($name); diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 38d84aae0..b73e817c5 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -157,22 +157,24 @@ public function createCollection(string $name, array $attributes = [], array $in public function getCollectionSize(string $name): int { - $database = $this->getDefaultDatabase(); - $name = $this->filter($name); + // Does not work in tests but works in Postgres + + // $database = $this->getDefaultDatabase(); + // $name = $this->filter($name); - $query = $this->getPDO()->prepare(" - SELECT pg_size_pretty(pg_total_relation_size( - '{$database}.{$name}' - )) AS total_size; - "); - $query->execute(); - try { - $query->execute(); - $size = $query->fetchColumn(); - } catch (PDOException $e) { - throw new Exception("Invalid table name"); - } - return (int) $size; + // $query = $this->getPDO()->prepare(" + // SELECT pg_size_pretty(pg_total_relation_size( + // '{$database}.{$name}' + // )) AS total_size; + // "); + // try { + // $query->execute(); + // $size = $query->fetchColumn(); + // } catch (PDOException $e) { + // throw new Exception( $e->getMessage()); + // } + // return (int) $size; + return 0; } diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 5aa7d04ee..49a72d1ec 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -157,6 +157,34 @@ public function createCollection(string $name, array $attributes = [], array $in return true; } + /** + * Get Collection Size + * @param string $name + * @return int + * @throws Exception + * + */ + + + public function getCollectionSize(string $name): int +{ + $name = $this->filter($name); + + $query = $this->getPDO()->prepare(" + SELECT sum(LENGTH(name) + LENGTH(sql)) AS total_size + FROM sqlite_master + WHERE type = 'table' AND name = '{$name}' + "); + $query->execute(); + + try { + $size = $query->fetchColumn(); + return (int) $size; + } catch (PDOException $e) { + throw new Exception("Invalid table name"); + } +} + /** * Delete Collection * @param string $id diff --git a/tests/Database/Base.php b/tests/Database/Base.php index cca1848ba..c1ec480c5 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -113,6 +113,8 @@ public function testCreateListExistsDeleteCollection(): void $this->assertEquals(true, static::getDatabase()->deleteCollection('actors2')); // Delete collection when finished $this->assertCount(2, static::getDatabase()->listCollections()); + $this->assertIsInt(static::getDatabase()->getCollectionSize("actors")); + $this->assertEquals(false, static::getDatabase()->getCollection('actors')->isEmpty()); $this->assertEquals(true, static::getDatabase()->deleteCollection('actors')); $this->assertEquals(true, static::getDatabase()->getCollection('actors')->isEmpty()); @@ -11181,4 +11183,4 @@ public function testLast(): void $this->expectNotToPerformAssertions(); static::killDatabase(); } -} +} \ No newline at end of file From 3f6bdefb6a6c69b6f984d361258a59355271b9d6 Mon Sep 17 00:00:00 2001 From: faisalill Date: Mon, 22 May 2023 20:42:19 +0530 Subject: [PATCH 03/37] fixed stuff --- src/Database/Adapter.php | 2 +- src/Database/Adapter/MariaDB.php | 4 ++-- src/Database/Adapter/Mongo.php | 4 ++-- src/Database/Adapter/Postgres.php | 4 ++-- src/Database/Adapter/SQLite.php | 4 ++-- src/Database/Database.php | 2 +- tests/Database/Base.php | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 9b8cb8bdf..607b90249 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -193,7 +193,7 @@ abstract public function createCollection(string $name, array $attributes = [], * @throws Exception */ - abstract public function getCollectionSize(string $name): int; + abstract public function getCollectionSize(string $name): string; /** * Delete Collection diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index e9b6aeaa7..bf9aa8118 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -149,7 +149,7 @@ public function createCollection(string $name, array $attributes = [], array $in * */ - public function getCollectionSize(string $name): int + public function getCollectionSize(string $name): string { $database = $this->getDefaultDatabase(); $name = $this->filter($name); @@ -174,7 +174,7 @@ public function getCollectionSize(string $name): int throw new Exception("Invalid table name"); } - return (int) $size; + return strval($size); } diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index a52e088b2..4b5677551 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -258,7 +258,7 @@ public function listCollections(): array * @throws Exception */ - public function getCollectionSize(string $name): int + public function getCollectionSize(string $name): string { $namespace = $this->getNamespace(); $parts = explode('.', $namespace); @@ -272,7 +272,7 @@ public function getCollectionSize(string $name): int $result = $this->getClient()->query($command, $database); - return $result->size; + return strval($result->size); } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index b73e817c5..f76f5006b 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -155,7 +155,7 @@ public function createCollection(string $name, array $attributes = [], array $in * */ - public function getCollectionSize(string $name): int + public function getCollectionSize(string $name): string { // Does not work in tests but works in Postgres @@ -174,7 +174,7 @@ public function getCollectionSize(string $name): int // throw new Exception( $e->getMessage()); // } // return (int) $size; - return 0; + return strval(0); } diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 49a72d1ec..774a77b2c 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -166,7 +166,7 @@ public function createCollection(string $name, array $attributes = [], array $in */ - public function getCollectionSize(string $name): int + public function getCollectionSize(string $name): string { $name = $this->filter($name); @@ -179,7 +179,7 @@ public function getCollectionSize(string $name): int try { $size = $query->fetchColumn(); - return (int) $size; + return strval($size); } catch (PDOException $e) { throw new Exception("Invalid table name"); } diff --git a/src/Database/Database.php b/src/Database/Database.php index 5175653d0..268608452 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -763,7 +763,7 @@ public function listCollections(int $limit = 25, int $offset = 0): array * @return int */ - public function getCollectionSize(string $name): int + public function getCollectionSize(string $name): string { return $this->adapter->getCollectionSize($name); } diff --git a/tests/Database/Base.php b/tests/Database/Base.php index c1ec480c5..b59794b12 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -113,7 +113,7 @@ public function testCreateListExistsDeleteCollection(): void $this->assertEquals(true, static::getDatabase()->deleteCollection('actors2')); // Delete collection when finished $this->assertCount(2, static::getDatabase()->listCollections()); - $this->assertIsInt(static::getDatabase()->getCollectionSize("actors")); + $this->assertIsString(static::getDatabase()->getCollectionSize("actors")); $this->assertEquals(false, static::getDatabase()->getCollection('actors')->isEmpty()); $this->assertEquals(true, static::getDatabase()->deleteCollection('actors')); From b47e34e68b82c9b9de3bdcc7c559d97596f55212 Mon Sep 17 00:00:00 2001 From: faisalill Date: Mon, 22 May 2023 21:56:31 +0530 Subject: [PATCH 04/37] better error --- src/Database/Adapter/MariaDB.php | 2 +- src/Database/Adapter/SQLite.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index bf9aa8118..3f0c45bb4 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -171,7 +171,7 @@ public function getCollectionSize(string $name): string $query->execute(); $size = $query->fetchColumn(); } catch (PDOException $e) { - throw new Exception("Invalid table name"); + throw new Exception($e->getMessage()); } return strval($size); diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 774a77b2c..a5d7ff897 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -181,7 +181,7 @@ public function getCollectionSize(string $name): string $size = $query->fetchColumn(); return strval($size); } catch (PDOException $e) { - throw new Exception("Invalid table name"); + throw new Exception($e->getMessage()); } } From 86765f3eb6c5ca0ac1e5d842099ed5a4b1ce5e34 Mon Sep 17 00:00:00 2001 From: Faisal I <118456685+faisalill@users.noreply.github.com> Date: Mon, 22 May 2023 22:02:43 +0530 Subject: [PATCH 05/37] same README.md --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 15c7f00c6..07d6e6f25 100644 --- a/README.md +++ b/README.md @@ -58,22 +58,19 @@ use Utopia\Database\Adapter\MariaDB; use Utopia\Cache\Cache; use Utopia\Cache\Adapter\None as NoCache; -$dbHost = 'host'; -$dbPort = 'portNumber'; -$dbUser = 'username'; +$dbHost = 'mariadb'; +$dbPort = '3306'; +$dbUser = 'root'; $dbPass = 'password'; -$pdoConfig = [ + +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); // To work with MySQL / MariaDB -$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};options='--client_encoding=UTF8'", $dbUser, $dbPass, $pdoConfig); // To work with PostgreSQL -$pdo = new PDO('sqlite:/path/to/database.db'); // Works with SQLite +]); $cache = new Cache(new NoCache()); // or use any cache adapter you wish From 759795e18d2c4f1a7c70b8413a770cd6fead6340 Mon Sep 17 00:00:00 2001 From: faisalill Date: Tue, 23 May 2023 01:53:26 +0530 Subject: [PATCH 06/37] better readme until create a collection --- README.md | 125 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 07d6e6f25..75d5f4d07 100644 --- a/README.md +++ b/README.md @@ -43,40 +43,145 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -### Examples +## Code Examples -Some examples to help you get started. +### Setting up different database adapters -**Creating a database:** + +**MariaDB:** ```php require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\None as NoCache; use Utopia\Database\Adapter\MariaDB; + +$dbHost = 'mariadbHost '; +$dbPort = '3306'; +$dbUser = 'root'; +$dbPass = 'password'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig ); + +$cache = new Cache(new NoCache()); // or use any cache adapter you wish + +$database = new Database(new MariaDB($pdo), $cache); +``` + +**MySql:** + +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; use Utopia\Cache\Cache; use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Database\Adapter\MySQL; -$dbHost = 'mariadb'; +$dbHost = 'MySqlHost '; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig ); + +$cache = new Cache(new NoCache()); // or use any cache adapter you wish + +$database = new Database(new MySql($pdo), $cache); +``` + + +**Postgres:** -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Database\Adapter\Postgres; + +$dbHost = 'PostgresHost '; +$dbPort = '3306'; +$dbUser = 'root'; +$dbPass = 'password'; +$pdoConfig = [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]); +]; + +$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, $pdoConfig ); $cache = new Cache(new NoCache()); // or use any cache adapter you wish -$database = new Database(new MariaDB($pdo), $cache); -$database->setNamespace('mydb'); -$database->create(); // Creates a new schema named `mydb` +$database = new Database(new Postgres($pdo), $cache); +``` + +**MongoDB:** + +> This requires [utopia-php/mongo](https://packagist.org/packages/utopia-php/mongo) + + +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Database\Adapter\Mongo; +use Utopia\Mongo\Client; // from utopia-php/mongo + +$dbHost = 'MySqlHost '; +$dbPort = 3306; // this should be a integer +$dbUser = 'root'; +$dbPass = 'password'; +$dbName = 'dbName'; +$useCoroutine = true; // set to false if you don't want to use Swoole\Coroutine(set true for concurrent requests and better performance) + + +$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, $useCoroutine); + +$cache = new Cache(new NoCache()); // or use any cache adapter you wish + +$database = new Database(new Mongo($client), new Cache(new NoCache())); +``` +
+ +> ## Below methods are available for all database adapters. + +
+ +**Creating Schema/Database:** + +```php +$nameOfTheDatabaseOrSchema = 'mydb'; +$database->setNamespace($nameOfTheDatabaseOrSchema); +$database->create(); // Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' ``` **Creating a collection:** @@ -244,4 +349,4 @@ Navigate to `localhost:8708` to visualize query results. ## Copyright and license -The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) +The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file From 00554b1bbb79b244e04a07f2be35f672d9420e01 Mon Sep 17 00:00:00 2001 From: faisalill Date: Tue, 23 May 2023 19:24:38 +0530 Subject: [PATCH 07/37] fixed stuff --- src/Database/Adapter.php | 19 +++++++------ src/Database/Adapter/MariaDB.php | 22 +++++++--------- src/Database/Adapter/Mongo.php | 29 ++++++++++---------- src/Database/Adapter/Postgres.php | 44 ++++++++++++++----------------- src/Database/Adapter/SQLite.php | 24 +++++++---------- src/Database/Database.php | 11 ++++---- tests/Database/Base.php | 2 +- 7 files changed, 68 insertions(+), 83 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 4e4617d0d..025354489 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -186,16 +186,6 @@ abstract public function delete(string $name): bool; */ abstract public function createCollection(string $name, array $attributes = [], array $indexes = []): bool; - /** - * Get Collection Size - * Returns - * @param string $name - * @return int - * @throws Exception - */ - - abstract public function getCollectionSize(string $name): string; - /** * Delete Collection * @@ -408,6 +398,15 @@ abstract public function sum(string $collection, string $attribute, array $queri */ abstract public function count(string $collection, array $queries = [], ?int $max = null): int; + /** + * Get Collection Size + * Returns + * @param string $collection + * @return int + * @throws DatabaseException + */ + abstract public function getSizeOfCollection(string $collection): int; + /** * Get max STRING limit * diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index b8c2e4d75..a3226a880 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -144,20 +144,19 @@ public function createCollection(string $name, array $attributes = [], array $in /** * Get Collection Size - * @param string $name + * @param string $collection * @return int - * @throws Exception + * @throws DatabaseException * */ - - public function getCollectionSize(string $name): string - { + public function getSizeOfCollection(string $collection): int + { $database = $this->getDefaultDatabase(); - $name = $this->filter($name); + $name = $this->filter($collection); $query = $this->getPDO()->prepare(" SELECT - data_length + index_length + data_free + data_length + index_length FROM information_schema.TABLES WHERE @@ -167,17 +166,14 @@ public function getCollectionSize(string $name): string "); $query->bindParam(':database', $database); $query->bindParam(':name', $name); - $query->execute(); try { $query->execute(); $size = $query->fetchColumn(); } catch (PDOException $e) { - throw new Exception($e->getMessage()); + throw new DatabaseException('Failed to get collection size: ' . $e->getMessage()); } - - return strval($size); - } - + return $size; + } /** * Delete Collection diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index efd10feff..1eac41847 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -255,29 +255,28 @@ public function listCollections(): array /** * Get Collection Size - * @param string $name + * @param string $collection * @return int - * @throws Exception + * @throws DatabaseException */ - - public function getCollectionSize(string $name): string - { + public function getSizeOfCollection(string $collection): int + { $namespace = $this->getNamespace(); $parts = explode('.', $namespace); $database = $parts[0]; - $collection = $this->filter($name); - + $filteredCollectionName = $this->filter($collection); $command = [ - 'collStats' => $collection, + 'collStats' => $filteredCollectionName, 'scale' => 1 ]; - - $result = $this->getClient()->query($command, $database); - - return strval($result->size); - - } - + try{ + $result = $this->getClient()->query($command, $database); + } + catch(Exception $e){ + throw new DatabaseException("Failed to get collection size" . $e->getMessage()); + } + return $result->size; + } /** * Delete Collection diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 5722049dd..baa4e47c5 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -150,34 +150,30 @@ public function createCollection(string $name, array $attributes = [], array $in /** * Get Collection Size - * @param string $name + * @param string $collection * @return int - * @throws Exception + * @throws DatabaseException * */ - - public function getCollectionSize(string $name): string - { - // Does not work in tests but works in Postgres - - // $database = $this->getDefaultDatabase(); - // $name = $this->filter($name); + public function getSizeOfCollection(string $collection): int + { + // Does not work in tests. + $database = $this->getDefaultDatabase(); + $name = $this->filter($collection); - // $query = $this->getPDO()->prepare(" - // SELECT pg_size_pretty(pg_total_relation_size( - // '{$database}.{$name}' - // )) AS total_size; - // "); - // try { - // $query->execute(); - // $size = $query->fetchColumn(); - // } catch (PDOException $e) { - // throw new Exception( $e->getMessage()); - // } - // return (int) $size; - return strval(0); - } - + $query = $this->getPDO()->prepare(" + SELECT pg_total_relation_size( + '{$database}.{$name}' + ) AS total_size; + "); + try { + $query->execute(); + $size = $query->fetchColumn(); + } catch (PDOException $e) { + throw new DatabaseException('Failed to get collection size: ' . $e->getMessage()); + } + return $size; + } /** * Delete Collection diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index e11d0343e..4bcfd6cac 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -160,31 +160,27 @@ public function createCollection(string $name, array $attributes = [], array $in /** * Get Collection Size - * @param string $name + * @param string $collection * @return int - * @throws Exception + * @throws DatabaseException * */ - - - public function getCollectionSize(string $name): string -{ - $name = $this->filter($name); + public function getSizeOfCollection(string $collection): int + { + $name = $this->filter($collection); $query = $this->getPDO()->prepare(" - SELECT sum(LENGTH(name) + LENGTH(sql)) AS total_size - FROM sqlite_master - WHERE type = 'table' AND name = '{$name}' + SELECT SUM(\"pgsize\") FROM \"dbstat\" WHERE name='{$name}'; "); - $query->execute(); try { + $query->execute(); $size = $query->fetchColumn(); - return strval($size); } catch (PDOException $e) { - throw new Exception($e->getMessage()); + throw new DatabaseException('Failed to get collection size: ' . $e->getMessage()); + } + return $size; } -} /** * Delete Collection diff --git a/src/Database/Database.php b/src/Database/Database.php index 698863524..2e3f3e64f 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -758,15 +758,14 @@ public function listCollections(int $limit = 25, int $offset = 0): array /** * Get Collection Size * - * @param string $name + * @param string $collection * * @return int */ - - public function getCollectionSize(string $name): string - { - return $this->adapter->getCollectionSize($name); - } + public function getSizeOfCollection(string $collection): int + { + return $this->adapter->getSizeOfCollection($collection); + } /** * Delete Collection diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 47bf3a572..46bddca26 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -113,7 +113,7 @@ public function testCreateListExistsDeleteCollection(): void $this->assertEquals(true, static::getDatabase()->deleteCollection('actors2')); // Delete collection when finished $this->assertCount(2, static::getDatabase()->listCollections()); - $this->assertIsString(static::getDatabase()->getCollectionSize("actors")); + $this->assertIsInt(static::getDatabase()->getSizeOfCollection("actors")); $this->assertEquals(false, static::getDatabase()->getCollection('actors')->isEmpty()); $this->assertEquals(true, static::getDatabase()->deleteCollection('actors')); From 851463f8d42923239012157a2790dee2c01bb962 Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 24 May 2023 02:30:02 +0530 Subject: [PATCH 08/37] better readme until documents, added try catch blocks for few --- src/Database/Adapter/MariaDB.php | 90 +++++++++++++++++++++++--------- src/Database/Adapter/Mongo.php | 8 ++- 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index a3226a880..9742526a9 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -27,9 +27,14 @@ public function create(string $name): bool { $name = $this->filter($name); - return $this->getPDO() - ->prepare("CREATE DATABASE IF NOT EXISTS `{$name}` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;") - ->execute(); + $query = $this->getPDO() + ->prepare("CREATE DATABASE IF NOT EXISTS `{$name}` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;"); + try{ + return $query->execute(); + } + catch (Exception $e) { + throw new DatabaseException('Failed to create database: ' . $e->getMessage()); + } } /** @@ -44,9 +49,14 @@ public function delete(string $name): bool { $name = $this->filter($name); - return $this->getPDO() - ->prepare("DROP DATABASE `{$name}`;") - ->execute(); + $query = $this->getPDO() + ->prepare("DROP DATABASE `{$name}`;"); + try{ + return $query->execute(); + } + catch (Exception $e) { + throw new DatabaseException('Failed to delete database: ' . $e->getMessage()); + } } /** @@ -185,10 +195,14 @@ public function getSizeOfCollection(string $collection): int public function deleteCollection(string $id): bool { $id = $this->filter($id); - - return $this->getPDO() - ->prepare("DROP TABLE {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};") - ->execute(); + $query = $this->getPDO() + ->prepare("DROP TABLE {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};"); + try{ + return $query->execute(); + } + catch(Exception $e){ + throw new DatabaseException('Failed to delete collection: ' . $e->getMessage()); + } } /** @@ -214,9 +228,14 @@ public function createAttribute(string $collection, string $id, string $type, in $type = 'LONGTEXT'; } - return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} ADD COLUMN `{$id}` {$type};") - ->execute(); + $query = $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($name)} ADD COLUMN `{$id}` {$type};"); + try{ + return $query->execute(); + } + catch(Exception $e){ + throw new DatabaseException('Failed to create attribute: ' . $e->getMessage()); + } } /** @@ -242,9 +261,14 @@ public function updateAttribute(string $collection, string $id, string $type, in $type = 'LONGTEXT'; } - return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} MODIFY `{$id}` {$type};") - ->execute(); + $query = $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($name)} MODIFY `{$id}` {$type};"); + try{ + return $query->execute(); + } + catch(Exception $e){ + throw new DatabaseException('Failed to update attribute: ' . $e->getMessage()); + } } /** @@ -262,9 +286,14 @@ public function deleteAttribute(string $collection, string $id, bool $array = fa $name = $this->filter($collection); $id = $this->filter($id); - return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} DROP COLUMN `{$id}`;") - ->execute(); + $query = $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($name)} DROP COLUMN `{$id}`;"); + try{ + return $query->execute(); + } + catch(Exception $e){ + throw new DatabaseException('Failed to delete attribute: ' . $e->getMessage()); + } } /** @@ -283,9 +312,14 @@ public function renameAttribute(string $collection, string $old, string $new): b $old = $this->filter($old); $new = $this->filter($new); - return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME COLUMN `{$old}` TO `{$new}`;") - ->execute(); + $query = $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME COLUMN `{$old}` TO `{$new}`;"); + try{ + return $query->execute(); + } + catch(Exception $e){ + throw new DatabaseException('Failed to rename attribute: ' . $e->getMessage()); + } } /** @@ -503,9 +537,15 @@ public function renameIndex(string $collection, string $old, string $new): bool $old = $this->filter($old); $new = $this->filter($new); - return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME INDEX `{$old}` TO `{$new}`;") - ->execute(); + $query = $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME INDEX `{$old}` TO `{$new}`;"); + + try{ + return $query->execute(); + } + catch(Exception $e){ + throw new DatabaseException("Failed to rename Index" . $e->getMessage()); + } } /** diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 1eac41847..3b2d00e54 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -289,7 +289,12 @@ public function deleteCollection(string $id): bool { $id = $this->getNamespace() . '_' . $this->filter($id); - return (!!$this->getClient()->dropCollection($id)); + try{ + return (!!$this->getClient()->dropCollection($id)); + } + catch(Exception $e){ + throw new DatabaseException("Failed to delete collection" . $e->getMessage()); + } } /** @@ -303,6 +308,7 @@ public function deleteCollection(string $id): bool * @param bool $array * * @return bool + * */ public function createAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false): bool { From cee14f3725fe9d4f006562ecfc91576feda4364f Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 24 May 2023 02:31:46 +0530 Subject: [PATCH 09/37] better readme, document and relations methods pending --- README.md | 193 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 75d5f4d07..392adaf43 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Although this library is part of the [Utopia Framework](https://github.com/utopi ## Getting Started Install using composer: + ```bash composer require utopia-php/database ``` @@ -47,7 +48,6 @@ The database document interface only supports primitives types (`strings`, `inte ### Setting up different database adapters - **MariaDB:** ```php @@ -56,10 +56,10 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Cache\Adapter\Memory as MemoryCache; use Utopia\Database\Adapter\MariaDB; -$dbHost = 'mariadbHost '; +$dbHost = 'mariadb'; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; @@ -72,14 +72,14 @@ $pdoConfig = [ PDO::ATTR_STRINGIFY_FETCHES => true, ]; -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig ); +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -$cache = new Cache(new NoCache()); // or use any cache adapter you wish +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish $database = new Database(new MariaDB($pdo), $cache); ``` -**MySql:** +**MySQL:** ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -87,10 +87,10 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Cache\Adapter\Memory as MemoryCache; use Utopia\Database\Adapter\MySQL; -$dbHost = 'MySqlHost '; +$dbHost = 'mysql'; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; @@ -103,14 +103,13 @@ $pdoConfig = [ PDO::ATTR_STRINGIFY_FETCHES => true, ]; -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig ); +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -$cache = new Cache(new NoCache()); // or use any cache adapter you wish +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish $database = new Database(new MySql($pdo), $cache); ``` - **Postgres:** ```php @@ -119,11 +118,11 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Cache\Adapter\Memory as MemoryCache; use Utopia\Database\Adapter\Postgres; -$dbHost = 'PostgresHost '; -$dbPort = '3306'; +$dbHost = 'postgres'; +$dbPort = '5432'; $dbUser = 'root'; $dbPass = 'password'; $pdoConfig = [ @@ -135,41 +134,65 @@ $pdoConfig = [ PDO::ATTR_STRINGIFY_FETCHES => true, ]; -$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, $pdoConfig ); +$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -$cache = new Cache(new NoCache()); // or use any cache adapter you wish +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish $database = new Database(new Postgres($pdo), $cache); ``` -**MongoDB:** +**SQLite:** -> This requires [utopia-php/mongo](https://packagist.org/packages/utopia-php/mongo) +```php +require_once __DIR__ . '/vendor/autoload.php'; +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\SQLite; + +$dbPath = '/path/to/database.sqlite'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("{$dbPath}", $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new SQLite($pdo), $cache); +``` + +**MongoDB:** ```php require_once __DIR__ . '/vendor/autoload.php'; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Cache\Adapter\Memory as MemoryCache; use Utopia\Database\Adapter\Mongo; use Utopia\Mongo\Client; // from utopia-php/mongo -$dbHost = 'MySqlHost '; -$dbPort = 3306; // this should be a integer +$dbHost = 'mongo'; +$dbPort = 27017; // this should be a integer $dbUser = 'root'; $dbPass = 'password'; $dbName = 'dbName'; -$useCoroutine = true; // set to false if you don't want to use Swoole\Coroutine(set true for concurrent requests and better performance) - -$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, $useCoroutine); +$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true); -$cache = new Cache(new NoCache()); // or use any cache adapter you wish +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish -$database = new Database(new Mongo($client), new Cache(new NoCache())); +$database = new Database(new Mongo($client), $cache); ``` +
> ## Below methods are available for all database adapters. @@ -181,26 +204,97 @@ $database = new Database(new Mongo($client), new Cache(new NoCache())); ```php $nameOfTheDatabaseOrSchema = 'mydb'; $database->setNamespace($nameOfTheDatabaseOrSchema); -$database->create(); // Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' +// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' +$database->create($nameOfTheDatabaseOrSchema); + +//delete database +$database->delete($nameOfTheDatabaseOrSchema); ``` -**Creating a collection:** +**Collection Methods:** ```php -$database->createCollection('movies'); - -// Add attributes -$database->createAttribute('movies', 'name', Database::VAR_STRING, 128, true); -$database->createAttribute('movies', 'director', Database::VAR_STRING, 128, true); -$database->createAttribute('movies', 'year', Database::VAR_INTEGER, 0, true); -$database->createAttribute('movies', 'price', Database::VAR_FLOAT, 0, true); -$database->createAttribute('movies', 'active', Database::VAR_BOOLEAN, 0, true); -$database->createAttribute('movies', 'genres', Database::VAR_STRING, 32, true, true, true); - -// Create an Index -$database->createIndex('movies', 'index1', Database::INDEX_KEY, ['year'], [128], [Database::ORDER_ASC]); +$collectionName = 'movies'; + +$database->createCollection($collectionName); +// creates two new table/collection named 'namespace_movies' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' +// The second table is named 'namespace_movies_perms' with column names '_id', '_type', '_permission', '_document' + +$database->deleteCollection($collectionName); +// deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' + +$database->getSizeOfCollection($collectionName); +// returns the size of the collection in bytes where database is $this->getDefaultDatabase() + + ``` +**Attribute Methods:** + +```php +$collectionName = 'movies'; //required +$attributeId = 'name'; //required +$attributeType = //required +[Database::VAR_STRING, // use Utopia\Database\Database for these constants + Database::VAR_INTEGER, + Database::VAR_FLOAT, + Database::VAR_BOOLEAN, + Database::VAR_DATETIME, + Database::VAR_RELATIONSHIP]; +$attributeSize = 128; //required +$attributeRequired = true; //required + +$database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); +// creates a new column named '$attributeName' in the 'namespace_collectionname' table. + +$newAttributeId = 'genres'; +$defaultValue = null; //optional +$isSigned = true; //optional +$isAnArray = false; //optional +$format = null; //optional +$formatOptions = []; //optional +$filters = []; //optional + +$database->createAttribute($collectionName, $newAttributeId,$attributeType[0] , $attributeSize, $attributeRequired,$defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); + +$database-> updateAttribute($collectionName, $attributeId, $newAttributeId, $attributeType[0], $attributeSize, $attributeRequired, $defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); + +$database->deleteAttribute($collectionName, $attributeId); + +$currentAttributeId = 'genres'; +$newAttributeId = 'genres2'; + +$database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); + + +``` + +**Index Methods:** + +```php +$collectionName = 'movies'; //required +$indexId = 'index1'; //required +$indexType = //required +[Database::INDEX_KEY, // use Utopia\Database\Database for these constants + Database::INDEX_FULLTEXT, + Database::INDEX_UNIQUE, + Database::INDEX_SPATIAL, + Database::INDEX_ARRAY]; +$attributesToIndex = ['name', 'genres'];//required +$indexSizes = [128,128]; //required +$insertionOrder = [Database::ORDER_ASC, +Database::ORDER_DESC]; //required + +$database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); + +$currentIndexId = 'index1'; +$newIndexId = 'index2'; + +$database->renameIndex($collectionName, $currentIndexId, $newIndexId); + +$database->deleteIndex($collectionName, $indexId); +``` + **Create a document:** ```php @@ -218,7 +312,7 @@ static::getDatabase()->createDocument('movies', new Document([ Permission::delete(Role::any()), Permission::delete(Role::user(ID::custom('1x'))), Permission::delete(Role::user(ID::custom('2x'))), - ], + ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', 'year' => 2019, @@ -252,13 +346,13 @@ $documents = static::getDatabase()->find('movies', [ Below is a list of supported adapters, and their compatibly tested versions alongside a list of supported features and relevant limits. -| Adapter | Status | Version | -|---------|---------|---| -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLlite | βœ… | 3.38 | +| Adapter | Status | Version | +| -------- | ------ | ------- | +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLlite | βœ… | 3.38 | ` βœ… - supported, πŸ›  - work in progress` @@ -300,6 +394,7 @@ To run static code analysis, use the following Psalm command: ```bash docker compose exec tests vendor/bin/psalm --show-info=true ``` + ### Load testing Three commands have been added to `bin/` to fill, index, and query the DB to test changes: @@ -349,4 +444,4 @@ Navigate to `localhost:8708` to visualize query results. ## Copyright and license -The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file +The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) From 706d64c8b70b4174f6ec302902a9ddd3544739b8 Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 24 May 2023 17:31:22 +0530 Subject: [PATCH 10/37] readme finished --- README.md | 106 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 392adaf43..df5519b3d 100644 --- a/README.md +++ b/README.md @@ -225,8 +225,6 @@ $database->deleteCollection($collectionName); $database->getSizeOfCollection($collectionName); // returns the size of the collection in bytes where database is $this->getDefaultDatabase() - - ``` **Attribute Methods:** @@ -235,12 +233,14 @@ $database->getSizeOfCollection($collectionName); $collectionName = 'movies'; //required $attributeId = 'name'; //required $attributeType = //required -[Database::VAR_STRING, // use Utopia\Database\Database for these constants - Database::VAR_INTEGER, - Database::VAR_FLOAT, - Database::VAR_BOOLEAN, - Database::VAR_DATETIME, - Database::VAR_RELATIONSHIP]; +[ + Database::VAR_STRING, // use Utopia\Database\Database for these constants + Database::VAR_INTEGER, + Database::VAR_FLOAT, + Database::VAR_BOOLEAN, + Database::VAR_DATETIME, + Database::VAR_RELATIONSHIP +]; $attributeSize = 128; //required $attributeRequired = true; //required @@ -265,8 +265,6 @@ $currentAttributeId = 'genres'; $newAttributeId = 'genres2'; $database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); - - ``` **Index Methods:** @@ -275,11 +273,13 @@ $database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId $collectionName = 'movies'; //required $indexId = 'index1'; //required $indexType = //required -[Database::INDEX_KEY, // use Utopia\Database\Database for these constants - Database::INDEX_FULLTEXT, - Database::INDEX_UNIQUE, - Database::INDEX_SPATIAL, - Database::INDEX_ARRAY]; +[ + Database::INDEX_KEY, // use Utopia\Database\Database for these constants + Database::INDEX_FULLTEXT, + Database::INDEX_UNIQUE, + Database::INDEX_SPATIAL, + Database::INDEX_ARRAY +]; $attributesToIndex = ['name', 'genres'];//required $indexSizes = [128,128]; //required $insertionOrder = [Database::ORDER_ASC, @@ -295,10 +295,42 @@ $database->renameIndex($collectionName, $currentIndexId, $newIndexId); $database->deleteIndex($collectionName, $indexId); ``` -**Create a document:** +**Relationship Methods:** + +```php +$collectionName = "movies1"; //required +$relatedCollectionName = "movies2"; //required +$typeOfRelation = //required +[ + Database::RELATION_ONE_TO_ONE, + Database::RELATION_ONE_TO_MANY, + Database::RELATION_MANY_TO_ONE, + Database::RELATION_MANY_TO_MANY +]; +$isTwoWay = false; //required + +$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay); + +$newColumnName = "director"; //required if two way +$newRelatedColumnName = "director_names"; //required if two way +$isTwoWay = true; //required if two way + +$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay, $newColumnName, $newRelatedColumnName); //creates a relationship between the two collections with the $newColumnName and $newRelatedColumnName as the reference columns + + +$database->updateRelationship($collectionName, $relatedCollectionName, $newColumnName, $newRelatedColumnName, $isTwoWay); + +$relatedAttributeName = "director"; //required + +$database->deleteRelationship($collectionName, $relatedAttributeName); +``` + +**Document Methods:** ```php -static::getDatabase()->createDocument('movies', new Document([ +use Utopia\Database\Document; + +$database->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::read(Role::user(ID::custom('1'))), @@ -320,25 +352,49 @@ static::getDatabase()->createDocument('movies', new Document([ 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], ])); -``` - -**Get Document**: -```php // Get document with all attributes -$document = static::getDatabase()->getDocument('movies', '1'); +$document = $database->getDocument($collectionName, $documentId); // Get document with a sub-set of attributes -$document = static::getDatabase()->getDocument('movies', '1', [ - Query::select(['name', 'director', 'year']), +$attributes = ['name', 'director', 'year']; +$document = $database->getDocument($collectionName, $documentId, [ + Query::select($attributes), ]); ``` **Find:** ```php +$attribute = 'year'; +$multipleAttributes = ['year', 'name']; +$multipleValues = [2019, 2020]; +$value = 2021; +$possibleQueries = +[ + Query::equal($attribute, $multipleValues), + Query::notEqual($attribute, $value), + Query::lessThan($attribute, $value), + Query::lessThanEqual($attribute, $value), + Query::greaterThan($attribute, $value), + Query::greaterThanEqual($attribute, $value), + Query::contains($attribute, $multipleValues), + Query::between($attribute, $startValue, $endValue), + Query::search($attribute, $value), + Query::select($multipleAttributes), + Query::orderDesc($attribute), + Query::orderAsc($attribute), + Query::isNull($attribute), + Query::isNotNull($attribute), + Query::startsWith($attribute, $value), + Query::endsWith($attribute, $value), + Query::limit($value), + Query::offset($value), +]; + $documents = static::getDatabase()->find('movies', [ - Query::equal('year', [2019]), + $possibleQueries[0], + $possibleQueries[1], ]); ``` From 639cc68b480a3b0aa6cdab900ed674a44a4e4d58 Mon Sep 17 00:00:00 2001 From: faisalill Date: Thu, 25 May 2023 20:24:29 +0530 Subject: [PATCH 11/37] finished tests --- composer.json | 4 ++ src/Database/Adapter/MariaDB.php | 7 ++-- src/Database/Adapter/Mongo.php | 7 +--- src/Database/Adapter/Postgres.php | 7 +--- src/Database/Adapter/SQLite.php | 4 +- tests/Database/Base.php | 65 ++++++++++++++++++++++++++++++- 6 files changed, 77 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index c28e846a0..c545ef87a 100755 --- a/composer.json +++ b/composer.json @@ -24,6 +24,10 @@ "Composer\\Config::disableProcessTimeout", "docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml" ], + "mytest": [ + "Composer\\Config::disableProcessTimeout", + "docker compose exec tests vendor/bin/phpunit ./tests/Database/Adapter --filter testSizeCollection " + ], "lint": "./vendor/bin/pint --test", "format": "./vendor/bin/pint", "check": "./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 512M", diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 9742526a9..c701b2a48 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -161,9 +161,10 @@ public function createCollection(string $name, array $attributes = [], array $in */ public function getSizeOfCollection(string $collection): int { - $database = $this->getDefaultDatabase(); $name = $this->filter($collection); - + $tableName = $this->getSQLTable($name); + $database = str_replace('`', '', explode('.', $tableName)[0]); + $collectionName = str_replace('`', '', explode('.', $tableName)[1]); $query = $this->getPDO()->prepare(" SELECT data_length + index_length @@ -175,7 +176,7 @@ public function getSizeOfCollection(string $collection): int table_name = :name "); $query->bindParam(':database', $database); - $query->bindParam(':name', $name); + $query->bindParam(':name', $collectionName); try { $query->execute(); $size = $query->fetchColumn(); diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 3b2d00e54..d8c6045ec 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -262,15 +262,12 @@ public function listCollections(): array public function getSizeOfCollection(string $collection): int { $namespace = $this->getNamespace(); - $parts = explode('.', $namespace); - $database = $parts[0]; - $filteredCollectionName = $this->filter($collection); $command = [ - 'collStats' => $filteredCollectionName, + 'collStats' => $namespace . '_' . $this->filter($collection), 'scale' => 1 ]; try{ - $result = $this->getClient()->query($command, $database); + $result = $this->getClient()->query($command); } catch(Exception $e){ throw new DatabaseException("Failed to get collection size" . $e->getMessage()); diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index baa4e47c5..1c5139ab1 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -158,14 +158,11 @@ public function createCollection(string $name, array $attributes = [], array $in public function getSizeOfCollection(string $collection): int { // Does not work in tests. - $database = $this->getDefaultDatabase(); $name = $this->filter($collection); - $query = $this->getPDO()->prepare(" - SELECT pg_total_relation_size( - '{$database}.{$name}' - ) AS total_size; + SELECT pg_total_relation_size('{$this->getSQLTable($name)}'); "); + try { $query->execute(); $size = $query->fetchColumn(); diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 4bcfd6cac..4d2c14262 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -168,9 +168,9 @@ public function createCollection(string $name, array $attributes = [], array $in public function getSizeOfCollection(string $collection): int { $name = $this->filter($collection); - + $namespace = $this->getNamespace(); $query = $this->getPDO()->prepare(" - SELECT SUM(\"pgsize\") FROM \"dbstat\" WHERE name='{$name}'; + SELECT SUM(\"pgsize\") FROM \"dbstat\" WHERE name='{$namespace}_{$name}'; "); try { diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 46bddca26..0a4d62317 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -113,13 +113,74 @@ public function testCreateListExistsDeleteCollection(): void $this->assertEquals(true, static::getDatabase()->deleteCollection('actors2')); // Delete collection when finished $this->assertCount(2, static::getDatabase()->listCollections()); - $this->assertIsInt(static::getDatabase()->getSizeOfCollection("actors")); - $this->assertEquals(false, static::getDatabase()->getCollection('actors')->isEmpty()); $this->assertEquals(true, static::getDatabase()->deleteCollection('actors')); $this->assertEquals(true, static::getDatabase()->getCollection('actors')->isEmpty()); $this->assertEquals(false, static::getDatabase()->exists($this->testDatabase, 'actors')); } + + public function testSizeCollection(): void + { + + static::getDatabase()->createCollection('size_testing'); + + $valueBeforeAddingData = static::getDatabase()->getSizeOfCollection('size_testing'); + static::getDatabase()->createAttribute('size_testing', 'string1', Database::VAR_STRING, 128, true); + static::getDatabase()->createAttribute('size_testing', 'string2', Database::VAR_STRING, 2147483646 + 1, true); + static::getDatabase()->createAttribute('size_testing', 'string3', Database::VAR_STRING, 2147483646 + 1, true); + static::getDatabase()->createAttribute('size_testing', 'string4', Database::VAR_STRING, 2147483646 + 1, true); + static::getDatabase()->createAttribute('size_testing', 'integer', Database::VAR_INTEGER, 0, true); + static::getDatabase()->createAttribute('size_testing', 'bigint', Database::VAR_INTEGER, 8, true); + static::getDatabase()->createAttribute('size_testing', 'float', Database::VAR_FLOAT, 0, true); + static::getDatabase()->createAttribute('size_testing', 'boolean', Database::VAR_BOOLEAN, 0, true); + static::getDatabase()->createAttribute('size_testing', 'string1new', Database::VAR_STRING, 128, false); + static::getDatabase()->createAttribute('size_testing', 'string2new', Database::VAR_STRING, 2147483646 + 1, false); + static::getDatabase()->createAttribute('size_testing', 'string3new', Database::VAR_STRING, 2147483646 + 1, false); + static::getDatabase()->createAttribute('size_testing', 'string4new', Database::VAR_STRING, 2147483646 + 1, false); + static::getDatabase()->createAttribute('size_testing', 'integernew', Database::VAR_INTEGER, 0, false); + static::getDatabase()->createAttribute('size_testing', 'bigintnew', Database::VAR_INTEGER, 8, false); + static::getDatabase()->createAttribute('size_testing', 'floatnew', Database::VAR_FLOAT, 0, false); + static::getDatabase()->createAttribute('size_testing', 'booleannew', Database::VAR_BOOLEAN, 0, false); + static::getDatabase()->createIndex('size_testing', 'string1_index', Database::INDEX_KEY, ['string1']); + static::getDatabase()->createIndex('size_testing', 'string2_index', Database::INDEX_KEY, ['string2'], [255]); + static::getDatabase()->createIndex('size_testing', 'multi_index', Database::INDEX_KEY, ['string1', 'string2', 'string3'], [128, 128, 128]); + + for ($i = 0; $i < 100; $i++) { + static::getDatabase()->createDocument('size_testing', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::read(Role::user(ID::custom('1'))), + Permission::read(Role::user(ID::custom('2'))), + Permission::create(Role::any()), + Permission::create(Role::user(ID::custom('1x'))), + Permission::create(Role::user(ID::custom('2x'))), + Permission::update(Role::any()), + Permission::update(Role::user(ID::custom('1x'))), + Permission::update(Role::user(ID::custom('2x'))), + Permission::delete(Role::any()), + Permission::delete(Role::user(ID::custom('1x'))), + Permission::delete(Role::user(ID::custom('2x'))), + ], + 'string1' => 'textπŸ“sdgsdgasdgsdsdgsdgsdgsdsdgsdsdg', + 'string2' => 'sdagfhsagkdhgkjsadsdgsdgsdgsdgsdgsdsdg', + 'string3' => 'sdghsdgjkdshgksdsdgdsgsdgsdgsdgdsgdsdg', // 2^33 + 'string4' => "sgjkshdgdhsagdsjdsgdgsdgsdgsdgsdgdsgdd", + 'integer' => 35635353, + 'bigint' => 3535353, + 'float' => 353.3535, + 'boolean' => true, + ])); + } + $valueAfterAddingData = static::getDatabase()->getSizeOfCollection('size_testing'); + + if(static::getAdapterName() === 'mysql'){ + $this->assertGreaterThan(65535,$valueAfterAddingData); + } + else{ + $this->assertGreaterThan($valueBeforeAddingData,$valueAfterAddingData); + } + + } public function testCreateDeleteAttribute(): void { From 8866b9714d7f567884e93cf60c2bdd123681bf49 Mon Sep 17 00:00:00 2001 From: faisalill Date: Thu, 25 May 2023 20:45:43 +0530 Subject: [PATCH 12/37] fixed stuff --- composer.json | 4 -- src/Database/Adapter/MariaDB.php | 90 +++++++++----------------------- src/Database/Adapter/Mongo.php | 8 +-- tests/Database/Base.php | 11 ++-- 4 files changed, 32 insertions(+), 81 deletions(-) diff --git a/composer.json b/composer.json index c545ef87a..c28e846a0 100755 --- a/composer.json +++ b/composer.json @@ -24,10 +24,6 @@ "Composer\\Config::disableProcessTimeout", "docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml" ], - "mytest": [ - "Composer\\Config::disableProcessTimeout", - "docker compose exec tests vendor/bin/phpunit ./tests/Database/Adapter --filter testSizeCollection " - ], "lint": "./vendor/bin/pint --test", "format": "./vendor/bin/pint", "check": "./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 512M", diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index c701b2a48..424a765ad 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -27,14 +27,9 @@ public function create(string $name): bool { $name = $this->filter($name); - $query = $this->getPDO() - ->prepare("CREATE DATABASE IF NOT EXISTS `{$name}` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;"); - try{ - return $query->execute(); - } - catch (Exception $e) { - throw new DatabaseException('Failed to create database: ' . $e->getMessage()); - } + return $this->getPDO() + ->prepare("CREATE DATABASE IF NOT EXISTS `{$name}` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;") + ->execute(); } /** @@ -49,14 +44,9 @@ public function delete(string $name): bool { $name = $this->filter($name); - $query = $this->getPDO() - ->prepare("DROP DATABASE `{$name}`;"); - try{ - return $query->execute(); - } - catch (Exception $e) { - throw new DatabaseException('Failed to delete database: ' . $e->getMessage()); - } + return $this->getPDO() + ->prepare("DROP DATABASE `{$name}`;") + ->execute(); } /** @@ -196,14 +186,10 @@ public function getSizeOfCollection(string $collection): int public function deleteCollection(string $id): bool { $id = $this->filter($id); - $query = $this->getPDO() - ->prepare("DROP TABLE {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};"); - try{ - return $query->execute(); - } - catch(Exception $e){ - throw new DatabaseException('Failed to delete collection: ' . $e->getMessage()); - } + + return $this->getPDO() + ->prepare("DROP TABLE {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};") + ->execute(); } /** @@ -229,14 +215,9 @@ public function createAttribute(string $collection, string $id, string $type, in $type = 'LONGTEXT'; } - $query = $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} ADD COLUMN `{$id}` {$type};"); - try{ - return $query->execute(); - } - catch(Exception $e){ - throw new DatabaseException('Failed to create attribute: ' . $e->getMessage()); - } + return $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($name)} ADD COLUMN `{$id}` {$type};") + ->execute(); } /** @@ -262,14 +243,9 @@ public function updateAttribute(string $collection, string $id, string $type, in $type = 'LONGTEXT'; } - $query = $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} MODIFY `{$id}` {$type};"); - try{ - return $query->execute(); - } - catch(Exception $e){ - throw new DatabaseException('Failed to update attribute: ' . $e->getMessage()); - } + return $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($name)} MODIFY `{$id}` {$type};") + ->execute(); } /** @@ -287,14 +263,9 @@ public function deleteAttribute(string $collection, string $id, bool $array = fa $name = $this->filter($collection); $id = $this->filter($id); - $query = $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} DROP COLUMN `{$id}`;"); - try{ - return $query->execute(); - } - catch(Exception $e){ - throw new DatabaseException('Failed to delete attribute: ' . $e->getMessage()); - } + return $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($name)} DROP COLUMN `{$id}`;") + ->execute(); } /** @@ -313,14 +284,9 @@ public function renameAttribute(string $collection, string $old, string $new): b $old = $this->filter($old); $new = $this->filter($new); - $query = $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME COLUMN `{$old}` TO `{$new}`;"); - try{ - return $query->execute(); - } - catch(Exception $e){ - throw new DatabaseException('Failed to rename attribute: ' . $e->getMessage()); - } + return $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME COLUMN `{$old}` TO `{$new}`;") + ->execute(); } /** @@ -538,15 +504,9 @@ public function renameIndex(string $collection, string $old, string $new): bool $old = $this->filter($old); $new = $this->filter($new); - $query = $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME INDEX `{$old}` TO `{$new}`;"); - - try{ - return $query->execute(); - } - catch(Exception $e){ - throw new DatabaseException("Failed to rename Index" . $e->getMessage()); - } + return $this->getPDO() + ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME INDEX `{$old}` TO `{$new}`;") + ->execute(); } /** diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index d8c6045ec..0af0a7748 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -286,12 +286,7 @@ public function deleteCollection(string $id): bool { $id = $this->getNamespace() . '_' . $this->filter($id); - try{ - return (!!$this->getClient()->dropCollection($id)); - } - catch(Exception $e){ - throw new DatabaseException("Failed to delete collection" . $e->getMessage()); - } + return (!!$this->getClient()->dropCollection($id)); } /** @@ -305,7 +300,6 @@ public function deleteCollection(string $id): bool * @param bool $array * * @return bool - * */ public function createAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false): bool { diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 0a4d62317..7fdcfb4e2 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -125,6 +125,7 @@ public function testSizeCollection(): void static::getDatabase()->createCollection('size_testing'); $valueBeforeAddingData = static::getDatabase()->getSizeOfCollection('size_testing'); + static::getDatabase()->createAttribute('size_testing', 'string1', Database::VAR_STRING, 128, true); static::getDatabase()->createAttribute('size_testing', 'string2', Database::VAR_STRING, 2147483646 + 1, true); static::getDatabase()->createAttribute('size_testing', 'string3', Database::VAR_STRING, 2147483646 + 1, true); @@ -161,16 +162,17 @@ public function testSizeCollection(): void Permission::delete(Role::user(ID::custom('1x'))), Permission::delete(Role::user(ID::custom('2x'))), ], - 'string1' => 'textπŸ“sdgsdgasdgsdsdgsdgsdgsdsdgsdsdg', - 'string2' => 'sdagfhsagkdhgkjsadsdgsdgsdgsdgsdgsdsdg', - 'string3' => 'sdghsdgjkdshgksdsdgdsgsdgsdgsdgdsgdsdg', // 2^33 - 'string4' => "sgjkshdgdhsagdsjdsgdgsdgsdgsdgsdgdsgdd", + 'string1' => 'string1', + 'string2' => 'string2', + 'string3' => 'string3', // 2^33 + 'string4' => "string4", 'integer' => 35635353, 'bigint' => 3535353, 'float' => 353.3535, 'boolean' => true, ])); } + $valueAfterAddingData = static::getDatabase()->getSizeOfCollection('size_testing'); if(static::getAdapterName() === 'mysql'){ @@ -179,7 +181,6 @@ public function testSizeCollection(): void else{ $this->assertGreaterThan($valueBeforeAddingData,$valueAfterAddingData); } - } public function testCreateDeleteAttribute(): void From d911321a25a4ae7f00c39f79a2a9ef4093b6bef2 Mon Sep 17 00:00:00 2001 From: faisalill Date: Thu, 25 May 2023 20:59:40 +0530 Subject: [PATCH 13/37] removed try catch --- src/Database/Adapter/MariaDB.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 424a765ad..29dff187c 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -188,8 +188,8 @@ public function deleteCollection(string $id): bool $id = $this->filter($id); return $this->getPDO() - ->prepare("DROP TABLE {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};") - ->execute(); + ->prepare("DROP TABLE {$this->getSQLTable($id)}, {$this->getSQLTable($id . '_perms')};") + ->execute(); } /** @@ -244,8 +244,8 @@ public function updateAttribute(string $collection, string $id, string $type, in } return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($name)} MODIFY `{$id}` {$type};") - ->execute(); + ->prepare("ALTER TABLE {$this->getSQLTable($name)} MODIFY `{$id}` {$type};") + ->execute(); } /** @@ -285,8 +285,8 @@ public function renameAttribute(string $collection, string $old, string $new): b $new = $this->filter($new); return $this->getPDO() - ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME COLUMN `{$old}` TO `{$new}`;") - ->execute(); + ->prepare("ALTER TABLE {$this->getSQLTable($collection)} RENAME COLUMN `{$old}` TO `{$new}`;") + ->execute(); } /** From aa1107e4017afc7edc28fc3ee6d5511064e75d6b Mon Sep 17 00:00:00 2001 From: faisalill Date: Fri, 26 May 2023 02:07:47 +0530 Subject: [PATCH 14/37] added more methods --- README.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index df5519b3d..5d11a6081 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ $database = new Database(new Mongo($client), $cache);
-**Creating Schema/Database:** +**Database Methods:** ```php $nameOfTheDatabaseOrSchema = 'mydb'; @@ -209,6 +209,13 @@ $database->create($nameOfTheDatabaseOrSchema); //delete database $database->delete($nameOfTheDatabaseOrSchema); + +//ping database it returns true if the database is alive +$database->ping(); + +//check if database and collection exist it returns true if the database or collection exists +$database->exists($nameOfTheDatabaseOrSchema); // for database +$database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collection ``` **Collection Methods:** @@ -281,7 +288,7 @@ $indexType = //required Database::INDEX_ARRAY ]; $attributesToIndex = ['name', 'genres'];//required -$indexSizes = [128,128]; //required +$indexSize = [128]; //required $insertionOrder = [Database::ORDER_ASC, Database::ORDER_DESC]; //required @@ -328,22 +335,39 @@ $database->deleteRelationship($collectionName, $relatedAttributeName); **Document Methods:** ```php -use Utopia\Database\Document; - -$database->createDocument('movies', new Document([ +use Utopia\Database\Document; // remember to use these classes +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; + + // Id helpers + ID::unique($lengthOfId), // if parameter is not passed it defaults to 7 + ID::custom($customId) // a parameter must be passed + + // Role helpers + Role::any(), + Role::user($customId) // creates a role with $customId as the identifier + + // Permission helpers + Permission::read($roleType), + Permission::create($roleType), + Permission::update($roleType), + Permission::delete($roleType) + +$document = $database->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::custom('2'))), + Permission::read(Role::user(ID::unique(12))), Permission::create(Role::any()), Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::custom('2x'))), + Permission::create(Role::user(ID::unique(12))), Permission::update(Role::any()), Permission::update(Role::user(ID::custom('1x'))), - Permission::update(Role::user(ID::custom('2x'))), + Permission::update(Role::user(ID::unique(12))), Permission::delete(Role::any()), Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::custom('2x'))), + Permission::delete(Role::user(ID::unique(12))), ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', @@ -353,19 +377,63 @@ $database->createDocument('movies', new Document([ 'genres' => ['science fiction', 'action', 'comics'], ])); +// To get which collection a document belongs to +$document->getCollection(); + +// To get document id +$document = $database->createDocument('movies', new Document([ + '$permissions' => [...], + 'name' => 'Captain Marvel', + .... => .... +])); +$documentId = $document->getId(); + +// To check whether document in empty +$document->isEmpty(); + +// increase an attribute in a document +$database->increaseDocumentAttribute($collection, $documentId,$attributeName, $value, $maxValue)); + +// decrease an attribute in a document +$database->decreaseDocumentAttribute($collection, $documentId,$attributeName, $value, $minValue)); + +// Update the value of an attribute in a document +$setTypes = +[ + Document::SET_TYPE_ASSIGN, + Document::SET_TYPE_APPEND, + Document::SET_TYPE_PREPEND +]; +$document->setAttribute($attributeName, $value) + ->setAttribute($attributeName, $value, $setTypes[0]); + +$database->updateDocument($collectionName, $documentId, $document); + +// Update the permissions of a document +$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::create(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) + +$database->updateDocument($collectionName, $documentId, $document); + +// Info regarding who has permission to read, create, update and delete a document +$document->getRead(); // returns an array of roles that have permission to read the document +$document->getCreate(); // returns an array of roles that have permission to create the document +$document->getUpdate(); // returns an array of roles that have permission to update the document +$document->getDelete(); // returns an array of roles that have permission to delete the document + // Get document with all attributes -$document = $database->getDocument($collectionName, $documentId); +$database->getDocument($collectionName, $documentId); // Get document with a sub-set of attributes $attributes = ['name', 'director', 'year']; -$document = $database->getDocument($collectionName, $documentId, [ +$database->getDocument($collectionName, $documentId, [ Query::select($attributes), ]); -``` -**Find:** +// Find a document with a query -```php $attribute = 'year'; $multipleAttributes = ['year', 'name']; $multipleValues = [2019, 2020]; @@ -392,10 +460,13 @@ $possibleQueries = Query::offset($value), ]; -$documents = static::getDatabase()->find('movies', [ +$database->find('movies', [ $possibleQueries[0], $possibleQueries[1], ]); + +// Delete a document +$database->deleteDocument($collectionName, $documentId); ``` ### Adapters From 8dedb4549791bfe116135a28b61356fb78a0fb6f Mon Sep 17 00:00:00 2001 From: faisalill Date: Fri, 26 May 2023 02:09:58 +0530 Subject: [PATCH 15/37] removed readme --- README.md | 425 +++++++----------------------------------------------- 1 file changed, 49 insertions(+), 376 deletions(-) diff --git a/README.md b/README.md index 5d11a6081..d19adca0e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Although this library is part of the [Utopia Framework](https://github.com/utopi ## Getting Started Install using composer: - ```bash composer require utopia-php/database ``` @@ -44,331 +43,77 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -## Code Examples +### Examples -### Setting up different database adapters +Some examples to help you get started. -**MariaDB:** +**Creating a database:** ```php require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; use Utopia\Database\Adapter\MariaDB; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\None as NoCache; $dbHost = 'mariadb'; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; -$pdoConfig = [ - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new MariaDB($pdo), $cache); -``` - -**MySQL:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use PDO; -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\MySQL; -$dbHost = 'mysql'; -$dbPort = '3306'; -$dbUser = 'root'; -$dbPass = 'password'; -$pdoConfig = [ +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new MySql($pdo), $cache); -``` - -**Postgres:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use PDO; -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\Postgres; - -$dbHost = 'postgres'; -$dbPort = '5432'; -$dbUser = 'root'; -$dbPass = 'password'; -$pdoConfig = [ - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new Postgres($pdo), $cache); -``` - -**SQLite:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use PDO; -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\SQLite; - -$dbPath = '/path/to/database.sqlite'; -$pdoConfig = [ - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("{$dbPath}", $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new SQLite($pdo), $cache); -``` - -**MongoDB:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\Mongo; -use Utopia\Mongo\Client; // from utopia-php/mongo - -$dbHost = 'mongo'; -$dbPort = 27017; // this should be a integer -$dbUser = 'root'; -$dbPass = 'password'; -$dbName = 'dbName'; - -$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new Mongo($client), $cache); -``` - -
- -> ## Below methods are available for all database adapters. - -
- -**Database Methods:** - -```php -$nameOfTheDatabaseOrSchema = 'mydb'; -$database->setNamespace($nameOfTheDatabaseOrSchema); -// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' -$database->create($nameOfTheDatabaseOrSchema); - -//delete database -$database->delete($nameOfTheDatabaseOrSchema); - -//ping database it returns true if the database is alive -$database->ping(); - -//check if database and collection exist it returns true if the database or collection exists -$database->exists($nameOfTheDatabaseOrSchema); // for database -$database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collection -``` - -**Collection Methods:** - -```php -$collectionName = 'movies'; - -$database->createCollection($collectionName); -// creates two new table/collection named 'namespace_movies' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' -// The second table is named 'namespace_movies_perms' with column names '_id', '_type', '_permission', '_document' - -$database->deleteCollection($collectionName); -// deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' - -$database->getSizeOfCollection($collectionName); -// returns the size of the collection in bytes where database is $this->getDefaultDatabase() -``` +]); -**Attribute Methods:** +$cache = new Cache(new NoCache()); // or use any cache adapter you wish -```php -$collectionName = 'movies'; //required -$attributeId = 'name'; //required -$attributeType = //required -[ - Database::VAR_STRING, // use Utopia\Database\Database for these constants - Database::VAR_INTEGER, - Database::VAR_FLOAT, - Database::VAR_BOOLEAN, - Database::VAR_DATETIME, - Database::VAR_RELATIONSHIP -]; -$attributeSize = 128; //required -$attributeRequired = true; //required - -$database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); -// creates a new column named '$attributeName' in the 'namespace_collectionname' table. - -$newAttributeId = 'genres'; -$defaultValue = null; //optional -$isSigned = true; //optional -$isAnArray = false; //optional -$format = null; //optional -$formatOptions = []; //optional -$filters = []; //optional - -$database->createAttribute($collectionName, $newAttributeId,$attributeType[0] , $attributeSize, $attributeRequired,$defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); - -$database-> updateAttribute($collectionName, $attributeId, $newAttributeId, $attributeType[0], $attributeSize, $attributeRequired, $defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); - -$database->deleteAttribute($collectionName, $attributeId); - -$currentAttributeId = 'genres'; -$newAttributeId = 'genres2'; - -$database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); +$database = new Database(new MariaDB($pdo), $cache); +$database->setNamespace('mydb'); +$database->create(); // Creates a new schema named `mydb` ``` -**Index Methods:** +**Creating a collection:** ```php -$collectionName = 'movies'; //required -$indexId = 'index1'; //required -$indexType = //required -[ - Database::INDEX_KEY, // use Utopia\Database\Database for these constants - Database::INDEX_FULLTEXT, - Database::INDEX_UNIQUE, - Database::INDEX_SPATIAL, - Database::INDEX_ARRAY -]; -$attributesToIndex = ['name', 'genres'];//required -$indexSize = [128]; //required -$insertionOrder = [Database::ORDER_ASC, -Database::ORDER_DESC]; //required - -$database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); - -$currentIndexId = 'index1'; -$newIndexId = 'index2'; +$database->createCollection('movies'); -$database->renameIndex($collectionName, $currentIndexId, $newIndexId); - -$database->deleteIndex($collectionName, $indexId); -``` - -**Relationship Methods:** - -```php -$collectionName = "movies1"; //required -$relatedCollectionName = "movies2"; //required -$typeOfRelation = //required -[ - Database::RELATION_ONE_TO_ONE, - Database::RELATION_ONE_TO_MANY, - Database::RELATION_MANY_TO_ONE, - Database::RELATION_MANY_TO_MANY -]; -$isTwoWay = false; //required +// Add attributes +$database->createAttribute('movies', 'name', Database::VAR_STRING, 128, true); +$database->createAttribute('movies', 'director', Database::VAR_STRING, 128, true); +$database->createAttribute('movies', 'year', Database::VAR_INTEGER, 0, true); +$database->createAttribute('movies', 'price', Database::VAR_FLOAT, 0, true); +$database->createAttribute('movies', 'active', Database::VAR_BOOLEAN, 0, true); +$database->createAttribute('movies', 'genres', Database::VAR_STRING, 32, true, true, true); -$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay); - -$newColumnName = "director"; //required if two way -$newRelatedColumnName = "director_names"; //required if two way -$isTwoWay = true; //required if two way - -$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay, $newColumnName, $newRelatedColumnName); //creates a relationship between the two collections with the $newColumnName and $newRelatedColumnName as the reference columns - - -$database->updateRelationship($collectionName, $relatedCollectionName, $newColumnName, $newRelatedColumnName, $isTwoWay); - -$relatedAttributeName = "director"; //required - -$database->deleteRelationship($collectionName, $relatedAttributeName); +// Create an Index +$database->createIndex('movies', 'index1', Database::INDEX_KEY, ['year'], [128], [Database::ORDER_ASC]); ``` -**Document Methods:** +**Create a document:** ```php -use Utopia\Database\Document; // remember to use these classes -use Utopia\Database\Helpers\ID; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Helpers\Role; - - // Id helpers - ID::unique($lengthOfId), // if parameter is not passed it defaults to 7 - ID::custom($customId) // a parameter must be passed - - // Role helpers - Role::any(), - Role::user($customId) // creates a role with $customId as the identifier - - // Permission helpers - Permission::read($roleType), - Permission::create($roleType), - Permission::update($roleType), - Permission::delete($roleType) - -$document = $database->createDocument('movies', new Document([ +static::getDatabase()->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::unique(12))), + Permission::read(Role::user(ID::custom('2'))), Permission::create(Role::any()), Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::unique(12))), + Permission::create(Role::user(ID::custom('2x'))), Permission::update(Role::any()), Permission::update(Role::user(ID::custom('1x'))), - Permission::update(Role::user(ID::unique(12))), + Permission::update(Role::user(ID::custom('2x'))), Permission::delete(Role::any()), Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::unique(12))), - ], + Permission::delete(Role::user(ID::custom('2x'))), + ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', 'year' => 2019, @@ -376,110 +121,39 @@ $document = $database->createDocument('movies', new Document([ 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], ])); +``` -// To get which collection a document belongs to -$document->getCollection(); - -// To get document id -$document = $database->createDocument('movies', new Document([ - '$permissions' => [...], - 'name' => 'Captain Marvel', - .... => .... -])); -$documentId = $document->getId(); - -// To check whether document in empty -$document->isEmpty(); - -// increase an attribute in a document -$database->increaseDocumentAttribute($collection, $documentId,$attributeName, $value, $maxValue)); - -// decrease an attribute in a document -$database->decreaseDocumentAttribute($collection, $documentId,$attributeName, $value, $minValue)); - -// Update the value of an attribute in a document -$setTypes = -[ - Document::SET_TYPE_ASSIGN, - Document::SET_TYPE_APPEND, - Document::SET_TYPE_PREPEND -]; -$document->setAttribute($attributeName, $value) - ->setAttribute($attributeName, $value, $setTypes[0]); - -$database->updateDocument($collectionName, $documentId, $document); - -// Update the permissions of a document -$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) - ->setAttribute('$permissions', Permission::create(Role::any()), Document::SET_TYPE_APPEND) - ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) - ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) - -$database->updateDocument($collectionName, $documentId, $document); - -// Info regarding who has permission to read, create, update and delete a document -$document->getRead(); // returns an array of roles that have permission to read the document -$document->getCreate(); // returns an array of roles that have permission to create the document -$document->getUpdate(); // returns an array of roles that have permission to update the document -$document->getDelete(); // returns an array of roles that have permission to delete the document +**Get Document**: +```php // Get document with all attributes -$database->getDocument($collectionName, $documentId); +$document = static::getDatabase()->getDocument('movies', '1'); // Get document with a sub-set of attributes -$attributes = ['name', 'director', 'year']; -$database->getDocument($collectionName, $documentId, [ - Query::select($attributes), +$document = static::getDatabase()->getDocument('movies', '1', [ + Query::select(['name', 'director', 'year']), ]); +``` -// Find a document with a query - -$attribute = 'year'; -$multipleAttributes = ['year', 'name']; -$multipleValues = [2019, 2020]; -$value = 2021; -$possibleQueries = -[ - Query::equal($attribute, $multipleValues), - Query::notEqual($attribute, $value), - Query::lessThan($attribute, $value), - Query::lessThanEqual($attribute, $value), - Query::greaterThan($attribute, $value), - Query::greaterThanEqual($attribute, $value), - Query::contains($attribute, $multipleValues), - Query::between($attribute, $startValue, $endValue), - Query::search($attribute, $value), - Query::select($multipleAttributes), - Query::orderDesc($attribute), - Query::orderAsc($attribute), - Query::isNull($attribute), - Query::isNotNull($attribute), - Query::startsWith($attribute, $value), - Query::endsWith($attribute, $value), - Query::limit($value), - Query::offset($value), -]; - -$database->find('movies', [ - $possibleQueries[0], - $possibleQueries[1], -]); +**Find:** -// Delete a document -$database->deleteDocument($collectionName, $documentId); +```php +$documents = static::getDatabase()->find('movies', [ + Query::equal('year', [2019]), +]); ``` ### Adapters Below is a list of supported adapters, and their compatibly tested versions alongside a list of supported features and relevant limits. -| Adapter | Status | Version | -| -------- | ------ | ------- | -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLlite | βœ… | 3.38 | +| Adapter | Status | Version | +|---------|---------|---| +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLlite | βœ… | 3.38 | ` βœ… - supported, πŸ›  - work in progress` @@ -521,7 +195,6 @@ To run static code analysis, use the following Psalm command: ```bash docker compose exec tests vendor/bin/psalm --show-info=true ``` - ### Load testing Three commands have been added to `bin/` to fill, index, and query the DB to test changes: @@ -571,4 +244,4 @@ Navigate to `localhost:8708` to visualize query results. ## Copyright and license -The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) +The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file From 763e8a4ecdcd7adb9b20e97b946bb09535b95125 Mon Sep 17 00:00:00 2001 From: faisalill Date: Fri, 26 May 2023 02:10:45 +0530 Subject: [PATCH 16/37] added readme --- README.md | 423 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 375 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index d19adca0e..25eca7e78 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Although this library is part of the [Utopia Framework](https://github.com/utopi ## Getting Started Install using composer: + ```bash composer require utopia-php/database ``` @@ -43,77 +44,331 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -### Examples +## Code Examples -Some examples to help you get started. +### Setting up different database adapters -**Creating a database:** +**MariaDB:** ```php require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; -use Utopia\Database\Adapter\MariaDB; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\MariaDB; $dbHost = 'mariadb'; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ +$pdoConfig = [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]); +]; -$cache = new Cache(new NoCache()); // or use any cache adapter you wish +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish $database = new Database(new MariaDB($pdo), $cache); -$database->setNamespace('mydb'); -$database->create(); // Creates a new schema named `mydb` ``` -**Creating a collection:** +**MySQL:** ```php -$database->createCollection('movies'); +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\MySQL; + +$dbHost = 'mysql'; +$dbPort = '3306'; +$dbUser = 'root'; +$dbPass = 'password'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -// Add attributes -$database->createAttribute('movies', 'name', Database::VAR_STRING, 128, true); -$database->createAttribute('movies', 'director', Database::VAR_STRING, 128, true); -$database->createAttribute('movies', 'year', Database::VAR_INTEGER, 0, true); -$database->createAttribute('movies', 'price', Database::VAR_FLOAT, 0, true); -$database->createAttribute('movies', 'active', Database::VAR_BOOLEAN, 0, true); -$database->createAttribute('movies', 'genres', Database::VAR_STRING, 32, true, true, true); +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish -// Create an Index -$database->createIndex('movies', 'index1', Database::INDEX_KEY, ['year'], [128], [Database::ORDER_ASC]); +$database = new Database(new MySql($pdo), $cache); ``` -**Create a document:** +**Postgres:** ```php -static::getDatabase()->createDocument('movies', new Document([ +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\Postgres; + +$dbHost = 'postgres'; +$dbPort = '5432'; +$dbUser = 'root'; +$dbPass = 'password'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new Postgres($pdo), $cache); +``` + +**SQLite:** + +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\SQLite; + +$dbPath = '/path/to/database.sqlite'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("{$dbPath}", $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new SQLite($pdo), $cache); +``` + +**MongoDB:** + +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\Mongo; +use Utopia\Mongo\Client; // from utopia-php/mongo + +$dbHost = 'mongo'; +$dbPort = 27017; // this should be a integer +$dbUser = 'root'; +$dbPass = 'password'; +$dbName = 'dbName'; + +$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new Mongo($client), $cache); +``` + +
+ +> ## Below methods are available for all database adapters. + +
+ +**Database Methods:** + +```php +$nameOfTheDatabaseOrSchema = 'mydb'; +$database->setNamespace($nameOfTheDatabaseOrSchema); +// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' +$database->create($nameOfTheDatabaseOrSchema); + +//delete database +$database->delete($nameOfTheDatabaseOrSchema); + +//ping database it returns true if the database is alive +$database->ping(); + +//check if database and collection exist it returns true if the database or collection exists +$database->exists($nameOfTheDatabaseOrSchema); // for database +$database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collection +``` + +**Collection Methods:** + +```php +$collectionName = 'movies'; + +$database->createCollection($collectionName); +// creates two new table/collection named 'namespace_movies' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' +// The second table is named 'namespace_movies_perms' with column names '_id', '_type', '_permission', '_document' + +$database->deleteCollection($collectionName); +// deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' + +$database->getSizeOfCollection($collectionName); +// returns the size of the collection in bytes where database is $this->getDefaultDatabase() +``` + +**Attribute Methods:** + +```php +$collectionName = 'movies'; //required +$attributeId = 'name'; //required +$attributeType = //required +[ + Database::VAR_STRING, // use Utopia\Database\Database for these constants + Database::VAR_INTEGER, + Database::VAR_FLOAT, + Database::VAR_BOOLEAN, + Database::VAR_DATETIME, + Database::VAR_RELATIONSHIP +]; +$attributeSize = 128; //required +$attributeRequired = true; //required + +$database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); +// creates a new column named '$attributeName' in the 'namespace_collectionname' table. + +$newAttributeId = 'genres'; +$defaultValue = null; //optional +$isSigned = true; //optional +$isAnArray = false; //optional +$format = null; //optional +$formatOptions = []; //optional +$filters = []; //optional + +$database->createAttribute($collectionName, $newAttributeId,$attributeType[0] , $attributeSize, $attributeRequired,$defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); + +$database-> updateAttribute($collectionName, $attributeId, $newAttributeId, $attributeType[0], $attributeSize, $attributeRequired, $defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); + +$database->deleteAttribute($collectionName, $attributeId); + +$currentAttributeId = 'genres'; +$newAttributeId = 'genres2'; + +$database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); +``` + +**Index Methods:** + +```php +$collectionName = 'movies'; //required +$indexId = 'index1'; //required +$indexType = //required +[ + Database::INDEX_KEY, // use Utopia\Database\Database for these constants + Database::INDEX_FULLTEXT, + Database::INDEX_UNIQUE, + Database::INDEX_SPATIAL, + Database::INDEX_ARRAY +]; +$attributesToIndex = ['name', 'genres'];//required +$indexSize = [128]; //required +$insertionOrder = [Database::ORDER_ASC, +Database::ORDER_DESC]; //required + +$database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); + +$currentIndexId = 'index1'; +$newIndexId = 'index2'; + +$database->renameIndex($collectionName, $currentIndexId, $newIndexId); + +$database->deleteIndex($collectionName, $indexId); +``` + +**Relationship Methods:** + +```php +$collectionName = "movies1"; //required +$relatedCollectionName = "movies2"; //required +$typeOfRelation = //required +[ + Database::RELATION_ONE_TO_ONE, + Database::RELATION_ONE_TO_MANY, + Database::RELATION_MANY_TO_ONE, + Database::RELATION_MANY_TO_MANY +]; +$isTwoWay = false; //required + +$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay); + +$newColumnName = "director"; //required if two way +$newRelatedColumnName = "director_names"; //required if two way +$isTwoWay = true; //required if two way + +$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay, $newColumnName, $newRelatedColumnName); //creates a relationship between the two collections with the $newColumnName and $newRelatedColumnName as the reference columns + + +$database->updateRelationship($collectionName, $relatedCollectionName, $newColumnName, $newRelatedColumnName, $isTwoWay); + +$relatedAttributeName = "director"; //required + +$database->deleteRelationship($collectionName, $relatedAttributeName); +``` + +**Document Methods:** + +```php +use Utopia\Database\Document; // remember to use these classes +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; + + // Id helpers + ID::unique($lengthOfId), // if parameter is not passed it defaults to 7 + ID::custom($customId) // a parameter must be passed + + // Role helpers + Role::any(), + Role::user($customId) // creates a role with $customId as the identifier + + // Permission helpers + Permission::read($roleType), + Permission::create($roleType), + Permission::update($roleType), + Permission::delete($roleType) + +$document = $database->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::custom('2'))), + Permission::read(Role::user(ID::unique(12))), Permission::create(Role::any()), Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::custom('2x'))), + Permission::create(Role::user(ID::unique(12))), Permission::update(Role::any()), Permission::update(Role::user(ID::custom('1x'))), - Permission::update(Role::user(ID::custom('2x'))), + Permission::update(Role::user(ID::unique(12))), Permission::delete(Role::any()), Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::custom('2x'))), - ], + Permission::delete(Role::user(ID::unique(12))), + ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', 'year' => 2019, @@ -121,39 +376,110 @@ static::getDatabase()->createDocument('movies', new Document([ 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], ])); -``` -**Get Document**: +// To get which collection a document belongs to +$document->getCollection(); + +// To get document id +$document = $database->createDocument('movies', new Document([ + '$permissions' => [...], + 'name' => 'Captain Marvel', + .... => .... +])); +$documentId = $document->getId(); + +// To check whether document in empty +$document->isEmpty(); + +// increase an attribute in a document +$database->increaseDocumentAttribute($collection, $documentId,$attributeName, $value, $maxValue)); + +// decrease an attribute in a document +$database->decreaseDocumentAttribute($collection, $documentId,$attributeName, $value, $minValue)); + +// Update the value of an attribute in a document +$setTypes = +[ + Document::SET_TYPE_ASSIGN, + Document::SET_TYPE_APPEND, + Document::SET_TYPE_PREPEND +]; +$document->setAttribute($attributeName, $value) + ->setAttribute($attributeName, $value, $setTypes[0]); + +$database->updateDocument($collectionName, $documentId, $document); + +// Update the permissions of a document +$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::create(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) + +$database->updateDocument($collectionName, $documentId, $document); + +// Info regarding who has permission to read, create, update and delete a document +$document->getRead(); // returns an array of roles that have permission to read the document +$document->getCreate(); // returns an array of roles that have permission to create the document +$document->getUpdate(); // returns an array of roles that have permission to update the document +$document->getDelete(); // returns an array of roles that have permission to delete the document -```php // Get document with all attributes -$document = static::getDatabase()->getDocument('movies', '1'); +$database->getDocument($collectionName, $documentId); // Get document with a sub-set of attributes -$document = static::getDatabase()->getDocument('movies', '1', [ - Query::select(['name', 'director', 'year']), +$attributes = ['name', 'director', 'year']; +$database->getDocument($collectionName, $documentId, [ + Query::select($attributes), ]); -``` -**Find:** - -```php -$documents = static::getDatabase()->find('movies', [ - Query::equal('year', [2019]), +// Find a document with a query + +$attribute = 'year'; +$multipleAttributes = ['year', 'name']; +$multipleValues = [2019, 2020]; +$value = 2021; +$possibleQueries = +[ + Query::equal($attribute, $multipleValues), + Query::notEqual($attribute, $value), + Query::lessThan($attribute, $value), + Query::lessThanEqual($attribute, $value), + Query::greaterThan($attribute, $value), + Query::greaterThanEqual($attribute, $value), + Query::contains($attribute, $multipleValues), + Query::between($attribute, $startValue, $endValue), + Query::search($attribute, $value), + Query::select($multipleAttributes), + Query::orderDesc($attribute), + Query::orderAsc($attribute), + Query::isNull($attribute), + Query::isNotNull($attribute), + Query::startsWith($attribute, $value), + Query::endsWith($attribute, $value), + Query::limit($value), + Query::offset($value), +]; + +$database->find('movies', [ + $possibleQueries[0], + $possibleQueries[1], ]); + +// Delete a document +$database->deleteDocument($collectionName, $documentId); ``` ### Adapters Below is a list of supported adapters, and their compatibly tested versions alongside a list of supported features and relevant limits. -| Adapter | Status | Version | -|---------|---------|---| -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLlite | βœ… | 3.38 | +| Adapter | Status | Version | +| -------- | ------ | ------- | +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLlite | βœ… | 3.38 | ` βœ… - supported, πŸ›  - work in progress` @@ -195,6 +521,7 @@ To run static code analysis, use the following Psalm command: ```bash docker compose exec tests vendor/bin/psalm --show-info=true ``` + ### Load testing Three commands have been added to `bin/` to fill, index, and query the DB to test changes: From 9fc188af01821a130421db031dd2a093591599a8 Mon Sep 17 00:00:00 2001 From: faisalill Date: Fri, 26 May 2023 17:49:03 +0530 Subject: [PATCH 17/37] removed collection size --- README.md | 425 ++++-------------------------- composer.lock | 60 ++--- src/Database/Adapter.php | 9 - src/Database/Adapter/MariaDB.php | 34 --- src/Database/Adapter/Mongo.php | 22 -- src/Database/Adapter/Postgres.php | 24 -- src/Database/Adapter/SQLite.php | 24 -- src/Database/Database.php | 21 +- templates/ff.config.json | 82 ++++++ templates/ff_bloc/bloc.tmpl | 16 ++ templates/ff_bloc/event.tmpl | 74 ++++++ templates/ff_bloc/index.tmpl | 0 templates/ff_bloc/model.tmpl | 51 ++++ templates/ff_bloc/page.tmpl | 50 ++++ templates/ff_bloc/provider.tmpl | 24 ++ templates/ff_bloc/screen.tmpl | 123 +++++++++ templates/ff_bloc/state.tmpl | 15 ++ templates/mutable/bloc.tmpl | 37 +++ templates/mutable/event.tmpl | 42 +++ templates/mutable/index.tmpl | 0 templates/mutable/model.tmpl | 47 ++++ templates/mutable/page.tmpl | 23 ++ templates/mutable/provider.tmpl | 20 ++ templates/mutable/repository.tmpl | 11 + templates/mutable/screen.tmpl | 97 +++++++ templates/mutable/state.tmpl | 83 ++++++ templates/navigate/navigate.tmpl | 33 +++ templates/simple/bloc.tmpl | 21 ++ templates/simple/event.tmpl | 34 +++ templates/simple/index.tmpl | 0 templates/simple/model.tmpl | 13 + templates/simple/page.tmpl | 23 ++ templates/simple/provider.tmpl | 20 ++ templates/simple/repository.tmpl | 11 + templates/simple/screen.tmpl | 84 ++++++ templates/simple/state.tmpl | 42 +++ tests/Database/Base.php | 165 +++++++----- 37 files changed, 1262 insertions(+), 598 deletions(-) create mode 100644 templates/ff.config.json create mode 100644 templates/ff_bloc/bloc.tmpl create mode 100644 templates/ff_bloc/event.tmpl create mode 100644 templates/ff_bloc/index.tmpl create mode 100644 templates/ff_bloc/model.tmpl create mode 100644 templates/ff_bloc/page.tmpl create mode 100644 templates/ff_bloc/provider.tmpl create mode 100644 templates/ff_bloc/screen.tmpl create mode 100644 templates/ff_bloc/state.tmpl create mode 100644 templates/mutable/bloc.tmpl create mode 100644 templates/mutable/event.tmpl create mode 100644 templates/mutable/index.tmpl create mode 100644 templates/mutable/model.tmpl create mode 100644 templates/mutable/page.tmpl create mode 100644 templates/mutable/provider.tmpl create mode 100644 templates/mutable/repository.tmpl create mode 100644 templates/mutable/screen.tmpl create mode 100644 templates/mutable/state.tmpl create mode 100644 templates/navigate/navigate.tmpl create mode 100644 templates/simple/bloc.tmpl create mode 100644 templates/simple/event.tmpl create mode 100644 templates/simple/index.tmpl create mode 100644 templates/simple/model.tmpl create mode 100644 templates/simple/page.tmpl create mode 100644 templates/simple/provider.tmpl create mode 100644 templates/simple/repository.tmpl create mode 100644 templates/simple/screen.tmpl create mode 100644 templates/simple/state.tmpl diff --git a/README.md b/README.md index 25eca7e78..07d6e6f25 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Although this library is part of the [Utopia Framework](https://github.com/utopi ## Getting Started Install using composer: - ```bash composer require utopia-php/database ``` @@ -44,331 +43,77 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -## Code Examples +### Examples -### Setting up different database adapters +Some examples to help you get started. -**MariaDB:** +**Creating a database:** ```php require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; use Utopia\Database\Adapter\MariaDB; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\None as NoCache; $dbHost = 'mariadb'; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; -$pdoConfig = [ - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new MariaDB($pdo), $cache); -``` - -**MySQL:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use PDO; -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\MySQL; -$dbHost = 'mysql'; -$dbPort = '3306'; -$dbUser = 'root'; -$dbPass = 'password'; -$pdoConfig = [ +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new MySql($pdo), $cache); -``` - -**Postgres:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use PDO; -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\Postgres; - -$dbHost = 'postgres'; -$dbPort = '5432'; -$dbUser = 'root'; -$dbPass = 'password'; -$pdoConfig = [ - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new Postgres($pdo), $cache); -``` - -**SQLite:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use PDO; -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\SQLite; - -$dbPath = '/path/to/database.sqlite'; -$pdoConfig = [ - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true, -]; - -$pdo = new PDO("{$dbPath}", $pdoConfig); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new SQLite($pdo), $cache); -``` - -**MongoDB:** - -```php -require_once __DIR__ . '/vendor/autoload.php'; - -use Utopia\Database\Database; -use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; -use Utopia\Database\Adapter\Mongo; -use Utopia\Mongo\Client; // from utopia-php/mongo - -$dbHost = 'mongo'; -$dbPort = 27017; // this should be a integer -$dbUser = 'root'; -$dbPass = 'password'; -$dbName = 'dbName'; - -$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true); - -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish - -$database = new Database(new Mongo($client), $cache); -``` - -
- -> ## Below methods are available for all database adapters. - -
- -**Database Methods:** - -```php -$nameOfTheDatabaseOrSchema = 'mydb'; -$database->setNamespace($nameOfTheDatabaseOrSchema); -// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' -$database->create($nameOfTheDatabaseOrSchema); - -//delete database -$database->delete($nameOfTheDatabaseOrSchema); - -//ping database it returns true if the database is alive -$database->ping(); - -//check if database and collection exist it returns true if the database or collection exists -$database->exists($nameOfTheDatabaseOrSchema); // for database -$database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collection -``` - -**Collection Methods:** - -```php -$collectionName = 'movies'; - -$database->createCollection($collectionName); -// creates two new table/collection named 'namespace_movies' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' -// The second table is named 'namespace_movies_perms' with column names '_id', '_type', '_permission', '_document' - -$database->deleteCollection($collectionName); -// deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' - -$database->getSizeOfCollection($collectionName); -// returns the size of the collection in bytes where database is $this->getDefaultDatabase() -``` +]); -**Attribute Methods:** +$cache = new Cache(new NoCache()); // or use any cache adapter you wish -```php -$collectionName = 'movies'; //required -$attributeId = 'name'; //required -$attributeType = //required -[ - Database::VAR_STRING, // use Utopia\Database\Database for these constants - Database::VAR_INTEGER, - Database::VAR_FLOAT, - Database::VAR_BOOLEAN, - Database::VAR_DATETIME, - Database::VAR_RELATIONSHIP -]; -$attributeSize = 128; //required -$attributeRequired = true; //required - -$database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); -// creates a new column named '$attributeName' in the 'namespace_collectionname' table. - -$newAttributeId = 'genres'; -$defaultValue = null; //optional -$isSigned = true; //optional -$isAnArray = false; //optional -$format = null; //optional -$formatOptions = []; //optional -$filters = []; //optional - -$database->createAttribute($collectionName, $newAttributeId,$attributeType[0] , $attributeSize, $attributeRequired,$defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); - -$database-> updateAttribute($collectionName, $attributeId, $newAttributeId, $attributeType[0], $attributeSize, $attributeRequired, $defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); - -$database->deleteAttribute($collectionName, $attributeId); - -$currentAttributeId = 'genres'; -$newAttributeId = 'genres2'; - -$database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); +$database = new Database(new MariaDB($pdo), $cache); +$database->setNamespace('mydb'); +$database->create(); // Creates a new schema named `mydb` ``` -**Index Methods:** +**Creating a collection:** ```php -$collectionName = 'movies'; //required -$indexId = 'index1'; //required -$indexType = //required -[ - Database::INDEX_KEY, // use Utopia\Database\Database for these constants - Database::INDEX_FULLTEXT, - Database::INDEX_UNIQUE, - Database::INDEX_SPATIAL, - Database::INDEX_ARRAY -]; -$attributesToIndex = ['name', 'genres'];//required -$indexSize = [128]; //required -$insertionOrder = [Database::ORDER_ASC, -Database::ORDER_DESC]; //required - -$database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); - -$currentIndexId = 'index1'; -$newIndexId = 'index2'; +$database->createCollection('movies'); -$database->renameIndex($collectionName, $currentIndexId, $newIndexId); - -$database->deleteIndex($collectionName, $indexId); -``` - -**Relationship Methods:** - -```php -$collectionName = "movies1"; //required -$relatedCollectionName = "movies2"; //required -$typeOfRelation = //required -[ - Database::RELATION_ONE_TO_ONE, - Database::RELATION_ONE_TO_MANY, - Database::RELATION_MANY_TO_ONE, - Database::RELATION_MANY_TO_MANY -]; -$isTwoWay = false; //required +// Add attributes +$database->createAttribute('movies', 'name', Database::VAR_STRING, 128, true); +$database->createAttribute('movies', 'director', Database::VAR_STRING, 128, true); +$database->createAttribute('movies', 'year', Database::VAR_INTEGER, 0, true); +$database->createAttribute('movies', 'price', Database::VAR_FLOAT, 0, true); +$database->createAttribute('movies', 'active', Database::VAR_BOOLEAN, 0, true); +$database->createAttribute('movies', 'genres', Database::VAR_STRING, 32, true, true, true); -$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay); - -$newColumnName = "director"; //required if two way -$newRelatedColumnName = "director_names"; //required if two way -$isTwoWay = true; //required if two way - -$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay, $newColumnName, $newRelatedColumnName); //creates a relationship between the two collections with the $newColumnName and $newRelatedColumnName as the reference columns - - -$database->updateRelationship($collectionName, $relatedCollectionName, $newColumnName, $newRelatedColumnName, $isTwoWay); - -$relatedAttributeName = "director"; //required - -$database->deleteRelationship($collectionName, $relatedAttributeName); +// Create an Index +$database->createIndex('movies', 'index1', Database::INDEX_KEY, ['year'], [128], [Database::ORDER_ASC]); ``` -**Document Methods:** +**Create a document:** ```php -use Utopia\Database\Document; // remember to use these classes -use Utopia\Database\Helpers\ID; -use Utopia\Database\Helpers\Permission; -use Utopia\Database\Helpers\Role; - - // Id helpers - ID::unique($lengthOfId), // if parameter is not passed it defaults to 7 - ID::custom($customId) // a parameter must be passed - - // Role helpers - Role::any(), - Role::user($customId) // creates a role with $customId as the identifier - - // Permission helpers - Permission::read($roleType), - Permission::create($roleType), - Permission::update($roleType), - Permission::delete($roleType) - -$document = $database->createDocument('movies', new Document([ +static::getDatabase()->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::unique(12))), + Permission::read(Role::user(ID::custom('2'))), Permission::create(Role::any()), Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::unique(12))), + Permission::create(Role::user(ID::custom('2x'))), Permission::update(Role::any()), Permission::update(Role::user(ID::custom('1x'))), - Permission::update(Role::user(ID::unique(12))), + Permission::update(Role::user(ID::custom('2x'))), Permission::delete(Role::any()), Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::unique(12))), - ], + Permission::delete(Role::user(ID::custom('2x'))), + ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', 'year' => 2019, @@ -376,110 +121,39 @@ $document = $database->createDocument('movies', new Document([ 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], ])); +``` -// To get which collection a document belongs to -$document->getCollection(); - -// To get document id -$document = $database->createDocument('movies', new Document([ - '$permissions' => [...], - 'name' => 'Captain Marvel', - .... => .... -])); -$documentId = $document->getId(); - -// To check whether document in empty -$document->isEmpty(); - -// increase an attribute in a document -$database->increaseDocumentAttribute($collection, $documentId,$attributeName, $value, $maxValue)); - -// decrease an attribute in a document -$database->decreaseDocumentAttribute($collection, $documentId,$attributeName, $value, $minValue)); - -// Update the value of an attribute in a document -$setTypes = -[ - Document::SET_TYPE_ASSIGN, - Document::SET_TYPE_APPEND, - Document::SET_TYPE_PREPEND -]; -$document->setAttribute($attributeName, $value) - ->setAttribute($attributeName, $value, $setTypes[0]); - -$database->updateDocument($collectionName, $documentId, $document); - -// Update the permissions of a document -$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) - ->setAttribute('$permissions', Permission::create(Role::any()), Document::SET_TYPE_APPEND) - ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) - ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) - -$database->updateDocument($collectionName, $documentId, $document); - -// Info regarding who has permission to read, create, update and delete a document -$document->getRead(); // returns an array of roles that have permission to read the document -$document->getCreate(); // returns an array of roles that have permission to create the document -$document->getUpdate(); // returns an array of roles that have permission to update the document -$document->getDelete(); // returns an array of roles that have permission to delete the document +**Get Document**: +```php // Get document with all attributes -$database->getDocument($collectionName, $documentId); +$document = static::getDatabase()->getDocument('movies', '1'); // Get document with a sub-set of attributes -$attributes = ['name', 'director', 'year']; -$database->getDocument($collectionName, $documentId, [ - Query::select($attributes), +$document = static::getDatabase()->getDocument('movies', '1', [ + Query::select(['name', 'director', 'year']), ]); +``` -// Find a document with a query - -$attribute = 'year'; -$multipleAttributes = ['year', 'name']; -$multipleValues = [2019, 2020]; -$value = 2021; -$possibleQueries = -[ - Query::equal($attribute, $multipleValues), - Query::notEqual($attribute, $value), - Query::lessThan($attribute, $value), - Query::lessThanEqual($attribute, $value), - Query::greaterThan($attribute, $value), - Query::greaterThanEqual($attribute, $value), - Query::contains($attribute, $multipleValues), - Query::between($attribute, $startValue, $endValue), - Query::search($attribute, $value), - Query::select($multipleAttributes), - Query::orderDesc($attribute), - Query::orderAsc($attribute), - Query::isNull($attribute), - Query::isNotNull($attribute), - Query::startsWith($attribute, $value), - Query::endsWith($attribute, $value), - Query::limit($value), - Query::offset($value), -]; - -$database->find('movies', [ - $possibleQueries[0], - $possibleQueries[1], -]); +**Find:** -// Delete a document -$database->deleteDocument($collectionName, $documentId); +```php +$documents = static::getDatabase()->find('movies', [ + Query::equal('year', [2019]), +]); ``` ### Adapters Below is a list of supported adapters, and their compatibly tested versions alongside a list of supported features and relevant limits. -| Adapter | Status | Version | -| -------- | ------ | ------- | -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLlite | βœ… | 3.38 | +| Adapter | Status | Version | +|---------|---------|---| +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLlite | βœ… | 3.38 | ` βœ… - supported, πŸ›  - work in progress` @@ -521,7 +195,6 @@ To run static code analysis, use the following Psalm command: ```bash docker compose exec tests vendor/bin/psalm --show-info=true ``` - ### Load testing Three commands have been added to `bin/` to fill, index, and query the DB to test changes: @@ -571,4 +244,4 @@ Navigate to `localhost:8708` to visualize query results. ## Copyright and license -The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file +The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) diff --git a/composer.lock b/composer.lock index c833faf44..ff525b0a8 100644 --- a/composer.lock +++ b/composer.lock @@ -512,16 +512,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.21.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2", + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2", "shasum": "" }, "require": { @@ -574,9 +574,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0" }, - "time": "2022-12-13T13:54:32+00:00" + "time": "2023-05-14T12:31:37+00:00" }, { "name": "laravel/pint", @@ -705,16 +705,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -755,9 +755,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "pcov/clobber", @@ -906,16 +906,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.14", + "version": "1.10.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "d232901b09e67538e5c86a724be841bea5768a7c" + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c", - "reference": "d232901b09e67538e5c86a724be841bea5768a7c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd", + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd", "shasum": "" }, "require": { @@ -964,7 +964,7 @@ "type": "tidelift" } ], - "time": "2023-04-19T13:47:27+00:00" + "time": "2023-05-09T15:28:01+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1286,16 +1286,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.7", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -1369,7 +1369,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -1385,7 +1385,7 @@ "type": "tidelift" } ], - "time": "2023-04-14T08:58:40+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "psr/container", @@ -1786,16 +1786,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1840,7 +1840,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { @@ -1848,7 +1848,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", @@ -2673,5 +2673,5 @@ "php": ">=8.0" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 025354489..d9e574851 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -398,15 +398,6 @@ abstract public function sum(string $collection, string $attribute, array $queri */ abstract public function count(string $collection, array $queries = [], ?int $max = null): int; - /** - * Get Collection Size - * Returns - * @param string $collection - * @return int - * @throws DatabaseException - */ - abstract public function getSizeOfCollection(string $collection): int; - /** * Get max STRING limit * diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 29dff187c..a676f9acb 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -142,40 +142,6 @@ public function createCollection(string $name, array $attributes = [], array $in return true; } - /** - * Get Collection Size - * @param string $collection - * @return int - * @throws DatabaseException - * - */ - public function getSizeOfCollection(string $collection): int - { - $name = $this->filter($collection); - $tableName = $this->getSQLTable($name); - $database = str_replace('`', '', explode('.', $tableName)[0]); - $collectionName = str_replace('`', '', explode('.', $tableName)[1]); - $query = $this->getPDO()->prepare(" - SELECT - data_length + index_length - FROM - information_schema.TABLES - WHERE - table_schema = :database - AND - table_name = :name - "); - $query->bindParam(':database', $database); - $query->bindParam(':name', $collectionName); - try { - $query->execute(); - $size = $query->fetchColumn(); - } catch (PDOException $e) { - throw new DatabaseException('Failed to get collection size: ' . $e->getMessage()); - } - return $size; - } - /** * Delete Collection * @param string $id diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 0af0a7748..d44102edf 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -253,28 +253,6 @@ public function listCollections(): array return $list; } - /** - * Get Collection Size - * @param string $collection - * @return int - * @throws DatabaseException - */ - public function getSizeOfCollection(string $collection): int - { - $namespace = $this->getNamespace(); - $command = [ - 'collStats' => $namespace . '_' . $this->filter($collection), - 'scale' => 1 - ]; - try{ - $result = $this->getClient()->query($command); - } - catch(Exception $e){ - throw new DatabaseException("Failed to get collection size" . $e->getMessage()); - } - return $result->size; - } - /** * Delete Collection * diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 1c5139ab1..fb569c85f 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -148,30 +148,6 @@ public function createCollection(string $name, array $attributes = [], array $in return true; } - /** - * Get Collection Size - * @param string $collection - * @return int - * @throws DatabaseException - * - */ - public function getSizeOfCollection(string $collection): int - { - // Does not work in tests. - $name = $this->filter($collection); - $query = $this->getPDO()->prepare(" - SELECT pg_total_relation_size('{$this->getSQLTable($name)}'); - "); - - try { - $query->execute(); - $size = $query->fetchColumn(); - } catch (PDOException $e) { - throw new DatabaseException('Failed to get collection size: ' . $e->getMessage()); - } - return $size; - } - /** * Delete Collection * diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index 4d2c14262..397d4001f 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -158,30 +158,6 @@ public function createCollection(string $name, array $attributes = [], array $in return true; } - /** - * Get Collection Size - * @param string $collection - * @return int - * @throws DatabaseException - * - */ - public function getSizeOfCollection(string $collection): int - { - $name = $this->filter($collection); - $namespace = $this->getNamespace(); - $query = $this->getPDO()->prepare(" - SELECT SUM(\"pgsize\") FROM \"dbstat\" WHERE name='{$namespace}_{$name}'; - "); - - try { - $query->execute(); - $size = $query->fetchColumn(); - } catch (PDOException $e) { - throw new DatabaseException('Failed to get collection size: ' . $e->getMessage()); - } - return $size; - } - /** * Delete Collection * @param string $id diff --git a/src/Database/Database.php b/src/Database/Database.php index 2e3f3e64f..053b41d39 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -755,18 +755,6 @@ public function listCollections(int $limit = 25, int $offset = 0): array return $result; } - /** - * Get Collection Size - * - * @param string $collection - * - * @return int - */ - public function getSizeOfCollection(string $collection): int - { - return $this->adapter->getSizeOfCollection($collection); - } - /** * Delete Collection * @@ -1486,6 +1474,13 @@ public function createRelationship( if (\strtolower($attribute->getId()) === \strtolower($id)) { throw new DuplicateException('Attribute already exists'); } + + if ($attribute->getAttribute('type') === self::VAR_RELATIONSHIP + && \strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) + && $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId() + ) { + throw new DuplicateException('Related attribute already exists'); + } } if ( @@ -1675,7 +1670,7 @@ public function updateRelationship( !\is_null($newTwoWayKey) && \in_array($newTwoWayKey, \array_map(fn ($attribute) => $attribute['key'], $relatedAttributes)) ) { - throw new DuplicateException('Attribute already exists'); + throw new DuplicateException('Related attribute already exists'); } $type = $attribute['options']['relationType']; diff --git a/templates/ff.config.json b/templates/ff.config.json new file mode 100644 index 000000000..ad05a6f59 --- /dev/null +++ b/templates/ff.config.json @@ -0,0 +1,82 @@ +{ + "name": "base", + "configs": [ + { + "name": "bloc", + "commands": [ + { + "name": "[FF] New Big Pack Bloc", + "templates": ["*"], + "key": "bigpack", + "files": [ + "bloc", + "event", + "index", + "model", + "page", + "provider", + "repository", + "screen", + "state" + ] + }, + { + "name": "[FF] New Small Pack Bloc", + "templates": ["*"], + "key": "smallpack", + "files": ["bloc", "event", "index", "page", "screen", "state"] + }, + { + "name": "[FF] New Bloc", + "templates": ["*"], + "files": ["bloc"] + }, + { + "name": "[FF] New Event", + "templates": ["*"], + "files": ["event"] + }, + { + "name": "[FF] New Model", + "templates": ["*"], + "files": ["model"] + }, + { + "name": "[FF] New Page", + "templates": ["*"], + "files": ["page"] + }, + { + "name": "[FF] New Provider", + "templates": ["*"], + "files": ["provider"] + }, + { + "name": "[FF] New Repository", + "templates": ["*"], + "files": ["repository"] + }, + { + "name": "[FF] New Screen", + "templates": ["*"], + "files": ["screen"] + }, + { + "name": "[FF] New State", + "templates": ["*"], + "files": ["state"] + }, + { + "name": "[FF] New Index", + "templates": ["*"], + "files": ["index"] + }, + { + "name": "[FF] New Navigate(Navme)", + "templates": ["navigate"], + "files": ["navigate"] + } + ] + } + ] +} diff --git a/templates/ff_bloc/bloc.tmpl b/templates/ff_bloc/bloc.tmpl new file mode 100644 index 000000000..c9ec8fd33 --- /dev/null +++ b/templates/ff_bloc/bloc.tmpl @@ -0,0 +1,16 @@ +import 'package:ff_bloc/ff_bloc.dart'; + +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Bloc extends FFBloc<${upperName}Event, ${upperName}State> { + ${upperName}Bloc({ + required this.provider, + super.initialState = const ${upperName}State(), + }); + + final ${upperName}Provider provider; + + @override + ${upperName}State onErrorState(Object error) => state.copy(error: error, isLoading: false); + +} diff --git a/templates/ff_bloc/event.tmpl b/templates/ff_bloc/event.tmpl new file mode 100644 index 000000000..3eed92565 --- /dev/null +++ b/templates/ff_bloc/event.tmpl @@ -0,0 +1,74 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart'; +import 'package:ff_bloc/ff_bloc.dart'; + +import 'package:${appName}${relative}/index.dart'; + +@immutable +abstract class ${upperName}Event implements FFBlocEvent<${upperName}State, ${upperName}Bloc> {} + +class Load${upperName}Event extends ${upperName}Event { + Load${upperName}Event({required this.id}); + final String? id; + + static const String _name = 'Load${upperName}Event'; + + @override + String toString() => _name; + + @override + Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* { + yield bloc.state.copyWithoutError(isLoading: true); + final result = await bloc.provider.fetchAsync(id); + yield bloc.state.copyWithoutError( + isLoading: false, + data: ${upperName}ViewModel(items: result), + ); + } +} + + +class Add${upperName}Event extends ${upperName}Event { + static const String _name = 'Add${upperName}Event'; + + @override + String toString() => _name; + + @override + Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* { + yield bloc.state.copyWithoutError(isLoading: true); + final result = await bloc.provider.addMore(bloc.state.data?.items); + yield bloc.state.copyWithoutError( + isLoading: false, + data: ${upperName}ViewModel(items: result), + ); + } +} + +class ErrorYouAwesomeEvent extends YouAwesomeEvent { + static const String _name = 'ErrorYouAwesomeEvent'; + + @override + String toString() => _name; + + @override + Stream applyAsync({required YouAwesomeBloc bloc}) async* { + throw Exception('Test error'); + } +} + +class Clear${upperName}Event extends ${upperName}Event { + static const String _name = 'Clear${upperName}Event'; + + @override + String toString() => _name; + + @override + Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* { + yield bloc.state.copyWithoutError(isLoading: true); + yield bloc.state.copyWithoutData( + isLoading: false, + ); + } +} diff --git a/templates/ff_bloc/index.tmpl b/templates/ff_bloc/index.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/templates/ff_bloc/model.tmpl b/templates/ff_bloc/model.tmpl new file mode 100644 index 000000000..4b13e7cbf --- /dev/null +++ b/templates/ff_bloc/model.tmpl @@ -0,0 +1,51 @@ +// ignore: depend_on_referenced_packages +import 'package:equatable/equatable.dart'; + +class ${upperName}Model extends Equatable { + const ${upperName}Model({ + required this.name, + }); + final String name; + + @override + List get props => [ name]; + + Map toMap() { + return { + 'name': name, + }; + } + + static ${upperName}Model? fromMap(Map? map) { + if (map == null) { + return null; + } + + return ${upperName}Model( + name: map['name']!.toString(), + ); + } + +} + +class ${upperName}ViewModel extends Equatable { + const ${upperName}ViewModel({ + // TODO(all): add all required constructor parameters + required this.items, + }); + + // TODO(all): declare your fields here + final List<${upperName}Model>? items; + + @override + List get props => [items /*TODO(all): List all fields here*/]; + + // TODO(all): implement copyWith + ${upperName}ViewModel copyWith({ + List<${upperName}Model>? items, + }) { + return ${upperName}ViewModel( + items: items ?? this.items, + ); + } +} diff --git a/templates/ff_bloc/page.tmpl b/templates/ff_bloc/page.tmpl new file mode 100644 index 000000000..2e32873ea --- /dev/null +++ b/templates/ff_bloc/page.tmpl @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:${appName}${relative}/index.dart'; + + +class ${upperName}Page extends StatefulWidget { + const ${upperName}Page({ + required this.bloc, + super.key + }); + static const String routeName = '/${privateName}'; + + final ${upperName}Bloc bloc; + + @override + State<${upperName}Page> createState() => _${upperName}PageState(); +} + +class _${upperName}PageState extends State<${upperName}Page> { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text('${upperName}'), + actions: [ + IconButton( + icon: const Icon(Icons.error), + onPressed: () { + widget.bloc.add(ErrorYouAwesomeEvent()); + }, + ), + IconButton( + icon: const Icon(Icons.add), + onPressed: () { + widget.bloc.add(Add${upperName}Event()); + }, + ), + IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + widget.bloc.add(Clear${upperName}Event()); + }, + ), + ], + ), + body: ${upperName}Screen(bloc: widget.bloc), + ); + } +} diff --git a/templates/ff_bloc/provider.tmpl b/templates/ff_bloc/provider.tmpl new file mode 100644 index 000000000..e4b9862bb --- /dev/null +++ b/templates/ff_bloc/provider.tmpl @@ -0,0 +1,24 @@ + +import 'dart:async'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Provider { + + Future?> fetchAsync(String? id) async { + if (id == null) { + return null; + } + return [${upperName}Model(name: id)]; + } + + + Future?> addMore(List<${upperName}Model>? now) async { + final result = [ + ...(now ?? <${upperName}Model>[]), + ${upperName}Model(name: now?.length.toString() ?? '0') + ]; + return result; + } + +} + diff --git a/templates/ff_bloc/screen.tmpl b/templates/ff_bloc/screen.tmpl new file mode 100644 index 000000000..c12ad1224 --- /dev/null +++ b/templates/ff_bloc/screen.tmpl @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:${appName}${relative}/index.dart'; + + +class ${upperName}Screen extends StatefulWidget { + const ${upperName}Screen({ + required this.bloc, + super.key, + }) ; + + @protected + final ${upperName}Bloc bloc; + + @override + State<${upperName}Screen> createState() { + return ${upperName}ScreenState(); + } +} + +class ${upperName}ScreenState extends State<${upperName}Screen> { + + @override + void initState() { + super.initState(); + if (!widget.bloc.state.hasData) { + _load(); + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder<${upperName}Bloc, ${upperName}State>( + bloc: widget.bloc, + builder: ( + BuildContext context, + ${upperName}State currentState, + ) { + return currentState.when( + onLoading: ()=>const CircularProgressIndicator(), + onEmpty: (data) => _Empty(), + onData: (data) => _BodyList(data: data), + onError: (e) => Center( + child: Column( + children: [ + Text(e.toString()), + TextButton( + onPressed: _load, + child: const Text('ReLoad'), + ) + ], + ), + ), + ); + }, + ); + } + + void _load() { + widget.bloc.add(Load${upperName}Event(id:'1')); + } + +} + + +class _BodyList extends StatefulWidget { + const _BodyList({required this.data}); + + final ${upperName}ViewModel data; + + @override + State<_BodyList> createState() => _BodyListState(); +} + +class _BodyListState extends State<_BodyList> { + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + return CustomScrollView( + // primary: true, + slivers: [ + const SliverToBoxAdapter(child: Divider()), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + final item = widget.data.items![index]; + if (index == 0) { + return Text('Header $index, id = '+item.name); + } + return Text('Index = $index, id = '+item.name); + }, + childCount: widget.data.items!.length, + ))]); + } +} + + +class _Empty extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Column( + children: [ + Text('Empty'), + ], + ); + } +} \ No newline at end of file diff --git a/templates/ff_bloc/state.tmpl b/templates/ff_bloc/state.tmpl new file mode 100644 index 000000000..6bf34a03e --- /dev/null +++ b/templates/ff_bloc/state.tmpl @@ -0,0 +1,15 @@ +import 'package:ff_bloc/ff_bloc.dart'; + +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}State extends FFState<${upperName}State, ${upperName}ViewModel> { + const ${upperName}State({ + super.version = 0, + super.isLoading = false, + super.data, + super.error, + }); + + @override + StateCopyFactory<${upperName}State, ${upperName}ViewModel> getCopyFactory() => ${upperName}State.new; +} diff --git a/templates/mutable/bloc.tmpl b/templates/mutable/bloc.tmpl new file mode 100644 index 000000000..190adf5d9 --- /dev/null +++ b/templates/mutable/bloc.tmpl @@ -0,0 +1,37 @@ +import 'dart:async'; +import 'dart:developer' as developer; + +import 'package:bloc/bloc.dart'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Bloc extends Bloc<${upperName}Event, ${upperName}State> { + // todo: check singleton for logic in project + // use GetIt for DI in projct + static final ${upperName}Bloc _${privateName}BlocSingleton = ${upperName}Bloc._internal(); + factory ${upperName}Bloc() { + return _${privateName}BlocSingleton; + } + + ${upperName}Bloc._internal(): super(Un${upperName}State(0)){ + on<${upperName}Event>((event, emit) { + return emit.forEach<${upperName}State>( + event.applyAsync(currentState: state, bloc: this), + onData: (state) => state, + onError: (error, stackTrace) { + developer.log('$error', name: '${upperName}Bloc', error: error, stackTrace: stackTrace); + return Error${upperName}State(0, error.toString()); + }, + ); + }); + } + + @override + Future close() async{ + // dispose objects + await super.close(); + } + + @override + ${upperName}State get initialState => Un${upperName}State(0); + +} diff --git a/templates/mutable/event.tmpl b/templates/mutable/event.tmpl new file mode 100644 index 000000000..cf45ca215 --- /dev/null +++ b/templates/mutable/event.tmpl @@ -0,0 +1,42 @@ +import 'dart:async'; +import 'dart:developer' as developer; + +import 'package:${appName}${relative}/index.dart'; +import 'package:meta/meta.dart'; + +@immutable +abstract class ${upperName}Event { + Stream<${upperName}State> applyAsync( + {${upperName}State currentState, ${upperName}Bloc bloc}); + final ${upperName}Repository _${privateName}Repository = ${upperName}Repository(); +} + +class Un${upperName}Event extends ${upperName}Event { + @override + Stream<${upperName}State> applyAsync({${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { + yield Un${upperName}State(0); + } +} + +class Load${upperName}Event extends ${upperName}Event { + + final bool isError; + @override + String toString() => 'Load${upperName}Event'; + + Load${upperName}Event(this.isError); + + @override + Stream<${upperName}State> applyAsync( + {${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { + try { + yield Un${upperName}State(0); + await Future.delayed(const Duration(seconds: 1)); + _${privateName}Repository.test(isError); + yield In${upperName}State(0, 'Hello world'); + } catch (_, stackTrace) { + developer.log('$_', name: 'Load${upperName}Event', error: _, stackTrace: stackTrace); + yield Error${upperName}State(0, _.toString()); + } + } +} diff --git a/templates/mutable/index.tmpl b/templates/mutable/index.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/templates/mutable/model.tmpl b/templates/mutable/model.tmpl new file mode 100644 index 000000000..64cf5783d --- /dev/null +++ b/templates/mutable/model.tmpl @@ -0,0 +1,47 @@ +import 'package:equatable/equatable.dart'; + +/// generate by https://javiercbk.github.io/json_to_dart/ +class Autogenerated${upperName} { + final List<${upperName}Model> results; + + Autogenerated${upperName}({required this.results}); + + factory Autogenerated${upperName}.fromJson(Map json) { + var temp = []; + if (json['results'] != null) { + temp = <${upperName}Model>[]; + json['results'].forEach((v) { + temp.add(${upperName}Model.fromJson(v as Map)); + }); + } + return Autogenerated${upperName}(results: temp); + } + + Map toJson() { + final data = {}; + data['results'] = results.map((v) => v.toJson()).toList(); + return data; + } +} + +class ${upperName}Model extends Equatable { + final int id; + final String name; + + ${upperName}Model(this.id, this.name); + + @override + List get props => [id, name]; + + factory ${upperName}Model.fromJson(Map json) { + return ${upperName}Model(json['id'] as int, json['name'] as String); + } + + Map toJson() { + final data = {}; + data['id'] = id; + data['name'] = name; + return data; + } + +} diff --git a/templates/mutable/page.tmpl b/templates/mutable/page.tmpl new file mode 100644 index 000000000..5e152b8ec --- /dev/null +++ b/templates/mutable/page.tmpl @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Page extends StatefulWidget { + static const String routeName = '/${privateName}'; + + @override + _${upperName}PageState createState() => _${upperName}PageState(); +} + +class _${upperName}PageState extends State<${upperName}Page> { + final _${privateName}Bloc = ${upperName}Bloc(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('${upperName}'), + ), + body: ${upperName}Screen(${privateName}Bloc: _${privateName}Bloc), + ); + } +} diff --git a/templates/mutable/provider.tmpl b/templates/mutable/provider.tmpl new file mode 100644 index 000000000..7fe9c15dc --- /dev/null +++ b/templates/mutable/provider.tmpl @@ -0,0 +1,20 @@ +import 'dart:async'; + +class ${upperName}Provider { + Future loadAsync(String token) async { + /// write from keystore/keychain + await Future.delayed(Duration(seconds: 2)); + } + + Future saveAsync(String token) async { + /// write from keystore/keychain + await Future.delayed(Duration(seconds: 2)); + } + + void test(bool isError) { + if (isError == true){ + throw Exception('manual error'); + } + } +} + diff --git a/templates/mutable/repository.tmpl b/templates/mutable/repository.tmpl new file mode 100644 index 000000000..332f91dcb --- /dev/null +++ b/templates/mutable/repository.tmpl @@ -0,0 +1,11 @@ +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Repository { + final ${upperName}Provider _${privateName}Provider = ${upperName}Provider(); + + ${upperName}Repository(); + + void test(bool isError) { + _${privateName}Provider.test(isError); + } +} \ No newline at end of file diff --git a/templates/mutable/screen.tmpl b/templates/mutable/screen.tmpl new file mode 100644 index 000000000..e126bf353 --- /dev/null +++ b/templates/mutable/screen.tmpl @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Screen extends StatefulWidget { + const ${upperName}Screen({ + required ${upperName}Bloc ${privateName}Bloc, + Key? key, + }) : _${privateName}Bloc = ${privateName}Bloc, + super(key: key); + + final ${upperName}Bloc _${privateName}Bloc; + + @override + ${upperName}ScreenState createState() { + return ${upperName}ScreenState(); + } +} + +class ${upperName}ScreenState extends State<${upperName}Screen> { + ${upperName}ScreenState(); + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder<${upperName}Bloc, ${upperName}State>( + bloc: widget._${privateName}Bloc, + builder: ( + BuildContext context, + ${upperName}State currentState, + ) { + if (currentState is Un${upperName}State) { + return Center( + child: CircularProgressIndicator(), + ); + } + if (currentState is Error${upperName}State) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(currentState.errorMessage), + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + ), + child: Text('reload'), + onPressed: _load, + ), + ), + ], + )); + } + if (currentState is In${upperName}State) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(currentState.hello), + const Text('Flutter files: done'), + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + ), + child: Text('throw error'), + onPressed: () => _load(true), + ), + ), + ], + ), + ); + } + return Center( + child: CircularProgressIndicator(), + ); + + }); + } + + void _load([bool isError = false]) { + widget._${privateName}Bloc.add(Load${upperName}Event(isError)); + } +} diff --git a/templates/mutable/state.tmpl b/templates/mutable/state.tmpl new file mode 100644 index 000000000..52c7ea720 --- /dev/null +++ b/templates/mutable/state.tmpl @@ -0,0 +1,83 @@ +import 'package:equatable/equatable.dart'; + +abstract class ${upperName}State extends Equatable { + ${upperName}State(this.version); + + /// notify change state without deep clone state + final int version; + + /// Copy object for use in action + /// if need use deep clone + ${upperName}State getStateCopy(); + + ${upperName}State getNewVersion(); + + @override + List get props => [version]; +} + +/// UnInitialized +class Un${upperName}State extends ${upperName}State { + + Un${upperName}State(int version) : super(version); + + @override + String toString() => 'Un${upperName}State'; + + @override + Un${upperName}State getStateCopy() { + return Un${upperName}State(0); + } + + @override + Un${upperName}State getNewVersion() { + return Un${upperName}State(version+1); + } +} + +/// Initialized +class In${upperName}State extends ${upperName}State { + + In${upperName}State(int version, this.hello) : super(version); + + final String hello; + + @override + String toString() => 'In${upperName}State $hello'; + + @override + In${upperName}State getStateCopy() { + return In${upperName}State(version, hello); + } + + @override + In${upperName}State getNewVersion() { + return In${upperName}State(version+1, hello); + } + + @override + List get props => [version, hello]; +} + +class Error${upperName}State extends ${upperName}State { + Error${upperName}State(int version, this.errorMessage): super(version); + + final String errorMessage; + + @override + String toString() => 'Error${upperName}State'; + + @override + Error${upperName}State getStateCopy() { + return Error${upperName}State(version, errorMessage); + } + + @override + Error${upperName}State getNewVersion() { + return Error${upperName}State(version+1, + errorMessage); + } + + @override + List get props => [version, errorMessage]; +} diff --git a/templates/navigate/navigate.tmpl b/templates/navigate/navigate.tmpl new file mode 100644 index 000000000..57b6748bb --- /dev/null +++ b/templates/navigate/navigate.tmpl @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:navme/navme.dart'; +import 'package:navme/helpers.dart'; + +import 'index.dart'; + +class ${upperName}Navigate { + // base path + static String path = '${privateName}'; + + // config for configurate Router + static RouteConfig routeConfig = RouteConfig( + state: (Uri? uri) => RouteState(uri: path.toUri()), + // condition for using this page + isThisPage: (RouteState state) { + if (state?.firstPath == path) { + return true; + } + return false; + }, + // settigs from url + settings: (RouteState state) { + return null; + }, + // get Page for Router + page: ({RouteState? state}) { + return MaterialPage( + key: const ValueKey('${upperName}Page'), + child: ${upperName}Page(), + name: '${upperName}Page'); + }, + ); +} diff --git a/templates/simple/bloc.tmpl b/templates/simple/bloc.tmpl new file mode 100644 index 000000000..3556967f2 --- /dev/null +++ b/templates/simple/bloc.tmpl @@ -0,0 +1,21 @@ +import 'dart:async'; +import 'dart:developer' as developer; + +import 'package:bloc/bloc.dart'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Bloc extends Bloc<${upperName}Event, ${upperName}State> { + + ${upperName}Bloc(${upperName}State initialState) : super(initialState){ + on<${upperName}Event>((event, emit) { + return emit.forEach<${upperName}State>( + event.applyAsync(currentState: state, bloc: this), + onData: (state) => state, + onError: (error, stackTrace) { + developer.log('$error', name: '${upperName}Bloc', error: error, stackTrace: stackTrace); + return Error${upperName}State(error.toString()); + }, + ); + }); + } +} diff --git a/templates/simple/event.tmpl b/templates/simple/event.tmpl new file mode 100644 index 000000000..d21542a20 --- /dev/null +++ b/templates/simple/event.tmpl @@ -0,0 +1,34 @@ +import 'dart:async'; +import 'dart:developer' as developer; + +import 'package:${appName}${relative}/index.dart'; +import 'package:meta/meta.dart'; + +@immutable +abstract class ${upperName}Event { + Stream<${upperName}State> applyAsync( + {${upperName}State currentState, ${upperName}Bloc bloc}); +} + +class Un${upperName}Event extends ${upperName}Event { + @override + Stream<${upperName}State> applyAsync({${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { + yield Un${upperName}State(); + } +} + +class Load${upperName}Event extends ${upperName}Event { + + @override + Stream<${upperName}State> applyAsync( + {${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { + try { + yield Un${upperName}State(); + await Future.delayed(const Duration(seconds: 1)); + yield In${upperName}State('Hello world'); + } catch (_, stackTrace) { + developer.log('$_', name: 'Load${upperName}Event', error: _, stackTrace: stackTrace); + yield Error${upperName}State( _.toString()); + } + } +} diff --git a/templates/simple/index.tmpl b/templates/simple/index.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/templates/simple/model.tmpl b/templates/simple/model.tmpl new file mode 100644 index 000000000..ede30505a --- /dev/null +++ b/templates/simple/model.tmpl @@ -0,0 +1,13 @@ +import 'package:equatable/equatable.dart'; + +/// use https://marketplace.visualstudio.com/items?itemName=BendixMa.dart-data-class-generator +class ${upperName}Model extends Equatable { + final int id; + final String name; + + ${upperName}Model(this.id, this.name); + + @override + List get props => [id, name]; + +} diff --git a/templates/simple/page.tmpl b/templates/simple/page.tmpl new file mode 100644 index 000000000..169680f2c --- /dev/null +++ b/templates/simple/page.tmpl @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Page extends StatefulWidget { + static const String routeName = '/${privateName}'; + + @override + _${upperName}PageState createState() => _${upperName}PageState(); +} + +class _${upperName}PageState extends State<${upperName}Page> { + final _${privateName}Bloc = ${upperName}Bloc(Un${upperName}State()); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('${upperName}'), + ), + body: ${upperName}Screen(${privateName}Bloc: _${privateName}Bloc), + ); + } +} diff --git a/templates/simple/provider.tmpl b/templates/simple/provider.tmpl new file mode 100644 index 000000000..7fe9c15dc --- /dev/null +++ b/templates/simple/provider.tmpl @@ -0,0 +1,20 @@ +import 'dart:async'; + +class ${upperName}Provider { + Future loadAsync(String token) async { + /// write from keystore/keychain + await Future.delayed(Duration(seconds: 2)); + } + + Future saveAsync(String token) async { + /// write from keystore/keychain + await Future.delayed(Duration(seconds: 2)); + } + + void test(bool isError) { + if (isError == true){ + throw Exception('manual error'); + } + } +} + diff --git a/templates/simple/repository.tmpl b/templates/simple/repository.tmpl new file mode 100644 index 000000000..332f91dcb --- /dev/null +++ b/templates/simple/repository.tmpl @@ -0,0 +1,11 @@ +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Repository { + final ${upperName}Provider _${privateName}Provider = ${upperName}Provider(); + + ${upperName}Repository(); + + void test(bool isError) { + _${privateName}Provider.test(isError); + } +} \ No newline at end of file diff --git a/templates/simple/screen.tmpl b/templates/simple/screen.tmpl new file mode 100644 index 000000000..dc592feca --- /dev/null +++ b/templates/simple/screen.tmpl @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:${appName}${relative}/index.dart'; + +class ${upperName}Screen extends StatefulWidget { + const ${upperName}Screen({ + required ${upperName}Bloc ${privateName}Bloc, + Key? key, + }) : _${privateName}Bloc = ${privateName}Bloc, + super(key: key); + + final ${upperName}Bloc _${privateName}Bloc; + + @override + ${upperName}ScreenState createState() { + return ${upperName}ScreenState(); + } +} + +class ${upperName}ScreenState extends State<${upperName}Screen> { + ${upperName}ScreenState(); + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder<${upperName}Bloc, ${upperName}State>( + bloc: widget._${privateName}Bloc, + builder: ( + BuildContext context, + ${upperName}State currentState, + ) { + if (currentState is Un${upperName}State) { + return Center( + child: CircularProgressIndicator(), + ); + } + if (currentState is Error${upperName}State) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(currentState.errorMessage ), + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: RaisedButton( + color: Colors.blue, + child: Text('reload'), + onPressed: _load, + ), + ), + ], + )); + } + if (currentState is In${upperName}State) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(currentState.hello), + ], + ), + ); + } + return Center( + child: CircularProgressIndicator(), + ); + + }); + } + + void _load() { + widget._${privateName}Bloc.add(Load${upperName}Event()); + } +} diff --git a/templates/simple/state.tmpl b/templates/simple/state.tmpl new file mode 100644 index 000000000..be0c2ac03 --- /dev/null +++ b/templates/simple/state.tmpl @@ -0,0 +1,42 @@ +import 'package:equatable/equatable.dart'; + +abstract class ${upperName}State extends Equatable { + ${upperName}State(); + + @override + List get props => []; +} + +/// UnInitialized +class Un${upperName}State extends ${upperName}State { + + Un${upperName}State(); + + @override + String toString() => 'Un${upperName}State'; +} + +/// Initialized +class In${upperName}State extends ${upperName}State { + In${upperName}State(this.hello); + + final String hello; + + @override + String toString() => 'In${upperName}State $hello'; + + @override + List get props => [hello]; +} + +class Error${upperName}State extends ${upperName}State { + Error${upperName}State(this.errorMessage); + + final String errorMessage; + + @override + String toString() => 'Error${upperName}State'; + + @override + List get props => [errorMessage]; +} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 7fdcfb4e2..9697fabb7 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -118,70 +118,6 @@ public function testCreateListExistsDeleteCollection(): void $this->assertEquals(true, static::getDatabase()->getCollection('actors')->isEmpty()); $this->assertEquals(false, static::getDatabase()->exists($this->testDatabase, 'actors')); } - - public function testSizeCollection(): void - { - - static::getDatabase()->createCollection('size_testing'); - - $valueBeforeAddingData = static::getDatabase()->getSizeOfCollection('size_testing'); - - static::getDatabase()->createAttribute('size_testing', 'string1', Database::VAR_STRING, 128, true); - static::getDatabase()->createAttribute('size_testing', 'string2', Database::VAR_STRING, 2147483646 + 1, true); - static::getDatabase()->createAttribute('size_testing', 'string3', Database::VAR_STRING, 2147483646 + 1, true); - static::getDatabase()->createAttribute('size_testing', 'string4', Database::VAR_STRING, 2147483646 + 1, true); - static::getDatabase()->createAttribute('size_testing', 'integer', Database::VAR_INTEGER, 0, true); - static::getDatabase()->createAttribute('size_testing', 'bigint', Database::VAR_INTEGER, 8, true); - static::getDatabase()->createAttribute('size_testing', 'float', Database::VAR_FLOAT, 0, true); - static::getDatabase()->createAttribute('size_testing', 'boolean', Database::VAR_BOOLEAN, 0, true); - static::getDatabase()->createAttribute('size_testing', 'string1new', Database::VAR_STRING, 128, false); - static::getDatabase()->createAttribute('size_testing', 'string2new', Database::VAR_STRING, 2147483646 + 1, false); - static::getDatabase()->createAttribute('size_testing', 'string3new', Database::VAR_STRING, 2147483646 + 1, false); - static::getDatabase()->createAttribute('size_testing', 'string4new', Database::VAR_STRING, 2147483646 + 1, false); - static::getDatabase()->createAttribute('size_testing', 'integernew', Database::VAR_INTEGER, 0, false); - static::getDatabase()->createAttribute('size_testing', 'bigintnew', Database::VAR_INTEGER, 8, false); - static::getDatabase()->createAttribute('size_testing', 'floatnew', Database::VAR_FLOAT, 0, false); - static::getDatabase()->createAttribute('size_testing', 'booleannew', Database::VAR_BOOLEAN, 0, false); - static::getDatabase()->createIndex('size_testing', 'string1_index', Database::INDEX_KEY, ['string1']); - static::getDatabase()->createIndex('size_testing', 'string2_index', Database::INDEX_KEY, ['string2'], [255]); - static::getDatabase()->createIndex('size_testing', 'multi_index', Database::INDEX_KEY, ['string1', 'string2', 'string3'], [128, 128, 128]); - - for ($i = 0; $i < 100; $i++) { - static::getDatabase()->createDocument('size_testing', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::custom('2'))), - Permission::create(Role::any()), - Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::custom('2x'))), - Permission::update(Role::any()), - Permission::update(Role::user(ID::custom('1x'))), - Permission::update(Role::user(ID::custom('2x'))), - Permission::delete(Role::any()), - Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::custom('2x'))), - ], - 'string1' => 'string1', - 'string2' => 'string2', - 'string3' => 'string3', // 2^33 - 'string4' => "string4", - 'integer' => 35635353, - 'bigint' => 3535353, - 'float' => 353.3535, - 'boolean' => true, - ])); - } - - $valueAfterAddingData = static::getDatabase()->getSizeOfCollection('size_testing'); - - if(static::getAdapterName() === 'mysql'){ - $this->assertGreaterThan(65535,$valueAfterAddingData); - } - else{ - $this->assertGreaterThan($valueBeforeAddingData,$valueAfterAddingData); - } - } public function testCreateDeleteAttribute(): void { @@ -4797,6 +4733,103 @@ public function testOneToOneTwoWayRelationship(): void $this->assertEquals(null, $country); } + public function testIdenticalTwoWayKeyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('parent'); + static::getDatabase()->createCollection('child'); + + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_ONE, + id: 'child1' + ); + + try { + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_MANY, + id: 'children', + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_MANY, + id: 'children', + twoWayKey: 'parent_id' + ); + + $collection = static::getDatabase()->getCollection('parent'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'child1') { + $this->assertEquals('parent', $attribute['options']['twoWayKey']); + } + + if ($attribute['key'] === 'children') { + $this->assertEquals('parent_id', $attribute['options']['twoWayKey']); + } + } + + static::getDatabase()->createDocument('parent', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'child1' => [ + '$id' => 'foo', + '$permissions' => [Permission::read(Role::any())], + ], + 'children' => [ + [ + '$id' => 'bar', + '$permissions' => [Permission::read(Role::any())], + ], + ], + ])); + + $documents = static::getDatabase()->find('parent', []); + $document = array_pop($documents); + $this->assertArrayHasKey('child1', $document); + $this->assertEquals('foo', $document->getAttribute('child1')->getId()); + $this->assertArrayHasKey('children', $document); + $this->assertEquals('bar', $document->getAttribute('children')[0]->getId()); + + try { + static::getDatabase()->updateRelationship( + collection: 'parent', + id: 'children', + newKey: 'child1' + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Attribute already exists', $e->getMessage()); + } + + try { + static::getDatabase()->updateRelationship( + collection: 'parent', + id: 'children', + newTwoWayKey: 'parent' + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + } + public function testOneToManyOneWayRelationship(): void { if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { @@ -10228,7 +10261,7 @@ public function testUpdateRelationshipToExistingKey(): void static::getDatabase()->updateRelationship('ovens', 'cakes', newTwoWayKey: 'height'); $this->fail('Failed to throw exception'); } catch (DuplicateException $e) { - $this->assertEquals('Attribute already exists', $e->getMessage()); + $this->assertEquals('Related attribute already exists', $e->getMessage()); } } @@ -11245,4 +11278,4 @@ public function testLast(): void $this->expectNotToPerformAssertions(); static::killDatabase(); } -} \ No newline at end of file +} From 63c84f9fadd442c4e2f73315c5b15097da9ccd10 Mon Sep 17 00:00:00 2001 From: Faisal I <118456685+faisalill@users.noreply.github.com> Date: Sat, 27 May 2023 22:15:49 +0530 Subject: [PATCH 18/37] remove templates --- templates/ff.config.json | 82 -------------------- templates/ff_bloc/bloc.tmpl | 16 ---- templates/ff_bloc/event.tmpl | 74 ------------------ templates/ff_bloc/index.tmpl | 0 templates/ff_bloc/model.tmpl | 51 ------------- templates/ff_bloc/page.tmpl | 50 ------------ templates/ff_bloc/provider.tmpl | 24 ------ templates/ff_bloc/screen.tmpl | 123 ------------------------------ templates/ff_bloc/state.tmpl | 15 ---- templates/mutable/bloc.tmpl | 37 --------- templates/mutable/event.tmpl | 42 ---------- templates/mutable/index.tmpl | 0 templates/mutable/model.tmpl | 47 ------------ templates/mutable/page.tmpl | 23 ------ templates/mutable/provider.tmpl | 20 ----- templates/mutable/repository.tmpl | 11 --- templates/mutable/screen.tmpl | 97 ----------------------- templates/mutable/state.tmpl | 83 -------------------- templates/navigate/navigate.tmpl | 33 -------- templates/simple/bloc.tmpl | 21 ----- templates/simple/event.tmpl | 34 --------- templates/simple/index.tmpl | 0 templates/simple/model.tmpl | 13 ---- templates/simple/page.tmpl | 23 ------ templates/simple/provider.tmpl | 20 ----- templates/simple/repository.tmpl | 11 --- templates/simple/screen.tmpl | 84 -------------------- templates/simple/state.tmpl | 42 ---------- 28 files changed, 1076 deletions(-) delete mode 100644 templates/ff.config.json delete mode 100644 templates/ff_bloc/bloc.tmpl delete mode 100644 templates/ff_bloc/event.tmpl delete mode 100644 templates/ff_bloc/index.tmpl delete mode 100644 templates/ff_bloc/model.tmpl delete mode 100644 templates/ff_bloc/page.tmpl delete mode 100644 templates/ff_bloc/provider.tmpl delete mode 100644 templates/ff_bloc/screen.tmpl delete mode 100644 templates/ff_bloc/state.tmpl delete mode 100644 templates/mutable/bloc.tmpl delete mode 100644 templates/mutable/event.tmpl delete mode 100644 templates/mutable/index.tmpl delete mode 100644 templates/mutable/model.tmpl delete mode 100644 templates/mutable/page.tmpl delete mode 100644 templates/mutable/provider.tmpl delete mode 100644 templates/mutable/repository.tmpl delete mode 100644 templates/mutable/screen.tmpl delete mode 100644 templates/mutable/state.tmpl delete mode 100644 templates/navigate/navigate.tmpl delete mode 100644 templates/simple/bloc.tmpl delete mode 100644 templates/simple/event.tmpl delete mode 100644 templates/simple/index.tmpl delete mode 100644 templates/simple/model.tmpl delete mode 100644 templates/simple/page.tmpl delete mode 100644 templates/simple/provider.tmpl delete mode 100644 templates/simple/repository.tmpl delete mode 100644 templates/simple/screen.tmpl delete mode 100644 templates/simple/state.tmpl diff --git a/templates/ff.config.json b/templates/ff.config.json deleted file mode 100644 index ad05a6f59..000000000 --- a/templates/ff.config.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "base", - "configs": [ - { - "name": "bloc", - "commands": [ - { - "name": "[FF] New Big Pack Bloc", - "templates": ["*"], - "key": "bigpack", - "files": [ - "bloc", - "event", - "index", - "model", - "page", - "provider", - "repository", - "screen", - "state" - ] - }, - { - "name": "[FF] New Small Pack Bloc", - "templates": ["*"], - "key": "smallpack", - "files": ["bloc", "event", "index", "page", "screen", "state"] - }, - { - "name": "[FF] New Bloc", - "templates": ["*"], - "files": ["bloc"] - }, - { - "name": "[FF] New Event", - "templates": ["*"], - "files": ["event"] - }, - { - "name": "[FF] New Model", - "templates": ["*"], - "files": ["model"] - }, - { - "name": "[FF] New Page", - "templates": ["*"], - "files": ["page"] - }, - { - "name": "[FF] New Provider", - "templates": ["*"], - "files": ["provider"] - }, - { - "name": "[FF] New Repository", - "templates": ["*"], - "files": ["repository"] - }, - { - "name": "[FF] New Screen", - "templates": ["*"], - "files": ["screen"] - }, - { - "name": "[FF] New State", - "templates": ["*"], - "files": ["state"] - }, - { - "name": "[FF] New Index", - "templates": ["*"], - "files": ["index"] - }, - { - "name": "[FF] New Navigate(Navme)", - "templates": ["navigate"], - "files": ["navigate"] - } - ] - } - ] -} diff --git a/templates/ff_bloc/bloc.tmpl b/templates/ff_bloc/bloc.tmpl deleted file mode 100644 index c9ec8fd33..000000000 --- a/templates/ff_bloc/bloc.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:ff_bloc/ff_bloc.dart'; - -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Bloc extends FFBloc<${upperName}Event, ${upperName}State> { - ${upperName}Bloc({ - required this.provider, - super.initialState = const ${upperName}State(), - }); - - final ${upperName}Provider provider; - - @override - ${upperName}State onErrorState(Object error) => state.copy(error: error, isLoading: false); - -} diff --git a/templates/ff_bloc/event.tmpl b/templates/ff_bloc/event.tmpl deleted file mode 100644 index 3eed92565..000000000 --- a/templates/ff_bloc/event.tmpl +++ /dev/null @@ -1,74 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/widgets.dart'; -import 'package:ff_bloc/ff_bloc.dart'; - -import 'package:${appName}${relative}/index.dart'; - -@immutable -abstract class ${upperName}Event implements FFBlocEvent<${upperName}State, ${upperName}Bloc> {} - -class Load${upperName}Event extends ${upperName}Event { - Load${upperName}Event({required this.id}); - final String? id; - - static const String _name = 'Load${upperName}Event'; - - @override - String toString() => _name; - - @override - Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* { - yield bloc.state.copyWithoutError(isLoading: true); - final result = await bloc.provider.fetchAsync(id); - yield bloc.state.copyWithoutError( - isLoading: false, - data: ${upperName}ViewModel(items: result), - ); - } -} - - -class Add${upperName}Event extends ${upperName}Event { - static const String _name = 'Add${upperName}Event'; - - @override - String toString() => _name; - - @override - Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* { - yield bloc.state.copyWithoutError(isLoading: true); - final result = await bloc.provider.addMore(bloc.state.data?.items); - yield bloc.state.copyWithoutError( - isLoading: false, - data: ${upperName}ViewModel(items: result), - ); - } -} - -class ErrorYouAwesomeEvent extends YouAwesomeEvent { - static const String _name = 'ErrorYouAwesomeEvent'; - - @override - String toString() => _name; - - @override - Stream applyAsync({required YouAwesomeBloc bloc}) async* { - throw Exception('Test error'); - } -} - -class Clear${upperName}Event extends ${upperName}Event { - static const String _name = 'Clear${upperName}Event'; - - @override - String toString() => _name; - - @override - Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* { - yield bloc.state.copyWithoutError(isLoading: true); - yield bloc.state.copyWithoutData( - isLoading: false, - ); - } -} diff --git a/templates/ff_bloc/index.tmpl b/templates/ff_bloc/index.tmpl deleted file mode 100644 index e69de29bb..000000000 diff --git a/templates/ff_bloc/model.tmpl b/templates/ff_bloc/model.tmpl deleted file mode 100644 index 4b13e7cbf..000000000 --- a/templates/ff_bloc/model.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -// ignore: depend_on_referenced_packages -import 'package:equatable/equatable.dart'; - -class ${upperName}Model extends Equatable { - const ${upperName}Model({ - required this.name, - }); - final String name; - - @override - List get props => [ name]; - - Map toMap() { - return { - 'name': name, - }; - } - - static ${upperName}Model? fromMap(Map? map) { - if (map == null) { - return null; - } - - return ${upperName}Model( - name: map['name']!.toString(), - ); - } - -} - -class ${upperName}ViewModel extends Equatable { - const ${upperName}ViewModel({ - // TODO(all): add all required constructor parameters - required this.items, - }); - - // TODO(all): declare your fields here - final List<${upperName}Model>? items; - - @override - List get props => [items /*TODO(all): List all fields here*/]; - - // TODO(all): implement copyWith - ${upperName}ViewModel copyWith({ - List<${upperName}Model>? items, - }) { - return ${upperName}ViewModel( - items: items ?? this.items, - ); - } -} diff --git a/templates/ff_bloc/page.tmpl b/templates/ff_bloc/page.tmpl deleted file mode 100644 index 2e32873ea..000000000 --- a/templates/ff_bloc/page.tmpl +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:${appName}${relative}/index.dart'; - - -class ${upperName}Page extends StatefulWidget { - const ${upperName}Page({ - required this.bloc, - super.key - }); - static const String routeName = '/${privateName}'; - - final ${upperName}Bloc bloc; - - @override - State<${upperName}Page> createState() => _${upperName}PageState(); -} - -class _${upperName}PageState extends State<${upperName}Page> { - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: const Text('${upperName}'), - actions: [ - IconButton( - icon: const Icon(Icons.error), - onPressed: () { - widget.bloc.add(ErrorYouAwesomeEvent()); - }, - ), - IconButton( - icon: const Icon(Icons.add), - onPressed: () { - widget.bloc.add(Add${upperName}Event()); - }, - ), - IconButton( - icon: const Icon(Icons.clear), - onPressed: () { - widget.bloc.add(Clear${upperName}Event()); - }, - ), - ], - ), - body: ${upperName}Screen(bloc: widget.bloc), - ); - } -} diff --git a/templates/ff_bloc/provider.tmpl b/templates/ff_bloc/provider.tmpl deleted file mode 100644 index e4b9862bb..000000000 --- a/templates/ff_bloc/provider.tmpl +++ /dev/null @@ -1,24 +0,0 @@ - -import 'dart:async'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Provider { - - Future?> fetchAsync(String? id) async { - if (id == null) { - return null; - } - return [${upperName}Model(name: id)]; - } - - - Future?> addMore(List<${upperName}Model>? now) async { - final result = [ - ...(now ?? <${upperName}Model>[]), - ${upperName}Model(name: now?.length.toString() ?? '0') - ]; - return result; - } - -} - diff --git a/templates/ff_bloc/screen.tmpl b/templates/ff_bloc/screen.tmpl deleted file mode 100644 index c12ad1224..000000000 --- a/templates/ff_bloc/screen.tmpl +++ /dev/null @@ -1,123 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:${appName}${relative}/index.dart'; - - -class ${upperName}Screen extends StatefulWidget { - const ${upperName}Screen({ - required this.bloc, - super.key, - }) ; - - @protected - final ${upperName}Bloc bloc; - - @override - State<${upperName}Screen> createState() { - return ${upperName}ScreenState(); - } -} - -class ${upperName}ScreenState extends State<${upperName}Screen> { - - @override - void initState() { - super.initState(); - if (!widget.bloc.state.hasData) { - _load(); - } - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocBuilder<${upperName}Bloc, ${upperName}State>( - bloc: widget.bloc, - builder: ( - BuildContext context, - ${upperName}State currentState, - ) { - return currentState.when( - onLoading: ()=>const CircularProgressIndicator(), - onEmpty: (data) => _Empty(), - onData: (data) => _BodyList(data: data), - onError: (e) => Center( - child: Column( - children: [ - Text(e.toString()), - TextButton( - onPressed: _load, - child: const Text('ReLoad'), - ) - ], - ), - ), - ); - }, - ); - } - - void _load() { - widget.bloc.add(Load${upperName}Event(id:'1')); - } - -} - - -class _BodyList extends StatefulWidget { - const _BodyList({required this.data}); - - final ${upperName}ViewModel data; - - @override - State<_BodyList> createState() => _BodyListState(); -} - -class _BodyListState extends State<_BodyList> { - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - - return CustomScrollView( - // primary: true, - slivers: [ - const SliverToBoxAdapter(child: Divider()), - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - final item = widget.data.items![index]; - if (index == 0) { - return Text('Header $index, id = '+item.name); - } - return Text('Index = $index, id = '+item.name); - }, - childCount: widget.data.items!.length, - ))]); - } -} - - -class _Empty extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text('Empty'), - ], - ); - } -} \ No newline at end of file diff --git a/templates/ff_bloc/state.tmpl b/templates/ff_bloc/state.tmpl deleted file mode 100644 index 6bf34a03e..000000000 --- a/templates/ff_bloc/state.tmpl +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:ff_bloc/ff_bloc.dart'; - -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}State extends FFState<${upperName}State, ${upperName}ViewModel> { - const ${upperName}State({ - super.version = 0, - super.isLoading = false, - super.data, - super.error, - }); - - @override - StateCopyFactory<${upperName}State, ${upperName}ViewModel> getCopyFactory() => ${upperName}State.new; -} diff --git a/templates/mutable/bloc.tmpl b/templates/mutable/bloc.tmpl deleted file mode 100644 index 190adf5d9..000000000 --- a/templates/mutable/bloc.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -import 'dart:async'; -import 'dart:developer' as developer; - -import 'package:bloc/bloc.dart'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Bloc extends Bloc<${upperName}Event, ${upperName}State> { - // todo: check singleton for logic in project - // use GetIt for DI in projct - static final ${upperName}Bloc _${privateName}BlocSingleton = ${upperName}Bloc._internal(); - factory ${upperName}Bloc() { - return _${privateName}BlocSingleton; - } - - ${upperName}Bloc._internal(): super(Un${upperName}State(0)){ - on<${upperName}Event>((event, emit) { - return emit.forEach<${upperName}State>( - event.applyAsync(currentState: state, bloc: this), - onData: (state) => state, - onError: (error, stackTrace) { - developer.log('$error', name: '${upperName}Bloc', error: error, stackTrace: stackTrace); - return Error${upperName}State(0, error.toString()); - }, - ); - }); - } - - @override - Future close() async{ - // dispose objects - await super.close(); - } - - @override - ${upperName}State get initialState => Un${upperName}State(0); - -} diff --git a/templates/mutable/event.tmpl b/templates/mutable/event.tmpl deleted file mode 100644 index cf45ca215..000000000 --- a/templates/mutable/event.tmpl +++ /dev/null @@ -1,42 +0,0 @@ -import 'dart:async'; -import 'dart:developer' as developer; - -import 'package:${appName}${relative}/index.dart'; -import 'package:meta/meta.dart'; - -@immutable -abstract class ${upperName}Event { - Stream<${upperName}State> applyAsync( - {${upperName}State currentState, ${upperName}Bloc bloc}); - final ${upperName}Repository _${privateName}Repository = ${upperName}Repository(); -} - -class Un${upperName}Event extends ${upperName}Event { - @override - Stream<${upperName}State> applyAsync({${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { - yield Un${upperName}State(0); - } -} - -class Load${upperName}Event extends ${upperName}Event { - - final bool isError; - @override - String toString() => 'Load${upperName}Event'; - - Load${upperName}Event(this.isError); - - @override - Stream<${upperName}State> applyAsync( - {${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { - try { - yield Un${upperName}State(0); - await Future.delayed(const Duration(seconds: 1)); - _${privateName}Repository.test(isError); - yield In${upperName}State(0, 'Hello world'); - } catch (_, stackTrace) { - developer.log('$_', name: 'Load${upperName}Event', error: _, stackTrace: stackTrace); - yield Error${upperName}State(0, _.toString()); - } - } -} diff --git a/templates/mutable/index.tmpl b/templates/mutable/index.tmpl deleted file mode 100644 index e69de29bb..000000000 diff --git a/templates/mutable/model.tmpl b/templates/mutable/model.tmpl deleted file mode 100644 index 64cf5783d..000000000 --- a/templates/mutable/model.tmpl +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// generate by https://javiercbk.github.io/json_to_dart/ -class Autogenerated${upperName} { - final List<${upperName}Model> results; - - Autogenerated${upperName}({required this.results}); - - factory Autogenerated${upperName}.fromJson(Map json) { - var temp = []; - if (json['results'] != null) { - temp = <${upperName}Model>[]; - json['results'].forEach((v) { - temp.add(${upperName}Model.fromJson(v as Map)); - }); - } - return Autogenerated${upperName}(results: temp); - } - - Map toJson() { - final data = {}; - data['results'] = results.map((v) => v.toJson()).toList(); - return data; - } -} - -class ${upperName}Model extends Equatable { - final int id; - final String name; - - ${upperName}Model(this.id, this.name); - - @override - List get props => [id, name]; - - factory ${upperName}Model.fromJson(Map json) { - return ${upperName}Model(json['id'] as int, json['name'] as String); - } - - Map toJson() { - final data = {}; - data['id'] = id; - data['name'] = name; - return data; - } - -} diff --git a/templates/mutable/page.tmpl b/templates/mutable/page.tmpl deleted file mode 100644 index 5e152b8ec..000000000 --- a/templates/mutable/page.tmpl +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Page extends StatefulWidget { - static const String routeName = '/${privateName}'; - - @override - _${upperName}PageState createState() => _${upperName}PageState(); -} - -class _${upperName}PageState extends State<${upperName}Page> { - final _${privateName}Bloc = ${upperName}Bloc(); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('${upperName}'), - ), - body: ${upperName}Screen(${privateName}Bloc: _${privateName}Bloc), - ); - } -} diff --git a/templates/mutable/provider.tmpl b/templates/mutable/provider.tmpl deleted file mode 100644 index 7fe9c15dc..000000000 --- a/templates/mutable/provider.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; - -class ${upperName}Provider { - Future loadAsync(String token) async { - /// write from keystore/keychain - await Future.delayed(Duration(seconds: 2)); - } - - Future saveAsync(String token) async { - /// write from keystore/keychain - await Future.delayed(Duration(seconds: 2)); - } - - void test(bool isError) { - if (isError == true){ - throw Exception('manual error'); - } - } -} - diff --git a/templates/mutable/repository.tmpl b/templates/mutable/repository.tmpl deleted file mode 100644 index 332f91dcb..000000000 --- a/templates/mutable/repository.tmpl +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Repository { - final ${upperName}Provider _${privateName}Provider = ${upperName}Provider(); - - ${upperName}Repository(); - - void test(bool isError) { - _${privateName}Provider.test(isError); - } -} \ No newline at end of file diff --git a/templates/mutable/screen.tmpl b/templates/mutable/screen.tmpl deleted file mode 100644 index e126bf353..000000000 --- a/templates/mutable/screen.tmpl +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Screen extends StatefulWidget { - const ${upperName}Screen({ - required ${upperName}Bloc ${privateName}Bloc, - Key? key, - }) : _${privateName}Bloc = ${privateName}Bloc, - super(key: key); - - final ${upperName}Bloc _${privateName}Bloc; - - @override - ${upperName}ScreenState createState() { - return ${upperName}ScreenState(); - } -} - -class ${upperName}ScreenState extends State<${upperName}Screen> { - ${upperName}ScreenState(); - - @override - void initState() { - super.initState(); - _load(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocBuilder<${upperName}Bloc, ${upperName}State>( - bloc: widget._${privateName}Bloc, - builder: ( - BuildContext context, - ${upperName}State currentState, - ) { - if (currentState is Un${upperName}State) { - return Center( - child: CircularProgressIndicator(), - ); - } - if (currentState is Error${upperName}State) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(currentState.errorMessage), - Padding( - padding: const EdgeInsets.only(top: 32.0), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - ), - child: Text('reload'), - onPressed: _load, - ), - ), - ], - )); - } - if (currentState is In${upperName}State) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(currentState.hello), - const Text('Flutter files: done'), - Padding( - padding: const EdgeInsets.only(top: 32.0), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - ), - child: Text('throw error'), - onPressed: () => _load(true), - ), - ), - ], - ), - ); - } - return Center( - child: CircularProgressIndicator(), - ); - - }); - } - - void _load([bool isError = false]) { - widget._${privateName}Bloc.add(Load${upperName}Event(isError)); - } -} diff --git a/templates/mutable/state.tmpl b/templates/mutable/state.tmpl deleted file mode 100644 index 52c7ea720..000000000 --- a/templates/mutable/state.tmpl +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:equatable/equatable.dart'; - -abstract class ${upperName}State extends Equatable { - ${upperName}State(this.version); - - /// notify change state without deep clone state - final int version; - - /// Copy object for use in action - /// if need use deep clone - ${upperName}State getStateCopy(); - - ${upperName}State getNewVersion(); - - @override - List get props => [version]; -} - -/// UnInitialized -class Un${upperName}State extends ${upperName}State { - - Un${upperName}State(int version) : super(version); - - @override - String toString() => 'Un${upperName}State'; - - @override - Un${upperName}State getStateCopy() { - return Un${upperName}State(0); - } - - @override - Un${upperName}State getNewVersion() { - return Un${upperName}State(version+1); - } -} - -/// Initialized -class In${upperName}State extends ${upperName}State { - - In${upperName}State(int version, this.hello) : super(version); - - final String hello; - - @override - String toString() => 'In${upperName}State $hello'; - - @override - In${upperName}State getStateCopy() { - return In${upperName}State(version, hello); - } - - @override - In${upperName}State getNewVersion() { - return In${upperName}State(version+1, hello); - } - - @override - List get props => [version, hello]; -} - -class Error${upperName}State extends ${upperName}State { - Error${upperName}State(int version, this.errorMessage): super(version); - - final String errorMessage; - - @override - String toString() => 'Error${upperName}State'; - - @override - Error${upperName}State getStateCopy() { - return Error${upperName}State(version, errorMessage); - } - - @override - Error${upperName}State getNewVersion() { - return Error${upperName}State(version+1, - errorMessage); - } - - @override - List get props => [version, errorMessage]; -} diff --git a/templates/navigate/navigate.tmpl b/templates/navigate/navigate.tmpl deleted file mode 100644 index 57b6748bb..000000000 --- a/templates/navigate/navigate.tmpl +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:navme/navme.dart'; -import 'package:navme/helpers.dart'; - -import 'index.dart'; - -class ${upperName}Navigate { - // base path - static String path = '${privateName}'; - - // config for configurate Router - static RouteConfig routeConfig = RouteConfig( - state: (Uri? uri) => RouteState(uri: path.toUri()), - // condition for using this page - isThisPage: (RouteState state) { - if (state?.firstPath == path) { - return true; - } - return false; - }, - // settigs from url - settings: (RouteState state) { - return null; - }, - // get Page for Router - page: ({RouteState? state}) { - return MaterialPage( - key: const ValueKey('${upperName}Page'), - child: ${upperName}Page(), - name: '${upperName}Page'); - }, - ); -} diff --git a/templates/simple/bloc.tmpl b/templates/simple/bloc.tmpl deleted file mode 100644 index 3556967f2..000000000 --- a/templates/simple/bloc.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -import 'dart:async'; -import 'dart:developer' as developer; - -import 'package:bloc/bloc.dart'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Bloc extends Bloc<${upperName}Event, ${upperName}State> { - - ${upperName}Bloc(${upperName}State initialState) : super(initialState){ - on<${upperName}Event>((event, emit) { - return emit.forEach<${upperName}State>( - event.applyAsync(currentState: state, bloc: this), - onData: (state) => state, - onError: (error, stackTrace) { - developer.log('$error', name: '${upperName}Bloc', error: error, stackTrace: stackTrace); - return Error${upperName}State(error.toString()); - }, - ); - }); - } -} diff --git a/templates/simple/event.tmpl b/templates/simple/event.tmpl deleted file mode 100644 index d21542a20..000000000 --- a/templates/simple/event.tmpl +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:async'; -import 'dart:developer' as developer; - -import 'package:${appName}${relative}/index.dart'; -import 'package:meta/meta.dart'; - -@immutable -abstract class ${upperName}Event { - Stream<${upperName}State> applyAsync( - {${upperName}State currentState, ${upperName}Bloc bloc}); -} - -class Un${upperName}Event extends ${upperName}Event { - @override - Stream<${upperName}State> applyAsync({${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { - yield Un${upperName}State(); - } -} - -class Load${upperName}Event extends ${upperName}Event { - - @override - Stream<${upperName}State> applyAsync( - {${upperName}State? currentState, ${upperName}Bloc? bloc}) async* { - try { - yield Un${upperName}State(); - await Future.delayed(const Duration(seconds: 1)); - yield In${upperName}State('Hello world'); - } catch (_, stackTrace) { - developer.log('$_', name: 'Load${upperName}Event', error: _, stackTrace: stackTrace); - yield Error${upperName}State( _.toString()); - } - } -} diff --git a/templates/simple/index.tmpl b/templates/simple/index.tmpl deleted file mode 100644 index e69de29bb..000000000 diff --git a/templates/simple/model.tmpl b/templates/simple/model.tmpl deleted file mode 100644 index ede30505a..000000000 --- a/templates/simple/model.tmpl +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// use https://marketplace.visualstudio.com/items?itemName=BendixMa.dart-data-class-generator -class ${upperName}Model extends Equatable { - final int id; - final String name; - - ${upperName}Model(this.id, this.name); - - @override - List get props => [id, name]; - -} diff --git a/templates/simple/page.tmpl b/templates/simple/page.tmpl deleted file mode 100644 index 169680f2c..000000000 --- a/templates/simple/page.tmpl +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Page extends StatefulWidget { - static const String routeName = '/${privateName}'; - - @override - _${upperName}PageState createState() => _${upperName}PageState(); -} - -class _${upperName}PageState extends State<${upperName}Page> { - final _${privateName}Bloc = ${upperName}Bloc(Un${upperName}State()); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('${upperName}'), - ), - body: ${upperName}Screen(${privateName}Bloc: _${privateName}Bloc), - ); - } -} diff --git a/templates/simple/provider.tmpl b/templates/simple/provider.tmpl deleted file mode 100644 index 7fe9c15dc..000000000 --- a/templates/simple/provider.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; - -class ${upperName}Provider { - Future loadAsync(String token) async { - /// write from keystore/keychain - await Future.delayed(Duration(seconds: 2)); - } - - Future saveAsync(String token) async { - /// write from keystore/keychain - await Future.delayed(Duration(seconds: 2)); - } - - void test(bool isError) { - if (isError == true){ - throw Exception('manual error'); - } - } -} - diff --git a/templates/simple/repository.tmpl b/templates/simple/repository.tmpl deleted file mode 100644 index 332f91dcb..000000000 --- a/templates/simple/repository.tmpl +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Repository { - final ${upperName}Provider _${privateName}Provider = ${upperName}Provider(); - - ${upperName}Repository(); - - void test(bool isError) { - _${privateName}Provider.test(isError); - } -} \ No newline at end of file diff --git a/templates/simple/screen.tmpl b/templates/simple/screen.tmpl deleted file mode 100644 index dc592feca..000000000 --- a/templates/simple/screen.tmpl +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:${appName}${relative}/index.dart'; - -class ${upperName}Screen extends StatefulWidget { - const ${upperName}Screen({ - required ${upperName}Bloc ${privateName}Bloc, - Key? key, - }) : _${privateName}Bloc = ${privateName}Bloc, - super(key: key); - - final ${upperName}Bloc _${privateName}Bloc; - - @override - ${upperName}ScreenState createState() { - return ${upperName}ScreenState(); - } -} - -class ${upperName}ScreenState extends State<${upperName}Screen> { - ${upperName}ScreenState(); - - @override - void initState() { - super.initState(); - _load(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocBuilder<${upperName}Bloc, ${upperName}State>( - bloc: widget._${privateName}Bloc, - builder: ( - BuildContext context, - ${upperName}State currentState, - ) { - if (currentState is Un${upperName}State) { - return Center( - child: CircularProgressIndicator(), - ); - } - if (currentState is Error${upperName}State) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(currentState.errorMessage ), - Padding( - padding: const EdgeInsets.only(top: 32.0), - child: RaisedButton( - color: Colors.blue, - child: Text('reload'), - onPressed: _load, - ), - ), - ], - )); - } - if (currentState is In${upperName}State) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(currentState.hello), - ], - ), - ); - } - return Center( - child: CircularProgressIndicator(), - ); - - }); - } - - void _load() { - widget._${privateName}Bloc.add(Load${upperName}Event()); - } -} diff --git a/templates/simple/state.tmpl b/templates/simple/state.tmpl deleted file mode 100644 index be0c2ac03..000000000 --- a/templates/simple/state.tmpl +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:equatable/equatable.dart'; - -abstract class ${upperName}State extends Equatable { - ${upperName}State(); - - @override - List get props => []; -} - -/// UnInitialized -class Un${upperName}State extends ${upperName}State { - - Un${upperName}State(); - - @override - String toString() => 'Un${upperName}State'; -} - -/// Initialized -class In${upperName}State extends ${upperName}State { - In${upperName}State(this.hello); - - final String hello; - - @override - String toString() => 'In${upperName}State $hello'; - - @override - List get props => [hello]; -} - -class Error${upperName}State extends ${upperName}State { - Error${upperName}State(this.errorMessage); - - final String errorMessage; - - @override - String toString() => 'Error${upperName}State'; - - @override - List get props => [errorMessage]; -} From 14d7e7f6789379e1e186369f05950f2d0f5ebb6c Mon Sep 17 00:00:00 2001 From: faisalill Date: Sat, 27 May 2023 22:18:41 +0530 Subject: [PATCH 19/37] added readme --- README.md | 425 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 376 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 07d6e6f25..25eca7e78 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Although this library is part of the [Utopia Framework](https://github.com/utopi ## Getting Started Install using composer: + ```bash composer require utopia-php/database ``` @@ -43,77 +44,331 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -### Examples +## Code Examples -Some examples to help you get started. +### Setting up different database adapters -**Creating a database:** +**MariaDB:** ```php require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; -use Utopia\Database\Adapter\MariaDB; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\None as NoCache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\MariaDB; $dbHost = 'mariadb'; $dbPort = '3306'; $dbUser = 'root'; $dbPass = 'password'; - -$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, [ +$pdoConfig = [ PDO::ATTR_TIMEOUT => 3, // Seconds PDO::ATTR_PERSISTENT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_STRINGIFY_FETCHES => true, -]); +]; -$cache = new Cache(new NoCache()); // or use any cache adapter you wish +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish $database = new Database(new MariaDB($pdo), $cache); -$database->setNamespace('mydb'); -$database->create(); // Creates a new schema named `mydb` ``` -**Creating a collection:** +**MySQL:** ```php -$database->createCollection('movies'); +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\MySQL; + +$dbHost = 'mysql'; +$dbPort = '3306'; +$dbUser = 'root'; +$dbPass = 'password'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -// Add attributes -$database->createAttribute('movies', 'name', Database::VAR_STRING, 128, true); -$database->createAttribute('movies', 'director', Database::VAR_STRING, 128, true); -$database->createAttribute('movies', 'year', Database::VAR_INTEGER, 0, true); -$database->createAttribute('movies', 'price', Database::VAR_FLOAT, 0, true); -$database->createAttribute('movies', 'active', Database::VAR_BOOLEAN, 0, true); -$database->createAttribute('movies', 'genres', Database::VAR_STRING, 32, true, true, true); +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish -// Create an Index -$database->createIndex('movies', 'index1', Database::INDEX_KEY, ['year'], [128], [Database::ORDER_ASC]); +$database = new Database(new MySql($pdo), $cache); ``` -**Create a document:** +**Postgres:** ```php -static::getDatabase()->createDocument('movies', new Document([ +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\Postgres; + +$dbHost = 'postgres'; +$dbPort = '5432'; +$dbUser = 'root'; +$dbPass = 'password'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new Postgres($pdo), $cache); +``` + +**SQLite:** + +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use PDO; +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\SQLite; + +$dbPath = '/path/to/database.sqlite'; +$pdoConfig = [ + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true, +]; + +$pdo = new PDO("{$dbPath}", $pdoConfig); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new SQLite($pdo), $cache); +``` + +**MongoDB:** + +```php +require_once __DIR__ . '/vendor/autoload.php'; + +use Utopia\Database\Database; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Database\Adapter\Mongo; +use Utopia\Mongo\Client; // from utopia-php/mongo + +$dbHost = 'mongo'; +$dbPort = 27017; // this should be a integer +$dbUser = 'root'; +$dbPass = 'password'; +$dbName = 'dbName'; + +$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true); + +$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish + +$database = new Database(new Mongo($client), $cache); +``` + +
+ +> ## Below methods are available for all database adapters. + +
+ +**Database Methods:** + +```php +$nameOfTheDatabaseOrSchema = 'mydb'; +$database->setNamespace($nameOfTheDatabaseOrSchema); +// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' +$database->create($nameOfTheDatabaseOrSchema); + +//delete database +$database->delete($nameOfTheDatabaseOrSchema); + +//ping database it returns true if the database is alive +$database->ping(); + +//check if database and collection exist it returns true if the database or collection exists +$database->exists($nameOfTheDatabaseOrSchema); // for database +$database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collection +``` + +**Collection Methods:** + +```php +$collectionName = 'movies'; + +$database->createCollection($collectionName); +// creates two new table/collection named 'namespace_movies' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' +// The second table is named 'namespace_movies_perms' with column names '_id', '_type', '_permission', '_document' + +$database->deleteCollection($collectionName); +// deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' + +$database->getSizeOfCollection($collectionName); +// returns the size of the collection in bytes where database is $this->getDefaultDatabase() +``` + +**Attribute Methods:** + +```php +$collectionName = 'movies'; //required +$attributeId = 'name'; //required +$attributeType = //required +[ + Database::VAR_STRING, // use Utopia\Database\Database for these constants + Database::VAR_INTEGER, + Database::VAR_FLOAT, + Database::VAR_BOOLEAN, + Database::VAR_DATETIME, + Database::VAR_RELATIONSHIP +]; +$attributeSize = 128; //required +$attributeRequired = true; //required + +$database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); +// creates a new column named '$attributeName' in the 'namespace_collectionname' table. + +$newAttributeId = 'genres'; +$defaultValue = null; //optional +$isSigned = true; //optional +$isAnArray = false; //optional +$format = null; //optional +$formatOptions = []; //optional +$filters = []; //optional + +$database->createAttribute($collectionName, $newAttributeId,$attributeType[0] , $attributeSize, $attributeRequired,$defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); + +$database-> updateAttribute($collectionName, $attributeId, $newAttributeId, $attributeType[0], $attributeSize, $attributeRequired, $defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); + +$database->deleteAttribute($collectionName, $attributeId); + +$currentAttributeId = 'genres'; +$newAttributeId = 'genres2'; + +$database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); +``` + +**Index Methods:** + +```php +$collectionName = 'movies'; //required +$indexId = 'index1'; //required +$indexType = //required +[ + Database::INDEX_KEY, // use Utopia\Database\Database for these constants + Database::INDEX_FULLTEXT, + Database::INDEX_UNIQUE, + Database::INDEX_SPATIAL, + Database::INDEX_ARRAY +]; +$attributesToIndex = ['name', 'genres'];//required +$indexSize = [128]; //required +$insertionOrder = [Database::ORDER_ASC, +Database::ORDER_DESC]; //required + +$database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); + +$currentIndexId = 'index1'; +$newIndexId = 'index2'; + +$database->renameIndex($collectionName, $currentIndexId, $newIndexId); + +$database->deleteIndex($collectionName, $indexId); +``` + +**Relationship Methods:** + +```php +$collectionName = "movies1"; //required +$relatedCollectionName = "movies2"; //required +$typeOfRelation = //required +[ + Database::RELATION_ONE_TO_ONE, + Database::RELATION_ONE_TO_MANY, + Database::RELATION_MANY_TO_ONE, + Database::RELATION_MANY_TO_MANY +]; +$isTwoWay = false; //required + +$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay); + +$newColumnName = "director"; //required if two way +$newRelatedColumnName = "director_names"; //required if two way +$isTwoWay = true; //required if two way + +$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay, $newColumnName, $newRelatedColumnName); //creates a relationship between the two collections with the $newColumnName and $newRelatedColumnName as the reference columns + + +$database->updateRelationship($collectionName, $relatedCollectionName, $newColumnName, $newRelatedColumnName, $isTwoWay); + +$relatedAttributeName = "director"; //required + +$database->deleteRelationship($collectionName, $relatedAttributeName); +``` + +**Document Methods:** + +```php +use Utopia\Database\Document; // remember to use these classes +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; + + // Id helpers + ID::unique($lengthOfId), // if parameter is not passed it defaults to 7 + ID::custom($customId) // a parameter must be passed + + // Role helpers + Role::any(), + Role::user($customId) // creates a role with $customId as the identifier + + // Permission helpers + Permission::read($roleType), + Permission::create($roleType), + Permission::update($roleType), + Permission::delete($roleType) + +$document = $database->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::custom('2'))), + Permission::read(Role::user(ID::unique(12))), Permission::create(Role::any()), Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::custom('2x'))), + Permission::create(Role::user(ID::unique(12))), Permission::update(Role::any()), Permission::update(Role::user(ID::custom('1x'))), - Permission::update(Role::user(ID::custom('2x'))), + Permission::update(Role::user(ID::unique(12))), Permission::delete(Role::any()), Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::custom('2x'))), - ], + Permission::delete(Role::user(ID::unique(12))), + ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', 'year' => 2019, @@ -121,39 +376,110 @@ static::getDatabase()->createDocument('movies', new Document([ 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], ])); -``` -**Get Document**: +// To get which collection a document belongs to +$document->getCollection(); + +// To get document id +$document = $database->createDocument('movies', new Document([ + '$permissions' => [...], + 'name' => 'Captain Marvel', + .... => .... +])); +$documentId = $document->getId(); + +// To check whether document in empty +$document->isEmpty(); + +// increase an attribute in a document +$database->increaseDocumentAttribute($collection, $documentId,$attributeName, $value, $maxValue)); + +// decrease an attribute in a document +$database->decreaseDocumentAttribute($collection, $documentId,$attributeName, $value, $minValue)); + +// Update the value of an attribute in a document +$setTypes = +[ + Document::SET_TYPE_ASSIGN, + Document::SET_TYPE_APPEND, + Document::SET_TYPE_PREPEND +]; +$document->setAttribute($attributeName, $value) + ->setAttribute($attributeName, $value, $setTypes[0]); + +$database->updateDocument($collectionName, $documentId, $document); + +// Update the permissions of a document +$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::create(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) + ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) + +$database->updateDocument($collectionName, $documentId, $document); + +// Info regarding who has permission to read, create, update and delete a document +$document->getRead(); // returns an array of roles that have permission to read the document +$document->getCreate(); // returns an array of roles that have permission to create the document +$document->getUpdate(); // returns an array of roles that have permission to update the document +$document->getDelete(); // returns an array of roles that have permission to delete the document -```php // Get document with all attributes -$document = static::getDatabase()->getDocument('movies', '1'); +$database->getDocument($collectionName, $documentId); // Get document with a sub-set of attributes -$document = static::getDatabase()->getDocument('movies', '1', [ - Query::select(['name', 'director', 'year']), +$attributes = ['name', 'director', 'year']; +$database->getDocument($collectionName, $documentId, [ + Query::select($attributes), ]); -``` -**Find:** - -```php -$documents = static::getDatabase()->find('movies', [ - Query::equal('year', [2019]), +// Find a document with a query + +$attribute = 'year'; +$multipleAttributes = ['year', 'name']; +$multipleValues = [2019, 2020]; +$value = 2021; +$possibleQueries = +[ + Query::equal($attribute, $multipleValues), + Query::notEqual($attribute, $value), + Query::lessThan($attribute, $value), + Query::lessThanEqual($attribute, $value), + Query::greaterThan($attribute, $value), + Query::greaterThanEqual($attribute, $value), + Query::contains($attribute, $multipleValues), + Query::between($attribute, $startValue, $endValue), + Query::search($attribute, $value), + Query::select($multipleAttributes), + Query::orderDesc($attribute), + Query::orderAsc($attribute), + Query::isNull($attribute), + Query::isNotNull($attribute), + Query::startsWith($attribute, $value), + Query::endsWith($attribute, $value), + Query::limit($value), + Query::offset($value), +]; + +$database->find('movies', [ + $possibleQueries[0], + $possibleQueries[1], ]); + +// Delete a document +$database->deleteDocument($collectionName, $documentId); ``` ### Adapters Below is a list of supported adapters, and their compatibly tested versions alongside a list of supported features and relevant limits. -| Adapter | Status | Version | -|---------|---------|---| -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLlite | βœ… | 3.38 | +| Adapter | Status | Version | +| -------- | ------ | ------- | +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLlite | βœ… | 3.38 | ` βœ… - supported, πŸ›  - work in progress` @@ -195,6 +521,7 @@ To run static code analysis, use the following Psalm command: ```bash docker compose exec tests vendor/bin/psalm --show-info=true ``` + ### Load testing Three commands have been added to `bin/` to fill, index, and query the DB to test changes: @@ -244,4 +571,4 @@ Navigate to `localhost:8708` to visualize query results. ## Copyright and license -The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) +The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file From 85ec5cef652a3196cc03ca4dbed076a26b024935 Mon Sep 17 00:00:00 2001 From: faisalill Date: Sat, 27 May 2023 22:35:54 +0530 Subject: [PATCH 20/37] better readme --- README.md | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 25eca7e78..217c884b5 100644 --- a/README.md +++ b/README.md @@ -224,8 +224,8 @@ $database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collectio $collectionName = 'movies'; $database->createCollection($collectionName); -// creates two new table/collection named 'namespace_movies' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' -// The second table is named 'namespace_movies_perms' with column names '_id', '_type', '_permission', '_document' +// creates two new table/collection named '$namespace_$collectionName' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' +// The second table is named '$namespace_$collectionName_perms' with column names '_id', '_type', '_permission', '_document' $database->deleteCollection($collectionName); // deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' @@ -252,7 +252,7 @@ $attributeSize = 128; //required $attributeRequired = true; //required $database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); -// creates a new column named '$attributeName' in the 'namespace_collectionname' table. +// creates a new column named '$attributeName' in the '$namespace_$collectionName' table. $newAttributeId = 'genres'; $defaultValue = null; //optional @@ -277,20 +277,23 @@ $database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId **Index Methods:** ```php -$collectionName = 'movies'; //required -$indexId = 'index1'; //required -$indexType = //required +$collectionName = 'movies'; //required +$indexId = 'index1'; //required +$indexType = //required [ - Database::INDEX_KEY, // use Utopia\Database\Database for these constants + Database::INDEX_KEY, // use Utopia\Database\Database for these constants Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL, Database::INDEX_ARRAY ]; -$attributesToIndex = ['name', 'genres'];//required -$indexSize = [128]; //required -$insertionOrder = [Database::ORDER_ASC, -Database::ORDER_DESC]; //required +$attributesToIndex = ['name', 'genres']; //required +$indexSize = [128]; //required +$insertionOrder = //required +[ + Database::ORDER_ASC, + Database::ORDER_DESC +]; $database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); @@ -335,7 +338,7 @@ $database->deleteRelationship($collectionName, $relatedAttributeName); **Document Methods:** ```php -use Utopia\Database\Document; // remember to use these classes +use Utopia\Database\Document; // remember to use these classes use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -357,17 +360,9 @@ use Utopia\Database\Helpers\Role; $document = $database->createDocument('movies', new Document([ '$permissions' => [ Permission::read(Role::any()), - Permission::read(Role::user(ID::custom('1'))), - Permission::read(Role::user(ID::unique(12))), - Permission::create(Role::any()), Permission::create(Role::user(ID::custom('1x'))), - Permission::create(Role::user(ID::unique(12))), - Permission::update(Role::any()), - Permission::update(Role::user(ID::custom('1x'))), Permission::update(Role::user(ID::unique(12))), - Permission::delete(Role::any()), - Permission::delete(Role::user(ID::custom('1x'))), - Permission::delete(Role::user(ID::unique(12))), + Permission::delete(Role::user($customId)), ], 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', @@ -384,7 +379,6 @@ $document->getCollection(); $document = $database->createDocument('movies', new Document([ '$permissions' => [...], 'name' => 'Captain Marvel', - .... => .... ])); $documentId = $document->getId(); From 1913805ac4a4c05aac158bbd906861ddc5ef3ca1 Mon Sep 17 00:00:00 2001 From: faisalill Date: Tue, 30 May 2023 00:54:49 +0530 Subject: [PATCH 21/37] updated docs --- README.md | 276 +++++++++++++++++++++++++++++------------------------- 1 file changed, 151 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 217c884b5..d00da65e4 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Cache\Adapter\Memory; use Utopia\Database\Adapter\MariaDB; $dbHost = 'mariadb'; @@ -74,7 +74,7 @@ $pdoConfig = [ $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish +$cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new MariaDB($pdo), $cache); ``` @@ -87,7 +87,7 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Cache\Adapter\Memory; use Utopia\Database\Adapter\MySQL; $dbHost = 'mysql'; @@ -105,7 +105,7 @@ $pdoConfig = [ $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish +$cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new MySql($pdo), $cache); ``` @@ -118,7 +118,7 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Cache\Adapter\Memory; use Utopia\Database\Adapter\Postgres; $dbHost = 'postgres'; @@ -136,7 +136,7 @@ $pdoConfig = [ $pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, $pdoConfig); -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish +$cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new Postgres($pdo), $cache); ``` @@ -149,7 +149,7 @@ require_once __DIR__ . '/vendor/autoload.php'; use PDO; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Cache\Adapter\Memory; use Utopia\Database\Adapter\SQLite; $dbPath = '/path/to/database.sqlite'; @@ -164,7 +164,7 @@ $pdoConfig = [ $pdo = new PDO("{$dbPath}", $pdoConfig); -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish +$cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new SQLite($pdo), $cache); ``` @@ -176,19 +176,19 @@ require_once __DIR__ . '/vendor/autoload.php'; use Utopia\Database\Database; use Utopia\Cache\Cache; -use Utopia\Cache\Adapter\Memory as MemoryCache; +use Utopia\Cache\Adapter\Memory; use Utopia\Database\Adapter\Mongo; use Utopia\Mongo\Client; // from utopia-php/mongo $dbHost = 'mongo'; -$dbPort = 27017; // this should be a integer +$dbPort = 27017; $dbUser = 'root'; $dbPass = 'password'; $dbName = 'dbName'; $mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true); -$cache = new Cache(new MemoryCache()); // or use any cache adapter you wish +$cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new Mongo($client), $cache); ``` @@ -202,44 +202,82 @@ $database = new Database(new Mongo($client), $cache); **Database Methods:** ```php -$nameOfTheDatabaseOrSchema = 'mydb'; -$database->setNamespace($nameOfTheDatabaseOrSchema); + +// Sets namespace that prefixes all collection names +$database->setNamespace(name: "namespace"); + // Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' -$database->create($nameOfTheDatabaseOrSchema); +$database->create(name: "mydb"); -//delete database -$database->delete($nameOfTheDatabaseOrSchema); +// Delete database +$database->delete(name: "mydb"); -//ping database it returns true if the database is alive +// Ping database it returns true if the database is alive $database->ping(); -//check if database and collection exist it returns true if the database or collection exists -$database->exists($nameOfTheDatabaseOrSchema); // for database -$database->exists($nameOfTheDatabaseOrSchema, $collectionName); // for collection +// Check if database and collection exist it returns true if the database or collection exists +$database->exists(database: "mydb"); // for database +$database->exists(database: "mydb", collection: "users"); // for collection ``` **Collection Methods:** ```php -$collectionName = 'movies'; - -$database->createCollection($collectionName); -// creates two new table/collection named '$namespace_$collectionName' with column names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' -// The second table is named '$namespace_$collectionName_perms' with column names '_id', '_type', '_permission', '_document' +$attributes = [ + new Document([ + '$id' => ID::custom('attribute1'), + 'type' => Database::VAR_STRING, + 'size' => 2500, // longer than 768 + 'required' => false, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]), + new Document([ + '$id' => ID::custom('attribute-2'), + 'type' => Database::VAR_INTEGER, + 'size' => 0, + 'required' => false, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]), +] + +$indexes = [ + $indexes = [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['attribute1'], + 'lengths' => [256], + 'orders' => ['ASC'], + ]), + new Document([ + '$id' => ID::custom('index-2'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['attribute-2'], + 'lengths' => [], + 'orders' => ['ASC'], + ]) +] +]; +// attributes and indexes are optional +// Creates two new collection named '$namespace_$collectionName' with attribute names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' +// The second collection is named '$namespace_$collectionName_perms' with attribute names '_id', '_type', '_permission', '_document' +$database->createCollection(name: "users", attributes: $attributes, indexes: $indexes); -$database->deleteCollection($collectionName); -// deletes the two tables/collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' +// Deletes the two collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' +$database->deleteCollection(id: "users"); -$database->getSizeOfCollection($collectionName); -// returns the size of the collection in bytes where database is $this->getDefaultDatabase() +// Returns the size of the collection in bytes where database is the default database +$database->getSizeOfCollection(collection: "users"); ``` **Attribute Methods:** ```php -$collectionName = 'movies'; //required -$attributeId = 'name'; //required -$attributeType = //required +$attributeType = [ Database::VAR_STRING, // use Utopia\Database\Database for these constants Database::VAR_INTEGER, @@ -248,38 +286,28 @@ $attributeType = //required Database::VAR_DATETIME, Database::VAR_RELATIONSHIP ]; -$attributeSize = 128; //required -$attributeRequired = true; //required -$database->createAttribute($collectionName,$attributeId, $attributeType[0], $attributeSize, $attributeRequired); -// creates a new column named '$attributeName' in the '$namespace_$collectionName' table. +// Creates a new attribute named '$attributeName' in the '$namespace_$collectionName' collection. +$database->createAttribute(collection: "movies",id: "name",type: $attributeType[0], size: 128, required: true); -$newAttributeId = 'genres'; -$defaultValue = null; //optional -$isSigned = true; //optional -$isAnArray = false; //optional -$format = null; //optional -$formatOptions = []; //optional -$filters = []; //optional +// New attribute with optional parameters +$database->createAttribute(collection: "movies", id: "genres",type: $attributeType[0] , size: 128, required: true, default: null, signed: true, array: false, format: null, formatOptions: [], filters: []); -$database->createAttribute($collectionName, $newAttributeId,$attributeType[0] , $attributeSize, $attributeRequired,$defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); +// Updates the attribute named '$attributeName' in the '$namespace_$collectionName' collection. +$database-> updateAttribute(collection: "movies", id: "genres",type: $attributeType[1] , size: 128, required: true, default: null, signed: true, array: false, format: null, formatOptions: [], filters: []); -$database-> updateAttribute($collectionName, $attributeId, $newAttributeId, $attributeType[0], $attributeSize, $attributeRequired, $defaultValue, $isSigned, $isAnArray, $format, $formatOptions, $filters); -$database->deleteAttribute($collectionName, $attributeId); +// Deletes the attribute in the '$namespace_$collectionName' collection. +$database->deleteAttribute(collection: "movies", id: "genres"); -$currentAttributeId = 'genres'; -$newAttributeId = 'genres2'; - -$database->renameAttribute($collectionName, $currentAttributeId, $newAttributeId); +// Renames the attribute from old to new in the '$namespace_$collectionName' collection. +$database->renameAttribute(collection: "movies",old: "genres", new: "genres2"); ``` **Index Methods:** ```php -$collectionName = 'movies'; //required -$indexId = 'index1'; //required -$indexType = //required +$indexType = [ Database::INDEX_KEY, // use Utopia\Database\Database for these constants Database::INDEX_FULLTEXT, @@ -287,52 +315,56 @@ $indexType = //required Database::INDEX_SPATIAL, Database::INDEX_ARRAY ]; -$attributesToIndex = ['name', 'genres']; //required -$indexSize = [128]; //required -$insertionOrder = //required + +$insertionOrder = [ Database::ORDER_ASC, Database::ORDER_DESC ]; -$database->createIndex($collectionName, $indexId, $indexType[0], $attributeToIndex, $indexSizes, [$insertionOrder[0], $insertionOrder[1]]); - -$currentIndexId = 'index1'; -$newIndexId = 'index2'; +// Creates a new index named '$indexName' in the '$namespace_$collectionName' collection. +// Note: The size for the index will be taken from the size of the attribute +$database->createIndex(collection: "movies", id: "index1", $indexType[0], attributes: ["name", "genres"], lengths: [128,128], orders: [$insertionOrder[0], $insertionOrder[1]]); -$database->renameIndex($collectionName, $currentIndexId, $newIndexId); +// Rename index from old to new in the '$namespace_$collectionName' collection. +$database->renameIndex(collection: "movies", old: "index1", new: "index2"); -$database->deleteIndex($collectionName, $indexId); +// Deletes the index in the '$namespace_$collectionName' collection. +$database->deleteIndex(collection: "movies", id: "index2"); ``` **Relationship Methods:** ```php -$collectionName = "movies1"; //required -$relatedCollectionName = "movies2"; //required -$typeOfRelation = //required +$typeOfRelation = [ Database::RELATION_ONE_TO_ONE, Database::RELATION_ONE_TO_MANY, Database::RELATION_MANY_TO_ONE, Database::RELATION_MANY_TO_MANY ]; -$isTwoWay = false; //required -$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay); +// Creates a relationship between the two collections with the default reference attributes +$database->createRelationship(collection: "movies", relatedCollection: "users", $typeOfRelation[0], twoWay: true); -$newColumnName = "director"; //required if two way -$newRelatedColumnName = "director_names"; //required if two way -$isTwoWay = true; //required if two way -$database->createRelationship($collectionName, $relatedCollectionName, $typeOfRelation[3], $isTwoWay, $newColumnName, $newRelatedColumnName); //creates a relationship between the two collections with the $newColumnName and $newRelatedColumnName as the reference columns +// Create a relationship with custom reference attributes +$database->createRelationship(collection: "movies", relatedCollection: "users", $typeOfRelation[0], twoWay: true, id: "movies_id", twoWayKey: "users_id"); +$onDeleteTypes = [ + Database::RELATION_MUTATE_CASCADE, + Database::RELATION_MUTATE_SET_NULL, + Database::RELATION_MUTATE_RESTRICT, +]; -$database->updateRelationship($collectionName, $relatedCollectionName, $newColumnName, $newRelatedColumnName, $isTwoWay); +// Update the relationship with the default reference attributes +$database->updateRelationship(collection: "movies", id: "users", onDelete: $onDeleteTypes[0]); -$relatedAttributeName = "director"; //required +// Update the relationship with custom reference attributes +$database->updateRelationship(collection: "movies", id: "users", onDelete: $onDeleteTypes[0], newKey: "movies_id", newTwoWayKey: "users_id", twoWay: true); -$database->deleteRelationship($collectionName, $relatedAttributeName); +// Delete the relationship with the default or custom reference attributes +$database->deleteRelationship(collection: "movies", id: "users"); ``` **Document Methods:** @@ -344,20 +376,21 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; // Id helpers - ID::unique($lengthOfId), // if parameter is not passed it defaults to 7 - ID::custom($customId) // a parameter must be passed + ID::unique(padding: 12), // Length of the ID: Default is 7 + ID::custom(id: "my_user_3235") // a parameter must be passed // Role helpers Role::any(), - Role::user($customId) // creates a role with $customId as the identifier + Role::user(ID::unique()) // creates a role with $customId as the identifier // Permission helpers - Permission::read($roleType), - Permission::create($roleType), - Permission::update($roleType), - Permission::delete($roleType) + Permission::read(Role::any()), + Permission::create(Role::user(ID::unique())), + Permission::update(Role::user(ID::unique(padding: 23))), + Permission::delete(Role::user(ID::custom(id: "my_user_3235")) -$document = $database->createDocument('movies', new Document([ +// To create a document +$document = new Document([ '$permissions' => [ Permission::read(Role::any()), Permission::create(Role::user(ID::custom('1x'))), @@ -370,26 +403,23 @@ $document = $database->createDocument('movies', new Document([ 'price' => 25.99, 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], -])); +]); +$document = $database->createDocument(collection: 'movies', document: $document); // To get which collection a document belongs to $document->getCollection(); // To get document id -$document = $database->createDocument('movies', new Document([ - '$permissions' => [...], - 'name' => 'Captain Marvel', -])); -$documentId = $document->getId(); +$document->getId(); // To check whether document in empty $document->isEmpty(); -// increase an attribute in a document -$database->increaseDocumentAttribute($collection, $documentId,$attributeName, $value, $maxValue)); +// Increase an attribute in a document +$database->increaseDocumentAttribute(collection: "movies", id: $document->getId(),attribute: "name", value: 24, max: 100)); -// decrease an attribute in a document -$database->decreaseDocumentAttribute($collection, $documentId,$attributeName, $value, $minValue)); +// Decrease an attribute in a document +$database->decreaseDocumentAttribute(collection: "movies", id: $document->getId(),attribute: "name", value: 24, min: 100)); // Update the value of an attribute in a document $setTypes = @@ -398,10 +428,10 @@ $setTypes = Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND ]; -$document->setAttribute($attributeName, $value) - ->setAttribute($attributeName, $value, $setTypes[0]); +$document->setAttribute(key: "name", "Chris Smoove") + ->setAttribute(key: "age", 33, $setTypes[0]); -$database->updateDocument($collectionName, $documentId, $document); +$database->updateDocument(collection: "users", id: $document->getId(), document: $document); // Update the permissions of a document $document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) @@ -409,7 +439,7 @@ $document->setAttribute('$permissions', Permission::read(Role::any()), Document: ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) -$database->updateDocument($collectionName, $documentId, $document); +$database->updateDocument(collection: "users", id: $document->getId(), document: $document); // Info regarding who has permission to read, create, update and delete a document $document->getRead(); // returns an array of roles that have permission to read the document @@ -418,49 +448,45 @@ $document->getUpdate(); // returns an array of roles that have permission to upd $document->getDelete(); // returns an array of roles that have permission to delete the document // Get document with all attributes -$database->getDocument($collectionName, $documentId); +$database->getDocument(collection: "movies", id: $document->getId()); // Get document with a sub-set of attributes $attributes = ['name', 'director', 'year']; -$database->getDocument($collectionName, $documentId, [ +$database->getDocument(collection: "movies", id: $document->getId(), [ Query::select($attributes), ]); // Find a document with a query - -$attribute = 'year'; -$multipleAttributes = ['year', 'name']; -$multipleValues = [2019, 2020]; -$value = 2021; $possibleQueries = [ - Query::equal($attribute, $multipleValues), - Query::notEqual($attribute, $value), - Query::lessThan($attribute, $value), - Query::lessThanEqual($attribute, $value), - Query::greaterThan($attribute, $value), - Query::greaterThanEqual($attribute, $value), - Query::contains($attribute, $multipleValues), - Query::between($attribute, $startValue, $endValue), - Query::search($attribute, $value), - Query::select($multipleAttributes), - Query::orderDesc($attribute), - Query::orderAsc($attribute), - Query::isNull($attribute), - Query::isNotNull($attribute), - Query::startsWith($attribute, $value), - Query::endsWith($attribute, $value), - Query::limit($value), - Query::offset($value), + Query::equal(attribute: "location", values: ["Bangalore", "California"]), + Query::notEqual(attribute: "location", value: "New York"), + Query::lessThan(attribute: "users", value: 3000), + Query::lessThanEqual(attribute: "users", value: 10000), + Query::greaterThan(attribute: "users", value: 200), + Query::greaterThanEqual(attribute: "users", value: 250), + Query::contains(attribute: "users", values: ["Jhon", "Smith"]), + Query::between(attribute: "price", start: 100, end: 1000), + Query::search(attribute: "users", value: "Jake"), + Query::select(attributes: ["location", "users"]), + Query::orderDesc(attribute: "users"), + Query::orderAsc(attribute: "users"), + Query::isNull(attribute: "users"), + Query::isNotNull(attribute: "users"), + Query::startsWith(attribute: "name", value: "Jhon"), + Query::endsWith(attribute: "name", value: "Smith"), + Query::limit(value: 35), + Query::offset(value: 0), ]; -$database->find('movies', [ + +$database->find(collection: 'movies', queries: [ $possibleQueries[0], $possibleQueries[1], -]); +], timeout: 1); //timeout is optional // Delete a document -$database->deleteDocument($collectionName, $documentId); +$database->deleteDocument(collection: "movies", id: $document->getId()); ``` ### Adapters @@ -471,7 +497,7 @@ Below is a list of supported adapters, and their compatibly tested versions alon | -------- | ------ | ------- | | MariaDB | βœ… | 10.5 | | MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | +| Postgres | πŸ›  | 13.0 | | MongoDB | βœ… | 5.0 | | SQLlite | βœ… | 3.38 | From 79ac692e19db5fadc835678d9b3dea5cd490c502 Mon Sep 17 00:00:00 2001 From: faisalill Date: Tue, 30 May 2023 23:54:48 +0530 Subject: [PATCH 22/37] fixed stuff --- README.md | 315 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 213 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index d00da65e4..7628a3a38 100644 --- a/README.md +++ b/README.md @@ -204,110 +204,158 @@ $database = new Database(new Mongo($client), $cache); ```php // Sets namespace that prefixes all collection names -$database->setNamespace(name: "namespace"); +$database->setNamespace(name: 'namespace'); // Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' -$database->create(name: "mydb"); +$database->create(name: 'mydb'); // Delete database -$database->delete(name: "mydb"); +$database->delete(name: 'mydb'); // Ping database it returns true if the database is alive $database->ping(); -// Check if database and collection exist it returns true if the database or collection exists -$database->exists(database: "mydb"); // for database -$database->exists(database: "mydb", collection: "users"); // for collection +// Check if database exists +$database->exists(database: 'mydb'); // for database + +// Check if collection exists +$database->exists( + database: 'mydb', + collection: 'users' +); ``` **Collection Methods:** ```php +// Creates two new collection named '$namespace_$collectionName' with attribute names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' +// The second collection is named '$namespace_$collectionName_perms' with attribute names '_id', '_type', '_permission', '_document' +$database->createCollection(name: 'users'); + +// Create collection with attributes and indexes $attributes = [ new Document([ - '$id' => ID::custom('attribute1'), - 'type' => Database::VAR_STRING, - 'size' => 2500, // longer than 768 - 'required' => false, - 'signed' => true, - 'array' => false, - 'filters' => [], + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ], + 'name' => 'Jhon', + 'age' => 20 ]), new Document([ - '$id' => ID::custom('attribute-2'), - 'type' => Database::VAR_INTEGER, - 'size' => 0, - 'required' => false, - 'signed' => true, - 'array' => false, - 'filters' => [], + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ], + 'name' => 'Doe', + 'age' => 34 ]), ] $indexes = [ - $indexes = [ - new Document([ - '$id' => ID::custom('index1'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['attribute1'], - 'lengths' => [256], - 'orders' => ['ASC'], + new Document([ + '$id' => ID::unique(), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [256], + 'orders' => ['ASC'], ]), - new Document([ - '$id' => ID::custom('index-2'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['attribute-2'], - 'lengths' => [], - 'orders' => ['ASC'], + new Document([ + '$id' => ID::unique(), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name', 'age'], + 'lengths' => [128, 128], + 'orders' => ['ASC'], ]) -] ]; -// attributes and indexes are optional -// Creates two new collection named '$namespace_$collectionName' with attribute names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' -// The second collection is named '$namespace_$collectionName_perms' with attribute names '_id', '_type', '_permission', '_document' -$database->createCollection(name: "users", attributes: $attributes, indexes: $indexes); + +$database->createCollection( + name: 'users', + attributes: $attributes, + indexes: $indexes +); // Deletes the two collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' -$database->deleteCollection(id: "users"); +$database->deleteCollection(id: 'users'); // Returns the size of the collection in bytes where database is the default database -$database->getSizeOfCollection(collection: "users"); +$database->getSizeOfCollection(collection: 'users'); ``` **Attribute Methods:** ```php -$attributeType = +$attributeTypes = [ Database::VAR_STRING, // use Utopia\Database\Database for these constants Database::VAR_INTEGER, Database::VAR_FLOAT, Database::VAR_BOOLEAN, Database::VAR_DATETIME, - Database::VAR_RELATIONSHIP ]; // Creates a new attribute named '$attributeName' in the '$namespace_$collectionName' collection. -$database->createAttribute(collection: "movies",id: "name",type: $attributeType[0], size: 128, required: true); +$database->createAttribute( + collection: 'movies', + id: 'name', + type: $attributeTypes[0], + size: 128, + required: true +); // New attribute with optional parameters -$database->createAttribute(collection: "movies", id: "genres",type: $attributeType[0] , size: 128, required: true, default: null, signed: true, array: false, format: null, formatOptions: [], filters: []); +$database->createAttribute( + collection: 'movies', + id: 'genres', + type: $attributeTypes[0] , + size: 128, + required: true, + default: null, + signed: true, + array: false, + format: null, + formatOptions: [], + filters: [] +); // Updates the attribute named '$attributeName' in the '$namespace_$collectionName' collection. -$database-> updateAttribute(collection: "movies", id: "genres",type: $attributeType[1] , size: 128, required: true, default: null, signed: true, array: false, format: null, formatOptions: [], filters: []); +$database-> updateAttribute( + collection: 'movies', + id: 'genres', + type: $attributeTypes[1] , + size: 128, + required: true, + default: null, + signed: true, + array: false, + format: null, + formatOptions: [], + filters: [] +); // Deletes the attribute in the '$namespace_$collectionName' collection. -$database->deleteAttribute(collection: "movies", id: "genres"); +$database->deleteAttribute( + collection: 'movies', + id: 'genres' +); // Renames the attribute from old to new in the '$namespace_$collectionName' collection. -$database->renameAttribute(collection: "movies",old: "genres", new: "genres2"); +$database->renameAttribute( + collection: 'movies', + old: 'genres', + new: 'genres2' +); ``` **Index Methods:** ```php -$indexType = +$indexTypes = [ Database::INDEX_KEY, // use Utopia\Database\Database for these constants Database::INDEX_FULLTEXT, @@ -324,19 +372,32 @@ $insertionOrder = // Creates a new index named '$indexName' in the '$namespace_$collectionName' collection. // Note: The size for the index will be taken from the size of the attribute -$database->createIndex(collection: "movies", id: "index1", $indexType[0], attributes: ["name", "genres"], lengths: [128,128], orders: [$insertionOrder[0], $insertionOrder[1]]); +$database->createIndex( + collection: 'movies', + id: 'index1', $indexTypes[0], + attributes: ['name', 'genres'], + lengths: [128,128], + orders: [$insertionOrder[0], $insertionOrder[1]] +); // Rename index from old to new in the '$namespace_$collectionName' collection. -$database->renameIndex(collection: "movies", old: "index1", new: "index2"); +$database->renameIndex( + collection: 'movies', + old: 'index1', + new: 'index2' +); // Deletes the index in the '$namespace_$collectionName' collection. -$database->deleteIndex(collection: "movies", id: "index2"); +$database->deleteIndex( + collection: 'movies', + id: 'index2' +); ``` **Relationship Methods:** ```php -$typeOfRelation = +$typesOfRelation = [ Database::RELATION_ONE_TO_ONE, Database::RELATION_ONE_TO_MANY, @@ -345,11 +406,23 @@ $typeOfRelation = ]; // Creates a relationship between the two collections with the default reference attributes -$database->createRelationship(collection: "movies", relatedCollection: "users", $typeOfRelation[0], twoWay: true); +$database->createRelationship( + collection: 'movies', + relatedCollection: 'users', + $typesOfRelation[0], + twoWay: true +); // Create a relationship with custom reference attributes -$database->createRelationship(collection: "movies", relatedCollection: "users", $typeOfRelation[0], twoWay: true, id: "movies_id", twoWayKey: "users_id"); +$database->createRelationship( + collection: 'movies', + relatedCollection: 'users', + $typesOfRelation[0], + twoWay: true, + id: 'movies_id', + twoWayKey: 'users_id' +); $onDeleteTypes = [ Database::RELATION_MUTATE_CASCADE, @@ -358,36 +431,50 @@ $onDeleteTypes = [ ]; // Update the relationship with the default reference attributes -$database->updateRelationship(collection: "movies", id: "users", onDelete: $onDeleteTypes[0]); +$database->updateRelationship( + collection: 'movies', + id: 'users', + onDelete: $onDeleteTypes[0] +); // Update the relationship with custom reference attributes -$database->updateRelationship(collection: "movies", id: "users", onDelete: $onDeleteTypes[0], newKey: "movies_id", newTwoWayKey: "users_id", twoWay: true); +$database->updateRelationship( + collection: 'movies', + id: 'users', + onDelete: $onDeleteTypes[0], + newKey: 'movies_id', + newTwoWayKey: 'users_id', + twoWay: true +); // Delete the relationship with the default or custom reference attributes -$database->deleteRelationship(collection: "movies", id: "users"); +$database->deleteRelationship( + collection: 'movies', + id: 'users' +); ``` **Document Methods:** ```php -use Utopia\Database\Document; // remember to use these classes +use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; - // Id helpers - ID::unique(padding: 12), // Length of the ID: Default is 7 - ID::custom(id: "my_user_3235") // a parameter must be passed +// Id helpers +ID::unique(padding: 12), // Creates a id of length 7 + padding +ID::custom(id: 'my_user_3235') - // Role helpers - Role::any(), - Role::user(ID::unique()) // creates a role with $customId as the identifier +// Role helpers +Role::any(), +Role::user(ID::unique()) - // Permission helpers - Permission::read(Role::any()), - Permission::create(Role::user(ID::unique())), - Permission::update(Role::user(ID::unique(padding: 23))), - Permission::delete(Role::user(ID::custom(id: "my_user_3235")) +// Permission helpers +Permission::read(Role::any()), +Permission::create(Role::user(ID::unique())), +Permission::update(Role::user(ID::unique(padding: 23))), +Permission::delete(Role::user(ID::custom(id: 'my_user_3235')) // To create a document $document = new Document([ @@ -406,20 +493,32 @@ $document = new Document([ ]); $document = $database->createDocument(collection: 'movies', document: $document); -// To get which collection a document belongs to +// Get which collection a document belongs to $document->getCollection(); -// To get document id +// Get document id $document->getId(); -// To check whether document in empty +// Check whether document in empty $document->isEmpty(); // Increase an attribute in a document -$database->increaseDocumentAttribute(collection: "movies", id: $document->getId(),attribute: "name", value: 24, max: 100)); +$database->increaseDocumentAttribute( + collection: 'movies', + id: $document->getId(), + attribute: 'name', + value: 24, + max: 100 +); // Decrease an attribute in a document -$database->decreaseDocumentAttribute(collection: "movies", id: $document->getId(),attribute: "name", value: 24, min: 100)); +$database->decreaseDocumentAttribute( + collection: 'movies', + id: $document->getId(), + attribute: 'name', + value: 24, + min: 100 +); // Update the value of an attribute in a document $setTypes = @@ -428,10 +527,14 @@ $setTypes = Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND ]; -$document->setAttribute(key: "name", "Chris Smoove") - ->setAttribute(key: "age", 33, $setTypes[0]); +$document->setAttribute(key: 'name', 'Chris Smoove') + ->setAttribute(key: 'age', 33, $setTypes[0]); -$database->updateDocument(collection: "users", id: $document->getId(), document: $document); +$database->updateDocument( + collection: 'users', + id: $document->getId(), + document: $document +); // Update the permissions of a document $document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND) @@ -439,7 +542,11 @@ $document->setAttribute('$permissions', Permission::read(Role::any()), Document: ->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND) ->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND) -$database->updateDocument(collection: "users", id: $document->getId(), document: $document); +$database->updateDocument( + collection: 'users', + id: $document->getId(), + document: $document +); // Info regarding who has permission to read, create, update and delete a document $document->getRead(); // returns an array of roles that have permission to read the document @@ -448,35 +555,39 @@ $document->getUpdate(); // returns an array of roles that have permission to upd $document->getDelete(); // returns an array of roles that have permission to delete the document // Get document with all attributes -$database->getDocument(collection: "movies", id: $document->getId()); +$database->getDocument( + collection: 'movies', + id: $document->getId() +); // Get document with a sub-set of attributes -$attributes = ['name', 'director', 'year']; -$database->getDocument(collection: "movies", id: $document->getId(), [ - Query::select($attributes), -]); +$database->getDocument( + collection: 'movies', + id: $document->getId(), + [Query::select(['name', 'director', 'year'])] +); // Find a document with a query $possibleQueries = [ - Query::equal(attribute: "location", values: ["Bangalore", "California"]), - Query::notEqual(attribute: "location", value: "New York"), - Query::lessThan(attribute: "users", value: 3000), - Query::lessThanEqual(attribute: "users", value: 10000), - Query::greaterThan(attribute: "users", value: 200), - Query::greaterThanEqual(attribute: "users", value: 250), - Query::contains(attribute: "users", values: ["Jhon", "Smith"]), - Query::between(attribute: "price", start: 100, end: 1000), - Query::search(attribute: "users", value: "Jake"), - Query::select(attributes: ["location", "users"]), - Query::orderDesc(attribute: "users"), - Query::orderAsc(attribute: "users"), - Query::isNull(attribute: "users"), - Query::isNotNull(attribute: "users"), - Query::startsWith(attribute: "name", value: "Jhon"), - Query::endsWith(attribute: "name", value: "Smith"), - Query::limit(value: 35), - Query::offset(value: 0), + Query::equal('location',['Bangalore', 'California']), + Query::notEqual('location','New York'), + Query::lessThan('users',3000), + Query::lessThanEqual('users',10000), + Query::greaterThan('users',200), + Query::greaterThanEqual('users',250), + Query::contains('users',['Jhon', 'Smith']), + Query::between('price', start: 100, end: 1000), + Query::search('users','Jake'), + Query::select(['location','users']), + Query::orderDesc('users'), + Query::orderAsc('users'), + Query::isNull('users'), + Query::isNotNull('users'), + Query::startsWith('name','Jhon'), + Query::endsWith('name','Smith'), + Query::limit(35), + Query::offset(0), ]; @@ -486,7 +597,7 @@ $database->find(collection: 'movies', queries: [ ], timeout: 1); //timeout is optional // Delete a document -$database->deleteDocument(collection: "movies", id: $document->getId()); +$database->deleteDocument(collection: 'movies', id: $document->getId()); ``` ### Adapters From 8b982e16561def1e22307f6dc2ecb7664c1241e9 Mon Sep 17 00:00:00 2001 From: faisalill Date: Mon, 10 Jul 2023 22:36:40 +0530 Subject: [PATCH 23/37] added more methods --- README.md | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 209 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7628a3a38..c98045f55 100644 --- a/README.md +++ b/README.md @@ -203,26 +203,90 @@ $database = new Database(new Mongo($client), $cache); ```php +// Get namespace +$database->getNamespace(); + // Sets namespace that prefixes all collection names -$database->setNamespace(name: 'namespace'); +$database->setNamespace( + namespace: 'namespace' +); + +// Get default database +$database->getDefaultDatabase(); + +// Sets default database +$database->setDefaultDatabase( + name: 'dbName' +); + +// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. +// Uses default database as the name. +$database->create(); + +// Returns an array of all databases +$database->list(); + -// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. named 'mydb' -$database->create(name: 'mydb'); // Delete database -$database->delete(name: 'mydb'); +$database->delete( + name: 'mydb' +); // Ping database it returns true if the database is alive $database->ping(); // Check if database exists -$database->exists(database: 'mydb'); // for database +$database->exists( + database: 'mydb' +); // for database // Check if collection exists $database->exists( database: 'mydb', collection: 'users' ); + +// Listen to events +$eventType = +[ + Database::EVENT_ALL + Database::EVENT_DATABASE_CREATE, + Database::EVENT_DATABASE_LIST, + Database::EVENT_COLLECTION_CREATE, + Database::EVENT_COLLECTION_LIST, + Database::EVENT_COLLECTION_READ, + Database::EVENT_ATTRIBUTE_CREATE, + Database::EVENT_ATTRIBUTE_UPDATE, + Database::EVENT_INDEX_CREATE, + Database::EVENT_DOCUMENT_CREATE, + Database::EVENT_DOCUMENT_UPDATE, + Database::EVENT_DOCUMENT_READ, + Database::EVENT_DOCUMENT_FIND, + Database::EVENT_DOCUMENT_FIND, + Database::EVENT_DOCUMENT_COUNT, + Database::EVENT_DOCUMENT_SUM, + Database::EVENT_DOCUMENT_INCREASE, + Database::EVENT_DOCUMENT_DECREASE, + Database::EVENT_INDEX_DELETE, + Database::EVENT_DOCUMENT_DELETE, + Database::EVENT_ATTRIBUTE_DELETE, + Database::EVENT_COLLECTION_DELETE, + Database::EVENT_DATABASE_DELETE, +]; + +$database->on( + string: $eventType[0], + callable: function($event, $data) { + // Do something + } +); + +// Get Database Adapter +$database->getAdapter(); + +// Get List of keywords that cannot be used +$database->getKeywords(); ``` **Collection Methods:** @@ -230,7 +294,9 @@ $database->exists( ```php // Creates two new collection named '$namespace_$collectionName' with attribute names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' // The second collection is named '$namespace_$collectionName_perms' with attribute names '_id', '_type', '_permission', '_document' -$database->createCollection(name: 'users'); +$database->createCollection( + name: 'users' +); // Create collection with attributes and indexes $attributes = [ @@ -279,11 +345,37 @@ $database->createCollection( indexes: $indexes ); +// Update Collection Permissions +$database->updateCollection( + id: 'users', + permissions: [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ], + documentSecurity: true +); + +// Get Collection +$database->getCollection( + id: 'users' +); + +// List Collections +$database->listCollections( + limit: 25, + offset: 0 +); + // Deletes the two collections named 'namespace_$collectionName' and 'namespace_$collectionName_perms' -$database->deleteCollection(id: 'users'); +$database->deleteCollection( + id: 'users' +); -// Returns the size of the collection in bytes where database is the default database -$database->getSizeOfCollection(collection: 'users'); +// Delete cached documents of a collection +$database->deleteCachedCollection( + collection: 'users' +); ``` **Attribute Methods:** @@ -337,19 +429,75 @@ $database-> updateAttribute( filters: [] ); +// Update the required status of an attribute +$database->updateAttributeRequired( + collection: 'movies', + id: 'genres', + required: true +); -// Deletes the attribute in the '$namespace_$collectionName' collection. -$database->deleteAttribute( +// Update the attribute format +$database->updateAttributeFormat( collection: 'movies', - id: 'genres' + id: 'genres', + format: null, ); +// Update the attribute format options +$database->updateAttributeFormatOptions( + collection: 'movies', + id: 'genres', + formatOptions: [] +); + +// Update the attribute filters +$database->updateAttributeFilters( + collection: 'movies', + id: 'genres', + filters: [] +); + +// Update the default value of an attribute +$database->updateAttributeDefault( + collection: 'movies', + id: 'genres', + default: 'sci-fi' +); + +// Check if attribute can be added to a collection +$collection = $database->getCollection('movies'); + +$attribute = new Document([ + '$id' => ID::unique(), + 'type' => Database::VAR_INTEGER, + 'size' => 256, + 'required' => true, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], +]); + +$database->checkAttribute($collection, $attribute); + +// Get Adapter attribute limit +$database->getLimitForAttributes(); // if 0 then no limit + +// Get Adapter index limit +$database->getLimitForIndexes(); + // Renames the attribute from old to new in the '$namespace_$collectionName' collection. $database->renameAttribute( collection: 'movies', old: 'genres', new: 'genres2' ); + +// Deletes the attribute in the '$namespace_$collectionName' collection. +$database->deleteAttribute( + collection: 'movies', + id: 'genres' +); ``` **Index Methods:** @@ -463,7 +611,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; // Id helpers -ID::unique(padding: 12), // Creates a id of length 7 + padding +ID::unique(padding: 12), // Creates an id of length 7 + padding ID::custom(id: 'my_user_3235') // Role helpers @@ -567,7 +715,7 @@ $database->getDocument( [Query::select(['name', 'director', 'year'])] ); -// Find a document with a query +// Find documents $possibleQueries = [ Query::equal('location',['Bangalore', 'California']), @@ -596,8 +744,54 @@ $database->find(collection: 'movies', queries: [ $possibleQueries[1], ], timeout: 1); //timeout is optional +// Find a document +$database->findOne(collection: 'movies', queries: [ + $possibleQueries[0], + $possibleQueries[1], +] +); + +// Get count of documents +$database->count(collection: 'movies', queries: [ + $possibleQueries[0], + $possibleQueries[1], +], +max: 1000); // max is optional +); + +// Get the sum of an attribute from all the documents +$database->sum(collection: 'movies', attribute: 'price', queries: [ + $possibleQueries[0], + $possibleQueries[1], +], +max: 0 // max = 0 means no limit +); + +// Encode Document +$collection = $database->getCollection('movies'); + +$document = $database->getDocument( + collection: 'movies', + id: $document->getId() +); + +$database->encode(collection: $collection, document: $document); + +// Decode Document +$database->decode(collection: $collection, document: $document); + // Delete a document -$database->deleteDocument(collection: 'movies', id: $document->getId()); +$database->deleteDocument( + collection: 'movies', + id: $document->getId() +); + +// Delete a cached document +$database->deleteCachedDocument( + collection: 'movies', + id: $document->getId() +); + ``` ### Adapters From 28f4fb45a5239aa834ea5107f011e48cff0fe93b Mon Sep 17 00:00:00 2001 From: faisalill Date: Mon, 10 Jul 2023 22:42:42 +0530 Subject: [PATCH 24/37] fixed indentation --- README.md | 67 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c98045f55..d5f778cd1 100644 --- a/README.md +++ b/README.md @@ -478,7 +478,10 @@ $attribute = new Document([ 'filters' => [], ]); -$database->checkAttribute($collection, $attribute); +$database->checkAttribute( + collection: $collection, + attribute: $attribute +); // Get Adapter attribute limit $database->getLimitForAttributes(); // if 0 then no limit @@ -639,7 +642,11 @@ $document = new Document([ 'active' => true, 'genres' => ['science fiction', 'action', 'comics'], ]); -$document = $database->createDocument(collection: 'movies', document: $document); + +$document = $database->createDocument( + collection: 'movies', + document: $document +); // Get which collection a document belongs to $document->getCollection(); @@ -739,32 +746,42 @@ $possibleQueries = ]; -$database->find(collection: 'movies', queries: [ - $possibleQueries[0], - $possibleQueries[1], -], timeout: 1); //timeout is optional +$database->find( + collection: 'movies', + queries: [ + $possibleQueries[0], + $possibleQueries[1], + ], + timeout: 1); //timeout is optional // Find a document -$database->findOne(collection: 'movies', queries: [ - $possibleQueries[0], - $possibleQueries[1], -] +$database->findOne( + collection: 'movies', + queries: [ + $possibleQueries[0], + $possibleQueries[1], + ] ); // Get count of documents -$database->count(collection: 'movies', queries: [ - $possibleQueries[0], - $possibleQueries[1], -], -max: 1000); // max is optional +$database->count( + collection: 'movies', + queries: [ + $possibleQueries[0], + $possibleQueries[1], + ], + max: 1000); // max is optional ); // Get the sum of an attribute from all the documents -$database->sum(collection: 'movies', attribute: 'price', queries: [ - $possibleQueries[0], - $possibleQueries[1], -], -max: 0 // max = 0 means no limit +$database->sum( + collection: 'movies', + attribute: 'price', + queries: [ + $possibleQueries[0], + $possibleQueries[1], + ], + max: 0 // max = 0 means no limit ); // Encode Document @@ -775,10 +792,16 @@ $document = $database->getDocument( id: $document->getId() ); -$database->encode(collection: $collection, document: $document); +$database->encode( + collection: $collection, + document: $document +); // Decode Document -$database->decode(collection: $collection, document: $document); +$database->decode( + collection: $collection, + document: $document +); // Delete a document $database->deleteDocument( From a219a71af9f48a12b043088e1ba2c66a312ccc98 Mon Sep 17 00:00:00 2001 From: faisalill Date: Thu, 13 Jul 2023 17:24:45 +0530 Subject: [PATCH 25/37] removed types in array --- README.md | 148 +++++++++++++++++++++++++----------------------------- 1 file changed, 69 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index d5f778cd1..b01bc9034 100644 --- a/README.md +++ b/README.md @@ -381,20 +381,19 @@ $database->deleteCachedCollection( **Attribute Methods:** ```php -$attributeTypes = -[ - Database::VAR_STRING, // use Utopia\Database\Database for these constants - Database::VAR_INTEGER, - Database::VAR_FLOAT, - Database::VAR_BOOLEAN, - Database::VAR_DATETIME, -]; +// Data types +Database::VAR_STRING +Database::VAR_INTEGER +Database::VAR_FLOAT +Database::VAR_BOOLEAN +Database::VAR_DATETIME + // Creates a new attribute named '$attributeName' in the '$namespace_$collectionName' collection. $database->createAttribute( collection: 'movies', id: 'name', - type: $attributeTypes[0], + type: Database::VAR_STRING, size: 128, required: true ); @@ -403,7 +402,7 @@ $database->createAttribute( $database->createAttribute( collection: 'movies', id: 'genres', - type: $attributeTypes[0] , + type: Database::VAR_STRING, size: 128, required: true, default: null, @@ -418,7 +417,7 @@ $database->createAttribute( $database-> updateAttribute( collection: 'movies', id: 'genres', - type: $attributeTypes[1] , + type: Database::VAR_STRING, size: 128, required: true, default: null, @@ -506,29 +505,26 @@ $database->deleteAttribute( **Index Methods:** ```php -$indexTypes = -[ - Database::INDEX_KEY, // use Utopia\Database\Database for these constants - Database::INDEX_FULLTEXT, - Database::INDEX_UNIQUE, - Database::INDEX_SPATIAL, - Database::INDEX_ARRAY -]; - -$insertionOrder = -[ - Database::ORDER_ASC, - Database::ORDER_DESC -]; +// Index types +Database::INDEX_KEY, +Database::INDEX_FULLTEXT +Database::INDEX_UNIQUE +Database::INDEX_SPATIAL +Database::INDEX_ARRAY + +// Insertion Order +Database::ORDER_ASC +Database::ORDER_DESC + // Creates a new index named '$indexName' in the '$namespace_$collectionName' collection. // Note: The size for the index will be taken from the size of the attribute $database->createIndex( collection: 'movies', - id: 'index1', $indexTypes[0], + id: 'index1', Database::INDEX_KEY, attributes: ['name', 'genres'], lengths: [128,128], - orders: [$insertionOrder[0], $insertionOrder[1]] + orders: [Database::ORDER_ASC, Database::ORDER_DESC] ); // Rename index from old to new in the '$namespace_$collectionName' collection. @@ -548,19 +544,17 @@ $database->deleteIndex( **Relationship Methods:** ```php -$typesOfRelation = -[ - Database::RELATION_ONE_TO_ONE, - Database::RELATION_ONE_TO_MANY, - Database::RELATION_MANY_TO_ONE, - Database::RELATION_MANY_TO_MANY -]; +// Relationship types +Database::RELATION_ONE_TO_ONE +Database::RELATION_ONE_TO_MANY +Database::RELATION_MANY_TO_ONE +Database::RELATION_MANY_TO_MANY // Creates a relationship between the two collections with the default reference attributes $database->createRelationship( collection: 'movies', relatedCollection: 'users', - $typesOfRelation[0], + Database::RELATION_ONE_TO_ONE,, twoWay: true ); @@ -569,30 +563,29 @@ $database->createRelationship( $database->createRelationship( collection: 'movies', relatedCollection: 'users', - $typesOfRelation[0], + Database::RELATION_ONE_TO_ONE, twoWay: true, id: 'movies_id', twoWayKey: 'users_id' ); -$onDeleteTypes = [ - Database::RELATION_MUTATE_CASCADE, - Database::RELATION_MUTATE_SET_NULL, - Database::RELATION_MUTATE_RESTRICT, -]; +// Relationship onDelete types +Database::RELATION_MUTATE_CASCADE, +Database::RELATION_MUTATE_SET_NULL, +Database::RELATION_MUTATE_RESTRICT, // Update the relationship with the default reference attributes $database->updateRelationship( collection: 'movies', id: 'users', - onDelete: $onDeleteTypes[0] + onDelete: Database::RELATION_MUTATE_CASCADE ); // Update the relationship with custom reference attributes $database->updateRelationship( collection: 'movies', id: 'users', - onDelete: $onDeleteTypes[0], + onDelete: Database::RELATION_MUTATE_CASCADE, newKey: 'movies_id', newTwoWayKey: 'users_id', twoWay: true @@ -676,14 +669,14 @@ $database->decreaseDocumentAttribute( ); // Update the value of an attribute in a document -$setTypes = -[ - Document::SET_TYPE_ASSIGN, - Document::SET_TYPE_APPEND, - Document::SET_TYPE_PREPEND -]; + +// Set types +Document::SET_TYPE_ASSIGN, +Document::SET_TYPE_APPEND, +Document::SET_TYPE_PREPEND + $document->setAttribute(key: 'name', 'Chris Smoove') - ->setAttribute(key: 'age', 33, $setTypes[0]); + ->setAttribute(key: 'age', 33, Document::SET_TYPE_ASSIGN); $database->updateDocument( collection: 'users', @@ -723,34 +716,32 @@ $database->getDocument( ); // Find documents -$possibleQueries = -[ - Query::equal('location',['Bangalore', 'California']), - Query::notEqual('location','New York'), - Query::lessThan('users',3000), - Query::lessThanEqual('users',10000), - Query::greaterThan('users',200), - Query::greaterThanEqual('users',250), - Query::contains('users',['Jhon', 'Smith']), - Query::between('price', start: 100, end: 1000), - Query::search('users','Jake'), - Query::select(['location','users']), - Query::orderDesc('users'), - Query::orderAsc('users'), - Query::isNull('users'), - Query::isNotNull('users'), - Query::startsWith('name','Jhon'), - Query::endsWith('name','Smith'), - Query::limit(35), - Query::offset(0), -]; +// Query Types +Query::equal(attribute: "...", values: ["...", "..."]), +Query::notEqual(attribute: "...", value: "..."), +Query::lessThan(attribute: "...", value: 100), +Query::lessThanEqual(attribute: "...", value: 1000), +Query::greaterThan(attribute: "...", value: 1000), +Query::greaterThanEqual(attribute: "...", value: ...), +Query::contains(attribute: "...", values: ["...", "..."]), +Query::between(attribute: "...", start: 100, end: 1000), +Query::search(attribute: "...", value: "..."), +Query::select(attributes: ["...", "..."]), +Query::orderDesc(attribute: "..."), +Query::orderAsc(attribute: "..."), +Query::isNull(attribute: "..."), +Query::isNotNull(attribute: "..."), +Query::startsWith(attribute: "...", value: "..."), +Query::endsWith(attribute: "...", value: "..."), +Query::limit(value: 35), +Query::offset(value: 0), $database->find( collection: 'movies', queries: [ - $possibleQueries[0], - $possibleQueries[1], + Query::equal(attribute: 'name', values: ['Captain Marvel']), + Query::notEqual(attribute: 'year', values: [2019]) ], timeout: 1); //timeout is optional @@ -758,8 +749,8 @@ $database->find( $database->findOne( collection: 'movies', queries: [ - $possibleQueries[0], - $possibleQueries[1], + Query::equal(attribute: 'name', values: ['Captain Marvel']), + Query::lessThan(attribute: 'year', value: 2019) ] ); @@ -767,8 +758,8 @@ $database->findOne( $database->count( collection: 'movies', queries: [ - $possibleQueries[0], - $possibleQueries[1], + Query::equal(attribute: 'name', values: ['Captain Marvel']), + Query::greaterThan(attribute: 'year', value: 2019) ], max: 1000); // max is optional ); @@ -778,8 +769,7 @@ $database->sum( collection: 'movies', attribute: 'price', queries: [ - $possibleQueries[0], - $possibleQueries[1], + Query::greaterThan(attribute: 'year', value: 2019) ], max: 0 // max = 0 means no limit ); From 4e5fde12135afe1af9797c9fac28dd88db52fcde Mon Sep 17 00:00:00 2001 From: faisalill Date: Mon, 17 Jul 2023 17:27:58 +0530 Subject: [PATCH 26/37] removed event types array and changed stuff --- README.md | 63 ++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index b01bc9034..1d735780e 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -## Code Examples +## Examples ### Setting up different database adapters @@ -195,7 +195,7 @@ $database = new Database(new Mongo($client), $cache);
-> ## Below methods are available for all database adapters. +> Following methods are available for all database adapters.
@@ -219,15 +219,13 @@ $database->setDefaultDatabase( name: 'dbName' ); -// Creates a new database for MySql, MariaDB, SQLite. For Postgres it creates a schema. +// Creates a new database. // Uses default database as the name. $database->create(); // Returns an array of all databases $database->list(); - - // Delete database $database->delete( name: 'mydb' @@ -239,7 +237,7 @@ $database->ping(); // Check if database exists $database->exists( database: 'mydb' -); // for database +); // Check if collection exists $database->exists( @@ -248,35 +246,34 @@ $database->exists( ); // Listen to events -$eventType = -[ - Database::EVENT_ALL - Database::EVENT_DATABASE_CREATE, - Database::EVENT_DATABASE_LIST, - Database::EVENT_COLLECTION_CREATE, - Database::EVENT_COLLECTION_LIST, - Database::EVENT_COLLECTION_READ, - Database::EVENT_ATTRIBUTE_CREATE, - Database::EVENT_ATTRIBUTE_UPDATE, - Database::EVENT_INDEX_CREATE, - Database::EVENT_DOCUMENT_CREATE, - Database::EVENT_DOCUMENT_UPDATE, - Database::EVENT_DOCUMENT_READ, - Database::EVENT_DOCUMENT_FIND, - Database::EVENT_DOCUMENT_FIND, - Database::EVENT_DOCUMENT_COUNT, - Database::EVENT_DOCUMENT_SUM, - Database::EVENT_DOCUMENT_INCREASE, - Database::EVENT_DOCUMENT_DECREASE, - Database::EVENT_INDEX_DELETE, - Database::EVENT_DOCUMENT_DELETE, - Database::EVENT_ATTRIBUTE_DELETE, - Database::EVENT_COLLECTION_DELETE, - Database::EVENT_DATABASE_DELETE, -]; + +// Event Types +Database::EVENT_ALL +Database::EVENT_DATABASE_CREATE, +Database::EVENT_DATABASE_LIST, +Database::EVENT_COLLECTION_CREATE, +Database::EVENT_COLLECTION_LIST, +Database::EVENT_COLLECTION_READ, +Database::EVENT_ATTRIBUTE_CREATE, +Database::EVENT_ATTRIBUTE_UPDATE, +Database::EVENT_INDEX_CREATE, +Database::EVENT_DOCUMENT_CREATE, +Database::EVENT_DOCUMENT_UPDATE, +Database::EVENT_DOCUMENT_READ, +Database::EVENT_DOCUMENT_FIND, +Database::EVENT_DOCUMENT_FIND, +Database::EVENT_DOCUMENT_COUNT, +Database::EVENT_DOCUMENT_SUM, +Database::EVENT_DOCUMENT_INCREASE, +Database::EVENT_DOCUMENT_DECREASE, +Database::EVENT_INDEX_DELETE, +Database::EVENT_DOCUMENT_DELETE, +Database::EVENT_ATTRIBUTE_DELETE, +Database::EVENT_COLLECTION_DELETE, +Database::EVENT_DATABASE_DELETE, $database->on( - string: $eventType[0], + string: Database::EVENT_ALL, callable: function($event, $data) { // Do something } From 1c9dfc077099b91d8b151acddb06f9210079ae05 Mon Sep 17 00:00:00 2001 From: faisalill Date: Tue, 18 Jul 2023 23:24:48 +0530 Subject: [PATCH 27/37] Improve Navigation and Enhance the documentation Add Table of contents for readme.md and contributing.md Update limitations for mariadb,mysql,postgres,sqlite and mongodb Add tests in contributing.md Add contributing section in readme.md Fix code as per review from steven --- CONTRIBUTING.md | 89 +++++++++++++++++++++++- README.md | 178 +++++++++++++++++++----------------------------- 2 files changed, 156 insertions(+), 111 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 274c9e942..c243c6b6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,18 @@ We would ❀️ for you to contribute to Utopia-php and help make it better! We want contributing to Utopia-php to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, new docs as well as updates and tweaks, blog posts, workshops, and more. -## How to Start? +## Table of contents + +1. [How to Start](#how-to-start) +1. [Code of Conduct](#code-of-conduct) +1. [Submit a Pull Request](#submit-a-pull-request) +1. [Introducing New Features](#introducing-new-features) +1. [Adding A New Adapter](#adding-a-new-adapter) +1. [Tests](#tests) +1. [Other Ways to Help](#other-ways-to-help) + + +## How to Start If you are worried or don’t know where to start, check out our next section explaining what kind of help we could use and where can you get involved. You can reach out with questions to [Eldad Fux (@eldadfux)](https://twitter.com/eldadfux) or anyone from the [Appwrite team on Discord](https://discord.gg/GSeTUeA). You can also submit an issue, and a maintainer can guide you! @@ -10,7 +21,7 @@ If you are worried or don’t know where to start, check out our next section ex Help us keep the community open and inclusive. Please read and follow our [Code of Conduct](https://github.com/appwrite/appwrite/blob/master/CODE_OF_CONDUCT.md). -## Submit a Pull Request πŸš€ +## Submit a Pull Request Branch naming convention is as following @@ -69,6 +80,80 @@ This is also important for the Utopia-php lead developers to be able to give tec You can follow our [Adding new Database Adapter](docs/add-new-adapter.md) tutorial to add new database support in this library. +## Tests + +To run tests, you first need to bring up the example Docker stack with the following command: + +```bash +docker compose up -d --build +``` + +To run all unit tests, use the following Docker command: + +```bash +docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests +``` + +To run tests for a single file, use the following Docker command structure: + +```bash +docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests/Database/[FILE_PATH] +``` + +To run static code analysis, use the following Psalm command: + +```bash +docker compose exec tests vendor/bin/psalm --show-info=true +``` + +### Load testing + +Three commands have been added to `bin/` to fill, index, and query the DB to test changes: + +- `bin/load` invokes `bin/tasks/load.php` +- `bin/index` invokes `bin/tasks/index.php` +- `bin/query` invokes `bin/tasks/query.php` + +To test your DB changes under load: + +#### Load the database + +```bash +docker compose exec tests bin/load --adapter=[adapter] --limit=[limit] [--name=[name]] + +# [adapter]: either 'mongodb' or 'mariadb', no quotes +# [limit]: integer of total documents to generate +# [name]: (optional) name for new database +``` + +#### Create indexes + +```bash +docker compose exec tests bin/index --adapter=[adapter] --name=[name] + +# [adapter]: either 'mongodb' or 'mariadb', no quotes +# [name]: name of filled database by bin/load +``` + +#### Run Query Suite + +```bash +docker compose exec tests bin/query --adapter=[adapter] --limit=[limit] --name=[name] + +# [adapter]: either 'mongodb' or 'mariadb', no quotes +# [limit]: integer of query limit (default 25) +# [name]: name of filled database by bin/load +``` + +#### Visualize Query Results + +```bash +docker compose exec tests bin/compare +``` + +Navigate to `localhost:8708` to visualize query results. + + ## Other Ways to Help Pull requests are great, but there are many other areas where you can help Utopia-php. diff --git a/README.md b/README.md index 1d735780e..d92110135 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,27 @@ Utopia framework database library is simple and lite library for managing applic Although this library is part of the [Utopia Framework](https://github.com/utopia-php/framework) project it is dependency free, and can be used as standalone with any other PHP project or framework. +## Table of contents + +1. [Getting Started](#getting-started) + 1. [Concepts](#concepts) + 1. [Filters](#filters) + 1. [Reserved Attributes](#reserved-attributes) + 1. [Attribute Types](#attribute-types) + 1. [Supported Databases](#supported-databases) + 1. [Limitations](#limitations) + +1. [Usage](#usage) + 1. [Connecting to a database](#connecting-to-a-database) + 1. [Database Methods](#database-methods) + 1. [Collection Methods](#collection-methods) + 1. [Attribute Methods](#attribute-methods) + 1. [Index Methods](#index-methods) + 1. [Relationship Methods](#relationship-methods) + 1. [Document Methods](#document-methods) +1. [System Requirements](#system-requirements) +1. [Contributing](#contributing) + ## Getting Started Install using composer: @@ -21,7 +42,7 @@ composer require utopia-php/database A list of the utopia/php concepts and their relevant equivalent using the different adapters - **Database** - An instance of the utopia/database library that abstracts one of the supported adapters and provides a unified API for CRUD operation and queries on a specific schema or isolated scope inside the underlining database. -- **Adapter** - An implementation of an underlying database engine that this library can support - below is a list of [supported adapters](#adapters) and supported capabilities for each Adapter. +- **Adapter** - An implementation of an underlying database engine that this library can support - below is a list of [supported databases](#supported-databases) and supported capabilities for each Database. - **Collection** - A set of documents stored on the same adapter scope. For SQL-based adapters, this will be equivalent to a table. For a No-SQL adapter, this will equivalent to a native collection. - **Document** - A simple JSON object that will be stored in one of the utopia/database collections. For SQL-based adapters, this will be equivalent to a row. For a No-SQL adapter, this will equivalent to a native document. - **Attribute** - A simple document attribute. For SQL-based adapters, this will be equivalent to a column. For a No-SQL adapter, this will equivalent to a native document field. @@ -44,9 +65,45 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -## Examples +### Limitations + +#### MariaDB, MySQL, Postgres, SQLite +- ID max size can be 255 bytes +- ID can only contain [^A-Za-z0-9] and symbols `_` `-` +- Document max size is 65535 bytes +- Collection can have a max of 1017 attributes +- Collection can have a max of 64 indexes +- Index value max size is 768 bytes. Values over 768 bytes are truncated +- String max size is 4294967295 characters +- Integer max size is 4294967295 + +#### MongoDB +- ID max size can be 255 bytes +- ID can only contain [^A-Za-z0-9] and symbols `_` `-` +- Document can have unrestricted size +- Collection can have unrestricted amount of attributes +- Collection can have a max of 64 indexes +- Index value can have unrestricted size +- String max size is 2147483647 characters +- Integer max size is 4294967295 + +### Supported Databases -### Setting up different database adapters +Below is a list of supported databases, and their compatibly tested versions alongside a list of supported features and relevant limits. + +| Adapter | Status | Version | +| -------- | ------ | ------- | +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLite | βœ… | 3.38 | + +` βœ… - supported, πŸ›  - work in progress` + +## Usage + +### Connecting to a database **MariaDB:** @@ -193,13 +250,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new Mongo($client), $cache); ``` -
- -> Following methods are available for all database adapters. - -
- -**Database Methods:** +#### Database Methods ```php @@ -286,7 +337,7 @@ $database->getAdapter(); $database->getKeywords(); ``` -**Collection Methods:** +### Collection Methods ```php // Creates two new collection named '$namespace_$collectionName' with attribute names '_id', '_uid', '_createdAt', '_updatedAt', '_permissions' @@ -375,7 +426,7 @@ $database->deleteCachedCollection( ); ``` -**Attribute Methods:** +### Attribute Methods ```php // Data types @@ -499,7 +550,7 @@ $database->deleteAttribute( ); ``` -**Index Methods:** +### Index Methods ```php // Index types @@ -538,7 +589,7 @@ $database->deleteIndex( ); ``` -**Relationship Methods:** +### Relationship Methods ```php // Relationship types @@ -595,7 +646,7 @@ $database->deleteRelationship( ); ``` -**Document Methods:** +### Document Methods ```php use Utopia\Database\Document; @@ -804,105 +855,14 @@ $database->deleteCachedDocument( ``` -### Adapters - -Below is a list of supported adapters, and their compatibly tested versions alongside a list of supported features and relevant limits. - -| Adapter | Status | Version | -| -------- | ------ | ------- | -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLlite | βœ… | 3.38 | - -` βœ… - supported, πŸ›  - work in progress` - -## Limitations (to be completed per adapter) - -- ID max size can be 255 bytes -- ID can only contain [^A-Za-z0-9] and symbols `_` `-` -- Document max size is x bytes -- Collection can have a max of x attributes -- Collection can have a max of x indexes -- Index value max size is x bytes. Values over x bytes are truncated - ## System Requirements Utopia Framework requires PHP 8.0 or later. We recommend using the latest PHP version whenever possible. -## Tests - -To run tests, you first need to bring up the example Docker stack with the following command: - -```bash -docker compose up -d --build -``` - -To run all unit tests, use the following Docker command: - -```bash -docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests -``` - -To run tests for a single file, use the following Docker command structure: - -```bash -docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests/Database/[FILE_PATH] -``` - -To run static code analysis, use the following Psalm command: - -```bash -docker compose exec tests vendor/bin/psalm --show-info=true -``` - -### Load testing - -Three commands have been added to `bin/` to fill, index, and query the DB to test changes: - -- `bin/load` invokes `bin/tasks/load.php` -- `bin/index` invokes `bin/tasks/index.php` -- `bin/query` invokes `bin/tasks/query.php` - -To test your DB changes under load: - -#### Load the database - -```bash -docker compose exec tests bin/load --adapter=[adapter] --limit=[limit] [--name=[name]] - -# [adapter]: either 'mongodb' or 'mariadb', no quotes -# [limit]: integer of total documents to generate -# [name]: (optional) name for new database -``` - -#### Create indexes - -```bash -docker compose exec tests bin/index --adapter=[adapter] --name=[name] - -# [adapter]: either 'mongodb' or 'mariadb', no quotes -# [name]: name of filled database by bin/load -``` - -#### Run Query Suite - -```bash -docker compose exec tests bin/query --adapter=[adapter] --limit=[limit] --name=[name] - -# [adapter]: either 'mongodb' or 'mariadb', no quotes -# [limit]: integer of query limit (default 25) -# [name]: name of filled database by bin/load -``` - -#### Visualize Query Results - -```bash -docker compose exec tests bin/compare -``` +## Contributing -Navigate to `localhost:8708` to visualize query results. +Thank you for considering contributing to the Utopia Framework! +Checkout the [CONTRIBUTING.md](https://github.com/utopia-php/database/blob/main/CONTRIBUTING.md) file for more information. ## Copyright and license From e5a584940bedbef3565a7815c3a37298f463a75c Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 19 Jul 2023 07:38:55 +0530 Subject: [PATCH 28/37] Remove table of contents and Enhance the readme and contributing.md Remove table of contents from readme.md and contributing.md Change supported databases table from horizontal to vertical. Fix improper headings and title cases --- CONTRIBUTING.md | 15 ++---------- README.md | 63 ++++++++++++++++++++----------------------------- 2 files changed, 28 insertions(+), 50 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c243c6b6c..be1a2414a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,18 +2,7 @@ We would ❀️ for you to contribute to Utopia-php and help make it better! We want contributing to Utopia-php to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, new docs as well as updates and tweaks, blog posts, workshops, and more. -## Table of contents - -1. [How to Start](#how-to-start) -1. [Code of Conduct](#code-of-conduct) -1. [Submit a Pull Request](#submit-a-pull-request) -1. [Introducing New Features](#introducing-new-features) -1. [Adding A New Adapter](#adding-a-new-adapter) -1. [Tests](#tests) -1. [Other Ways to Help](#other-ways-to-help) - - -## How to Start +## How to Start? If you are worried or don’t know where to start, check out our next section explaining what kind of help we could use and where can you get involved. You can reach out with questions to [Eldad Fux (@eldadfux)](https://twitter.com/eldadfux) or anyone from the [Appwrite team on Discord](https://discord.gg/GSeTUeA). You can also submit an issue, and a maintainer can guide you! @@ -21,7 +10,7 @@ If you are worried or don’t know where to start, check out our next section ex Help us keep the community open and inclusive. Please read and follow our [Code of Conduct](https://github.com/appwrite/appwrite/blob/master/CODE_OF_CONDUCT.md). -## Submit a Pull Request +## Submit a Pull Request πŸš€ Branch naming convention is as following diff --git a/README.md b/README.md index d92110135..33bb102d4 100644 --- a/README.md +++ b/README.md @@ -8,27 +8,6 @@ Utopia framework database library is simple and lite library for managing applic Although this library is part of the [Utopia Framework](https://github.com/utopia-php/framework) project it is dependency free, and can be used as standalone with any other PHP project or framework. -## Table of contents - -1. [Getting Started](#getting-started) - 1. [Concepts](#concepts) - 1. [Filters](#filters) - 1. [Reserved Attributes](#reserved-attributes) - 1. [Attribute Types](#attribute-types) - 1. [Supported Databases](#supported-databases) - 1. [Limitations](#limitations) - -1. [Usage](#usage) - 1. [Connecting to a database](#connecting-to-a-database) - 1. [Database Methods](#database-methods) - 1. [Collection Methods](#collection-methods) - 1. [Attribute Methods](#attribute-methods) - 1. [Index Methods](#index-methods) - 1. [Relationship Methods](#relationship-methods) - 1. [Document Methods](#document-methods) -1. [System Requirements](#system-requirements) -1. [Contributing](#contributing) - ## Getting Started Install using composer: @@ -91,21 +70,31 @@ The database document interface only supports primitives types (`strings`, `inte Below is a list of supported databases, and their compatibly tested versions alongside a list of supported features and relevant limits. -| Adapter | Status | Version | -| -------- | ------ | ------- | -| MariaDB | βœ… | 10.5 | -| MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | -| MongoDB | βœ… | 5.0 | -| SQLite | βœ… | 3.38 | +**MariaDB** +* Status: βœ… +* Version: 10.5 + +**MySQL** +* Status: βœ… +* Version: 8.0 + +**Postgres** +* Status: πŸ›  (Work in progress) +* Version: 13.0 + +**MongoDB** +* Status: βœ… +* Version: 5.0 -` βœ… - supported, πŸ›  - work in progress` +**SQLite** +* Status: βœ… +*Version: 3.38 ## Usage -### Connecting to a database +### Connecting to a Database -**MariaDB:** +#### MariaDB: ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -136,7 +125,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new MariaDB($pdo), $cache); ``` -**MySQL:** +#### MySQL: ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -167,7 +156,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new MySql($pdo), $cache); ``` -**Postgres:** +#### Postgres: ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -198,7 +187,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new Postgres($pdo), $cache); ``` -**SQLite:** +#### SQLite: ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -226,7 +215,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new SQLite($pdo), $cache); ``` -**MongoDB:** +#### MongoDB: ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -250,7 +239,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new Mongo($client), $cache); ``` -#### Database Methods +### Database Methods ```php @@ -864,6 +853,6 @@ Utopia Framework requires PHP 8.0 or later. We recommend using the latest PHP ve Thank you for considering contributing to the Utopia Framework! Checkout the [CONTRIBUTING.md](https://github.com/utopia-php/database/blob/main/CONTRIBUTING.md) file for more information. -## Copyright and license +## Copyright and License The MIT License (MIT) [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file From 0d10f7df8ff8cc157e8b1cb17fc6de6430f6e7a0 Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 19 Jul 2023 07:54:59 +0530 Subject: [PATCH 29/37] Fix Indentation and Remove colon from headings --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 33bb102d4..733973bba 100644 --- a/README.md +++ b/README.md @@ -88,13 +88,13 @@ Below is a list of supported databases, and their compatibly tested versions alo **SQLite** * Status: βœ… -*Version: 3.38 +* Version: 3.38 ## Usage ### Connecting to a Database -#### MariaDB: +#### MariaDB ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -125,7 +125,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new MariaDB($pdo), $cache); ``` -#### MySQL: +#### MySQL ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -156,7 +156,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new MySql($pdo), $cache); ``` -#### Postgres: +#### Postgres ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -187,7 +187,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new Postgres($pdo), $cache); ``` -#### SQLite: +#### SQLite ```php require_once __DIR__ . '/vendor/autoload.php'; @@ -215,7 +215,7 @@ $cache = new Cache(new Memory()); // or use any cache adapter you wish $database = new Database(new SQLite($pdo), $cache); ``` -#### MongoDB: +#### MongoDB ```php require_once __DIR__ . '/vendor/autoload.php'; From 21f173ddb6ae2fed95e3e32d6bd992565b302786 Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 19 Jul 2023 07:57:48 +0530 Subject: [PATCH 30/37] Move Limitations section below Supported databases section --- README.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 733973bba..4204fbfc7 100644 --- a/README.md +++ b/README.md @@ -44,28 +44,6 @@ Attribute filters are functions that manipulate attributes before saving them to The database document interface only supports primitives types (`strings`, `integers`, `floats`, and `booleans`) translated to their native database types for each of the relevant database adapters. Complex types like arrays or objects will be encoded to JSON strings when stored and decoded back when fetched from their adapters. -### Limitations - -#### MariaDB, MySQL, Postgres, SQLite -- ID max size can be 255 bytes -- ID can only contain [^A-Za-z0-9] and symbols `_` `-` -- Document max size is 65535 bytes -- Collection can have a max of 1017 attributes -- Collection can have a max of 64 indexes -- Index value max size is 768 bytes. Values over 768 bytes are truncated -- String max size is 4294967295 characters -- Integer max size is 4294967295 - -#### MongoDB -- ID max size can be 255 bytes -- ID can only contain [^A-Za-z0-9] and symbols `_` `-` -- Document can have unrestricted size -- Collection can have unrestricted amount of attributes -- Collection can have a max of 64 indexes -- Index value can have unrestricted size -- String max size is 2147483647 characters -- Integer max size is 4294967295 - ### Supported Databases Below is a list of supported databases, and their compatibly tested versions alongside a list of supported features and relevant limits. @@ -90,6 +68,28 @@ Below is a list of supported databases, and their compatibly tested versions alo * Status: βœ… * Version: 3.38 +### Limitations + +#### MariaDB, MySQL, Postgres, SQLite +- ID max size can be 255 bytes +- ID can only contain [^A-Za-z0-9] and symbols `_` `-` +- Document max size is 65535 bytes +- Collection can have a max of 1017 attributes +- Collection can have a max of 64 indexes +- Index value max size is 768 bytes. Values over 768 bytes are truncated +- String max size is 4294967295 characters +- Integer max size is 4294967295 + +#### MongoDB +- ID max size can be 255 bytes +- ID can only contain [^A-Za-z0-9] and symbols `_` `-` +- Document can have unrestricted size +- Collection can have unrestricted amount of attributes +- Collection can have a max of 64 indexes +- Index value can have unrestricted size +- String max size is 2147483647 characters +- Integer max size is 4294967295 + ## Usage ### Connecting to a Database From 838011cf93f4464d4dfeb2ae2b05aa721615546b Mon Sep 17 00:00:00 2001 From: faisalill Date: Sat, 22 Jul 2023 20:56:20 +0530 Subject: [PATCH 31/37] Change Supported Databases table back to Horizontal --- README.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4204fbfc7..940c346cd 100644 --- a/README.md +++ b/README.md @@ -48,25 +48,16 @@ The database document interface only supports primitives types (`strings`, `inte Below is a list of supported databases, and their compatibly tested versions alongside a list of supported features and relevant limits. -**MariaDB** -* Status: βœ… -* Version: 10.5 - -**MySQL** -* Status: βœ… -* Version: 8.0 - -**Postgres** -* Status: πŸ›  (Work in progress) -* Version: 13.0 - -**MongoDB** -* Status: βœ… -* Version: 5.0 - -**SQLite** -* Status: βœ… -* Version: 3.38 +| Adapter | Status | Version | +|---------|---------|---| +| MariaDB | βœ… | 10.5 | +| MySQL | βœ… | 8.0 | +| Postgres | πŸ›  | 13.0 | +| MongoDB | βœ… | 5.0 | +| SQLlite | βœ… | 3.38 | + +` βœ… - supported ` +` πŸ›  - work in progress` ### Limitations From 4306ed5388ff5e9ba6e92a47d90ade98f37f70b9 Mon Sep 17 00:00:00 2001 From: faisalill Date: Sat, 22 Jul 2023 20:58:17 +0530 Subject: [PATCH 32/37] Change Legends in Supported Datbases to vertical --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 940c346cd..e17e106b5 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Below is a list of supported databases, and their compatibly tested versions alo | SQLlite | βœ… | 3.38 | ` βœ… - supported ` + ` πŸ›  - work in progress` ### Limitations From fe0665607c14fc8b39254d29c878625ecbc86f56 Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 26 Jul 2023 07:23:29 +0530 Subject: [PATCH 33/37] Made changes as per review --- CONTRIBUTING.md | 4 +- README.md | 106 +++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be1a2414a..0057824ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,10 +89,10 @@ To run tests for a single file, use the following Docker command structure: docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests/Database/[FILE_PATH] ``` -To run static code analysis, use the following Psalm command: +To run static code analysis, use the following phpstan command: ```bash -docker compose exec tests vendor/bin/psalm --show-info=true +composer check ``` ### Load testing diff --git a/README.md b/README.md index e17e106b5..94459f6bf 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Below is a list of supported databases, and their compatibly tested versions alo |---------|---------|---| | MariaDB | βœ… | 10.5 | | MySQL | βœ… | 8.0 | -| Postgres | πŸ›  | 13.0 | +| Postgres | βœ… | 13.0 | | MongoDB | βœ… | 5.0 | | SQLlite | βœ… | 3.38 | @@ -305,8 +305,8 @@ Database::EVENT_COLLECTION_DELETE, Database::EVENT_DATABASE_DELETE, $database->on( - string: Database::EVENT_ALL, - callable: function($event, $data) { + Database::EVENT_ALL, + function($event, $data) { // Do something } ); @@ -332,9 +332,9 @@ $attributes = [ new Document([ '$id' => ID::unique(), '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) ], 'name' => 'Jhon', 'age' => 20 @@ -342,9 +342,9 @@ $attributes = [ new Document([ '$id' => ID::unique(), '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) ], 'name' => 'Doe', 'age' => 34 @@ -353,18 +353,18 @@ $attributes = [ $indexes = [ new Document([ - '$id' => ID::unique(), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name'], - 'lengths' => [256], - 'orders' => ['ASC'], + '$id' => ID::unique(), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name'], + 'lengths' => [256], + 'orders' => ['ASC'], ]), new Document([ - '$id' => ID::unique(), - 'type' => Database::INDEX_KEY, - 'attributes' => ['name', 'age'], - 'lengths' => [128, 128], - 'orders' => ['ASC'], + '$id' => ID::unique(), + 'type' => Database::INDEX_KEY, + 'attributes' => ['name', 'age'], + 'lengths' => [128, 128], + 'orders' => ['ASC'], ]) ]; @@ -583,7 +583,7 @@ Database::RELATION_MANY_TO_MANY $database->createRelationship( collection: 'movies', relatedCollection: 'users', - Database::RELATION_ONE_TO_ONE,, + type: Database::RELATION_ONE_TO_ONE,, twoWay: true ); @@ -592,7 +592,7 @@ $database->createRelationship( $database->createRelationship( collection: 'movies', relatedCollection: 'users', - Database::RELATION_ONE_TO_ONE, + type: Database::RELATION_ONE_TO_ONE, twoWay: true, id: 'movies_id', twoWayKey: 'users_id' @@ -636,12 +636,32 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; // Id helpers -ID::unique(padding: 12), // Creates an id of length 7 + padding +ID::unique(padding: 12) // Creates an id of length 7 + padding ID::custom(id: 'my_user_3235') // Role helpers -Role::any(), -Role::user(ID::unique()) +Role::any() +Role::guests() +Role::user( + identifier: ID::unique() + status: 'active' //optional + ) +Role::users() +Role::team( + identifier: ID::unique() + ) +Role::team( + identifier: ID::unique() + dimension: '123' //team:id/dimension + ) +Role::label( + identifier: 'admin' + ) +Role::members( + identifier: ID::unique() + ) + + // Permission helpers Permission::read(Role::any()), @@ -657,6 +677,7 @@ $document = new Document([ Permission::update(Role::user(ID::unique(12))), Permission::delete(Role::user($customId)), ], + '$id' => ID::unique(), 'name' => 'Captain Marvel', 'director' => 'Anna Boden & Ryan Fleck', 'year' => 2019, @@ -700,9 +721,9 @@ $database->decreaseDocumentAttribute( // Update the value of an attribute in a document // Set types -Document::SET_TYPE_ASSIGN, -Document::SET_TYPE_APPEND, -Document::SET_TYPE_PREPEND +Document::SET_TYPE_ASSIGN, // Assign the new value directly +Document::SET_TYPE_APPEND, // Append the new value to end of the array +Document::SET_TYPE_PREPEND // Prepend the new value to start of the array $document->setAttribute(key: 'name', 'Chris Smoove') ->setAttribute(key: 'age', 33, Document::SET_TYPE_ASSIGN); @@ -741,7 +762,9 @@ $database->getDocument( $database->getDocument( collection: 'movies', id: $document->getId(), - [Query::select(['name', 'director', 'year'])] + queries: [ + Query::select(['name', 'director', 'year']) + ] ); // Find documents @@ -772,7 +795,8 @@ $database->find( Query::equal(attribute: 'name', values: ['Captain Marvel']), Query::notEqual(attribute: 'year', values: [2019]) ], - timeout: 1); //timeout is optional + timeout: 1 //timeout is optional +); // Find a document $database->findOne( @@ -790,7 +814,7 @@ $database->count( Query::equal(attribute: 'name', values: ['Captain Marvel']), Query::greaterThan(attribute: 'year', value: 2019) ], - max: 1000); // max is optional + max: 1000); // Max is optional ); // Get the sum of an attribute from all the documents @@ -800,28 +824,9 @@ $database->sum( queries: [ Query::greaterThan(attribute: 'year', value: 2019) ], - max: 0 // max = 0 means no limit + max: null // max = null means no limit ); -// Encode Document -$collection = $database->getCollection('movies'); - -$document = $database->getDocument( - collection: 'movies', - id: $document->getId() -); - -$database->encode( - collection: $collection, - document: $document -); - -// Decode Document -$database->decode( - collection: $collection, - document: $document -); - // Delete a document $database->deleteDocument( collection: 'movies', @@ -829,6 +834,7 @@ $database->deleteDocument( ); // Delete a cached document +Note: Cached Documents or Collections are automatically deleted when a document or collection is updated or deleted $database->deleteCachedDocument( collection: 'movies', id: $document->getId() From c3398921af42f7d1169199f37ceb7afcc359320f Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 26 Jul 2023 07:27:43 +0530 Subject: [PATCH 34/37] Fix Role indentation --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 94459f6bf..243c47b23 100644 --- a/README.md +++ b/README.md @@ -645,21 +645,21 @@ Role::guests() Role::user( identifier: ID::unique() status: 'active' //optional - ) +) Role::users() Role::team( identifier: ID::unique() - ) +) Role::team( identifier: ID::unique() dimension: '123' //team:id/dimension - ) +) Role::label( identifier: 'admin' - ) +) Role::members( identifier: ID::unique() - ) +) From 52b38ad92c7c2c2714fd6538cce25aa8ae5447bd Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 26 Jul 2023 07:31:02 +0530 Subject: [PATCH 35/37] Add note to Document Set Types --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 243c47b23..8e01da9c0 100644 --- a/README.md +++ b/README.md @@ -724,6 +724,7 @@ $database->decreaseDocumentAttribute( Document::SET_TYPE_ASSIGN, // Assign the new value directly Document::SET_TYPE_APPEND, // Append the new value to end of the array Document::SET_TYPE_PREPEND // Prepend the new value to start of the array +Note: Using append/prepend with an attribute which is not an array, it will be set to an array containing the new value. $document->setAttribute(key: 'name', 'Chris Smoove') ->setAttribute(key: 'age', 33, Document::SET_TYPE_ASSIGN); From 927db4f102ca91c3c03343d98d2ffb6b86637c9e Mon Sep 17 00:00:00 2001 From: faisalill Date: Wed, 26 Jul 2023 07:34:01 +0530 Subject: [PATCH 36/37] Remove extra brackets --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e01da9c0..f206e25f6 100644 --- a/README.md +++ b/README.md @@ -815,7 +815,7 @@ $database->count( Query::equal(attribute: 'name', values: ['Captain Marvel']), Query::greaterThan(attribute: 'year', value: 2019) ], - max: 1000); // Max is optional + max: 1000 // Max is optional ); // Get the sum of an attribute from all the documents From 7c3b5c57dcdc069e6f6ede46559639a2f1381218 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 26 Jul 2023 15:00:02 -0400 Subject: [PATCH 37/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f206e25f6..f9792d101 100644 --- a/README.md +++ b/README.md @@ -583,7 +583,7 @@ Database::RELATION_MANY_TO_MANY $database->createRelationship( collection: 'movies', relatedCollection: 'users', - type: Database::RELATION_ONE_TO_ONE,, + type: Database::RELATION_ONE_TO_ONE, twoWay: true );