utopia-php/database

一个简单的库,用于使用多个数据库适配器管理应用程序的持久性

1.0.0-RC1 2024-08-09 17:41 UTC

This package is auto-updated.

Last update: 2024-09-18 04:02:46 UTC


README

Build Status Total Downloads Discord

Utopia框架数据库库是一个简单且轻量级的库,用于使用多个数据库适配器管理应用程序的持久性。这个库旨在尽可能简单且易于学习和使用。这个库由Appwrite团队维护。

尽管这个库是Utopia Framework项目的一部分,但它没有依赖,可以用作任何其他PHP项目或框架的独立组件。

入门

使用composer安装

composer require utopia-php/database

概念

utopia/php的概念列表及其在不同适配器中使用的相关等效项

  • 数据库 - utopia/database库的一个实例,它抽象了一个支持的适配器,并为在底层数据库中的特定模式或隔离作用域内提供了CRUD操作和查询的统一API。
  • 适配器 - 该库可以支持的底层数据库引擎的实现。以下是支持的数据库及其各自支持的数据库功能的列表。
  • 集合 - 存储在相同适配器作用域中的一组文档。对于基于SQL的适配器,这相当于一个表。对于NoSQL适配器,这相当于一个本地集合。
  • 文档 - 一个简单的JSON对象,将被存储在utopia/database集合之一中。对于基于SQL的适配器,这相当于一行。对于NoSQL适配器,这相当于一个本地文档。
  • 属性 - 一个简单的文档属性。对于基于SQL的适配器,这相当于一列。对于NoSQL适配器,这相当于本地文档字段。
  • 索引 - 一个简单的集合索引,用于提高数据库查询的性能。
  • 权限 - 使用权限,您可以决定哪些角色可以读取、创建、更新和删除特定文档。特殊属性$permissions用于在集合中为每个文档存储权限元数据。权限角色可以是任何您想要的字符串。您可以使用$authorization->addRole()将新角色委派给您的用户,一旦获得新角色,用户将获得对相关文档的读取、创建、更新或删除访问权限。

过滤器

属性过滤器是操作属性在将其保存到数据库之前和从数据库检索之后的函数。您可以使用Database::addFilter($name, $encode, $decode)添加过滤器,其中$name是要添加到以后可以添加到属性filters数组的过滤器名称。$encode$decode是分别用于编码和解码属性的函数。还有实例级别的过滤器,它们只能在构造Database实例时定义。实例级别的过滤器会覆盖具有相同名称的静态过滤器。

保留属性

  • $id - 文档的唯一ID,您可以为它设置自己的自定义ID,或者库将生成一个随机的UID。
  • $createdAt - 文档创建日期,此属性在文档创建时自动设置。
  • $updatedAt - 文档更新日期,此属性在文档更新时自动设置。
  • $collection - 包含文档存储的集合名称的属性。
  • $permissions - 包含字符串数组的属性。每个字符串代表一个特定动作和角色。如果您的用户获得了该动作的该角色,他们将有权访问此文档。

属性类型

数据库文档接口仅支持原始类型(字符串整数浮点数布尔值),并将其转换为每个相关数据库适配器本地的数据库类型。复杂类型,如数组或对象,在存储时将被编码为JSON字符串,并在从适配器检索时解码。

支持的数据库

以下是一个支持数据库列表,包括兼容性测试的版本,以及支持的功能和相关的限制。

✅ - 支持

🛠 - 进行中

限制

MariaDB、MySQL、Postgres、SQLite

  • ID最大大小为255字节
  • ID只能包含[^A-Za-z0-9]和符号_ -
  • 文档最大大小为65535字节
  • 集合最多可以有1017个属性
  • 集合最多可以有64个索引
  • 索引值最大大小为768字节。超过768字节的值将被截断
  • 字符串最大大小为4294967295个字符
  • 整数最大大小为4294967295

MongoDB

  • ID最大大小为255字节
  • ID只能包含[^A-Za-z0-9]和符号_ -
  • 文档可以有不受限制的大小
  • 集合可以有不受限制的属性数量
  • 集合最多可以有64个索引
  • 索引值可以有不受限制的大小
  • 字符串最大大小为2147483647个字符
  • 整数最大大小为4294967295

使用方法

连接到数据库

MariaDB

require_once __DIR__ . '/vendor/autoload.php';

use PDO;
use Utopia\Database\Database;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Memory;
use Utopia\Database\Adapter\MariaDB;

$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 Memory()); // or use any cache adapter you wish

$database = new Database(new MariaDB($pdo), $cache);

MySQL

require_once __DIR__ . '/vendor/autoload.php';

use PDO;
use Utopia\Database\Database;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Memory;
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);

$cache = new Cache(new Memory()); // or use any cache adapter you wish

$database = new Database(new MySql($pdo), $cache);

Postgres

require_once __DIR__ . '/vendor/autoload.php';

use PDO;
use Utopia\Database\Database;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Memory;
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 Memory()); // or use any cache adapter you wish

$database = new Database(new Postgres($pdo), $cache);

SQLite

require_once __DIR__ . '/vendor/autoload.php';

use PDO;
use Utopia\Database\Database;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Memory;
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 Memory()); // or use any cache adapter you wish

$database = new Database(new SQLite($pdo), $cache);

MongoDB

require_once __DIR__ . '/vendor/autoload.php';

use Utopia\Database\Database;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Memory;
use Utopia\Database\Adapter\Mongo;
use Utopia\Mongo\Client; // from utopia-php/mongo

$dbHost = 'mongo';
$dbPort = 27017; 
$dbUser = 'root';
$dbPass = 'password';
$dbName = 'dbName';

$mongoClient = new Client($dbName, $dbHost, $dbPort, $dbUser, $dbPass, true);

$cache = new Cache(new Memory()); // or use any cache adapter you wish

$database = new Database(new Mongo($client), $cache);

数据库方法

// Get namespace
$database->getNamespace();

// Sets namespace that prefixes all collection names
$database->setNamespace(
    namespace: 'namespace'
);

// Get default database
$database->getDatabase();

// Sets default database
$database->setDatabase(
    name: 'dbName'
);

// 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'
);

// Ping database it returns true if the database is alive
$database->ping();

// Check if database exists
$database->exists(
    database: 'mydb'
); 

// Check if collection exists
$database->exists(
    database: 'mydb',
    collection: 'users'
); 

// Listen to events

// 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(
    Database::EVENT_ALL, 
    function($event, $data) {
        // Do something
    }
);

// Get Database Adapter
$database->getAdapter();

// Get List of keywords that cannot be used
$database->getKeywords();

集合方法

// 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::unique(),
         '$permissions' => [
            Permission::read(Role::any()),
            Permission::update(Role::any()),
            Permission::delete(Role::any())
         ],
         'name' => 'Jhon', 
         'age'  =>  20
     ]),
     new Document([
         '$id' => ID::unique(),
         '$permissions' => [
            Permission::read(Role::any()),
            Permission::update(Role::any()),
            Permission::delete(Role::any())
         ],
         'name' => 'Doe', 
         'age'  =>  34
     ]),
]

$indexes = [
     new Document([
            '$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'],
        ])
];

$database->createCollection(
    name: 'users', 
    attributes: $attributes, 
    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'
);

// Delete cached documents of a collection
$database->purgeCachedCollection(
    collection: 'users'
);

属性方法

// 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:  Database::VAR_STRING,
    size: 128, 
    required: true
);

// New attribute with optional parameters
$database->createAttribute(
    collection: 'movies', 
    id: 'genres',
    type: Database::VAR_STRING, 
    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: Database::VAR_STRING, 
    size: 128, 
    required: true, 
    default: null, 
    signed: true, 
    array: false, 
    format: null, 
    formatOptions: [], 
    filters: []
);

// Update the required status of an attribute
$database->updateAttributeRequired(
    collection: 'movies', 
    id: 'genres',
    required: true
);

// Update the attribute format
$database->updateAttributeFormat(
    collection: 'movies', 
    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: $collection,
    attribute: $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 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', Database::INDEX_KEY, 
    attributes: ['name', 'genres'], 
    lengths: [128,128], 
    orders: [Database::ORDER_ASC, Database::ORDER_DESC]
);

// Rename index from old to new in the '$namespace_$collectionName' collection.
$database->renameIndex(
    collection: 'movies', 
    old: 'index1', 
    new: 'index2'
);

// Deletes the index in the '$namespace_$collectionName' collection.
$database->deleteIndex(
    collection: 'movies', 
    id: 'index2'
);

关系方法

// 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', 
    type: Database::RELATION_ONE_TO_ONE,
    twoWay: true
);


// Create a relationship with custom reference attributes
$database->createRelationship(
    collection: 'movies', 
    relatedCollection: 'users', 
    type: Database::RELATION_ONE_TO_ONE, 
    twoWay: true, 
    id: 'movies_id', 
    twoWayKey: 'users_id'
); 

// 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: Database::RELATION_MUTATE_CASCADE
); 

// Update the relationship with custom reference attributes
$database->updateRelationship(
    collection: 'movies', 
    id: 'users', 
    onDelete: Database::RELATION_MUTATE_CASCADE, 
    newKey: 'movies_id', 
    newTwoWayKey: 'users_id', 
    twoWay: true
);

// Delete the relationship with the default or custom reference attributes
$database->deleteRelationship(
    collection: 'movies', 
    id: 'users'
);

文档方法

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)        // Creates an id of length 7 + padding
ID::custom(id: 'my_user_3235')  

// Role helpers
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()),
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([
    '$permissions' => [
        Permission::read(Role::any()),
        Permission::create(Role::user(ID::custom('1x'))),
        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,
    'price' => 25.99,
    'active' => true,
    'genres' => ['science fiction', 'action', 'comics'],
]);

$document = $database->createDocument(
    collection: 'movies', 
    document: $document
);

// Get which collection a document belongs to
$document->getCollection();

// Get document id
$document->getId();

// 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
);

// 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

// Set types
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);

$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)
         ->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(
    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
$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
$database->getDocument(
    collection: 'movies', 
    id: $document->getId()
); 

// Get document with a sub-set of attributes
$database->getDocument(
    collection: 'movies', 
    id: $document->getId(), 
    queries: [
        Query::select(['name', 'director', 'year'])
    ]
);

// Find documents 

// 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:  [
        Query::equal(attribute: 'name', values: ['Captain Marvel']),
        Query::notEqual(attribute: 'year', values: [2019])
    ], 
    timeout: 1 //timeout is optional
);  

// Find a document 
$database->findOne(
    collection: 'movies', 
    queries:  [
        Query::equal(attribute: 'name', values: ['Captain Marvel']),
        Query::lessThan(attribute: 'year', value: 2019)
    ]
);  

// Get count of documents 
$database->count(
    collection: 'movies', 
    queries:  [
        Query::equal(attribute: 'name', values: ['Captain Marvel']),
        Query::greaterThan(attribute: 'year', value: 2019)
    ], 
    max: 1000 // Max is optional
);

// Get the sum of an attribute from all the documents
$database->sum(
    collection: 'movies', 
    attribute: 'price', 
    queries:  [
        Query::greaterThan(attribute: 'year', value: 2019)
    ],
    max: null // max = null means no limit
); 

// Delete a document
$database->deleteDocument(
    collection: 'movies', 
    id: $document->getId()
);

// Delete a cached document
Note: Cached Documents or Collections are automatically deleted when a document or collection is updated or deleted 
$database->purgeCachedDocument(
    collection: 'movies', 
    id: $document->getId()
);

系统需求

Utopia框架需要PHP 8.0或更高版本。我们建议尽可能使用最新的PHP版本。

贡献

感谢您考虑为Utopia框架做出贡献!有关更多信息,请参阅CONTRIBUTING.md文件。

版权和许可

MIT许可(MIT) http://www.opensource.org/licenses/mit-license.php