ravendb / ravendb-php-client
RavenDB PHP 客户端
Requires
- php: >=8.1
- ext-ds: *
- ext-fileinfo: *
- ext-json: *
- ext-zip: *
- doctrine/annotations: ^1.13
- doctrine/inflector: ^2.0
- ramsey/uuid: ^4.2
- symfony/http-client: ^6.1
- symfony/mime: ^6.1
- symfony/property-access: ^6.1
- symfony/serializer: ^6.1
Requires (Dev)
- phpstan/phpstan: ^0.12.98
- phpunit/phpunit: ~9.6
- squizlabs/php_codesniffer: ^3.0
This package is not auto-updated.
Last update: 2024-09-11 22:58:34 UTC
README
安装
您可以通过 Composer 将库安装到项目中
$ composer require ravendb/ravendb-php-client
版本发布
-
所有 5.2.x 版本的客户端完全兼容并支持 RavenDB 服务器 5.4 和 6.0 版本。
-
点击此处 查看所有版本和更新日志。
文档
本说明提供了以下简短示例
入门,
CRUD 示例,
查询文档,
附件,
时间序列,
修订,
建议,
修复,
使用类,
PHP 使用,
与安全服务器协同工作,
运行测试
有关更多信息,请访问在线 RavenDB 文档。
有关如何使用 RavenDB 与 Laravel 的更多信息,请查看 Raven Laravel 示例应用程序
入门
- 从 ravendb 包中引入
DocumentStore
类
use RavenDB\Documents\DocumentStore;
- 初始化文档存储(每个应用程序应有一个 DocumentStore 实例)
$store = new DocumentStore('http://live-test.ravendb.net', 'databaseName'); $store->initialize();
- 打开一个会话
$session = $store->openSession();
- 完成时调用
saveChanges()
$user = $session->load('users/1-A'); // Load document $user->setPassword(PBKDF2('new password')); // Update data $session->saveChanges(); // Save changes // Data is now persisted // You can proceed e.g. finish web request
CRUD 示例
存储文档
$product = new Product(); $product->setTitle("iPhone X"); $product->setPrice(999.99); $product->setCurrency("USD"); $product->setStorage(64); $product->setManufacturer("Apple"); $product->setInStock(true); $session->store($product, 'products/1-A'); echo $product->id; // products/1-A $session->saveChanges();
相关测试
加载文档
$product = $session->load(Product::class, 'products/1-A'); echo $product->getTitle(); // iPhone X echo $product->getId(); // products/1-A
加载带有包含的文档
// users/1 // { // "name": "John", // "kids": ["users/2", "users/3"] // } $session = $store->openSession(); try { $user1 = $session ->include("kids") ->load("users/1"); // Document users/1 and all docs referenced in "kids" // will be fetched from the server in a single request. $user2 = $session->load("users/2"); // this won't call server again $this->assertNotNull($user1); $this->assertNotNull($user2); $this->assertEqual(1, $session->advanced()->getNumberOfRequests()); } finally { $session->close(); }
相关测试
更新文档
$product = $session->load(Product::class, 'products/1-A'); $product->setInStock(false); $product->setLastUpdate(new Date()); $session->saveChanges(); // ... $product = $session->load(Product::class, 'products/1-A'); echo $product->getInStock(); // false echo $product->getLastUpdate(); // the current date
删除文档
- 使用实体
$product = $session->load('products/1-A'); $session->delete($product); $session->saveChanges(); $product = $session->load('products/1-A'); $this->assertNull($product); // null
- 使用文档 ID
$session->delete('products/1-A');
相关测试
按实体删除文档
按 ID 删除文档
在按 ID 删除之前调用 onBeforeDelete
不能删除未跟踪的实体
加载已删除的文档返回 null
查询文档
- 使用
query()
会话方法
按集合查询
$query = $session->query(Product::class, Query::collection('products'));
按索引名称查询
$query = $session->query(Product::class, Query::indexName('productsByCategory'));
按索引查询
$query = $session->query(Product::class, Products_ByCategory::class);
按实体类型查询
$query = $session->query(Product::class);
- 构建查询 - 应用搜索条件,设置排序等。
查询支持链式调用
$query ->waitForNonStaleResults() ->usingDefaultOperator('AND') ->whereEquals('manufacturer', 'Apple') ->whereEquals('in_stock', true) ->whereBetween('last_update', new DateTime('- 1 week'), new DateTime()) ->orderBy('price');
- 执行查询以获取结果
$results = $query->toList(); // get all results // ... $firstResult = $query->first(); // gets first result // ... $single = $query->single(); // gets single result
查询方法概述
selectFields() - 使用单个字段进行投影
// RQL // from users select name // Query $userNames = $session->query(User::class) ->selectFields("name") ->toList(); // Sample results // John, Stefanie, Thomas
相关测试
selectFields() - 使用多个字段进行投影
// RQL // from users select name, age // Query $session->query(User::class) ->selectFields([ "name", "age" ]) ->toList(); // Sample results // [ [ name: 'John', age: 30 ], // [ name: 'Stefanie', age: 25 ], // [ name: 'Thomas', age: 25 ] ]
相关测试
distinct()
// RQL // from users select distinct age // Query $session->query(User::class) ->selectFields("age") ->distinct() ->toList(); // Sample results // [ 30, 25 ]
相关测试
whereEquals() / whereNotEquals()
// RQL // from users where age = 30 // Query $session->query(User::class) ->whereEquals("age", 30) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // kids: [...], // registeredAt: 2017-11-10T23:00:00.000Z } ]
相关测试
whereIn()
// RQL // from users where name in ("John", "Thomas") // Query $session->query(User::class) ->whereIn("name", ["John", "Thomas"]) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: [...], // id: 'users/1-A' }, // User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' } ]
相关测试
whereStartsWith() / whereEndsWith()
// RQL // from users where startsWith(name, 'J') // Query $session->query(User::class) ->whereStartsWith("name", "J") ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // kids: [...], // registeredAt: 2017-11-10T23:00:00.000Z } ]
相关测试
whereBetween()
// RQL // from users where registeredAt between '2016-01-01' and '2017-01-01' // Query $session->query({ collection: "users" }) ->whereBetween("registeredAt", DateTime::createFromFormat('Y-m-d', '2016-01-01'), DateTime::createFromFormat('Y-m-d', '2017-01-01')) ->toList(); // Sample results // [ User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' } ]
相关测试
whereGreaterThan() / whereGreaterThanOrEqual() / whereLessThan() / whereLessThanOrEqual()
// RQL // from users where age > 29 // Query $session->query(User::class) ->whereGreaterThan("age", 29) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: [...], // id: 'users/1-A' } ]
相关测试
带有 where less than 的查询
带有 where less than or equal 的查询
带有 where greater than 的查询
带有 where greater than or equal 的查询
whereExists()
检查字段是否存在。
// RQL // from users where exists("age") // Query $session->query(User::class) ->whereExists("kids") ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: [...], // id: 'users/1-A' } ]
相关测试
containsAny() / containsAll()
// RQL // from users where kids in ('Mara') // Query $session->query(User::class) ->containsAll("kids", ["Mara", "Dmitri"]) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: ["Dmitri", "Mara"] // id: 'users/1-A' } ]
相关测试
search()
执行全文搜索。
// RQL // from users where search(kids, 'Mara') // Query $session->query(User::class) ->search("kids", "Mara Dmitri") ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: ["Dmitri", "Mara"] // id: 'users/1-A' } ]
相关测试
带有 or 的查询 search
query_CreateClausesForQueryDynamicallyWithOnBeforeQueryEvent
openSubclause() / closeSubclause()
// RQL // from users where exists(kids) or (age = 25 and name != Thomas) // Query $session->query(User::class) ->whereExists("kids") ->orElse() ->openSubclause() ->whereEquals("age", 25) ->whereNotEquals("name", "Thomas") ->closeSubclause() ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: ["Dmitri", "Mara"] // id: 'users/1-A' }, // User { // name: 'Stefanie', // age: 25, // registeredAt: 2015-07-29T22:00:00.000Z, // id: 'users/2-A' } ]
相关测试
not()
// RQL // from users where age != 25 // Query $session->query(User::class) ->not() ->whereEquals("age", 25) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: ["Dmitri", "Mara"] // id: 'users/1-A' } ]
相关测试
orElse() / andAlso()
// RQL // from users where exists(kids) or age < 30 // Query $session->query(User::class) ->whereExists("kids") ->orElse() ->whereLessThan("age", 30) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: [ 'Dmitri', 'Mara' ], // id: 'users/1-A' }, // User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' }, // User { // name: 'Stefanie', // age: 25, // registeredAt: 2015-07-29T22:00:00.000Z, // id: 'users/2-A' } ]
相关测试
usingDefaultOperator()
如果没有调用 andAlso()
或 orElse()
,则查询过滤条件之间的默认运算符将是 AND
。
您可以使用usingDefaultOperator
来覆盖它,该函数必须在任何其他where条件之前调用。
// RQL // from users where exists(kids) or age < 29 // Query $session->query(User::class) ->usingDefaultOperator("OR") // override the default 'AND' operator ->whereExists("kids") ->whereLessThan("age", 29) ->toList(); // Sample results // [ User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: [ 'Dmitri', 'Mara' ], // id: 'users/1-A' }, // User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' }, // User { // name: 'Stefanie', // age: 25, // registeredAt: 2015-07-29T22:00:00.000Z, // id: 'users/2-A' } ]
orderBy() / orderByDesc() / orderByScore() / randomOrdering()
// RQL // from users order by age // Query $session->query(User::class) ->orderBy("age") ->toList(); // Sample results // [ User { // name: 'Stefanie', // age: 25, // registeredAt: 2015-07-29T22:00:00.000Z, // id: 'users/2-A' }, // User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' }, // User { // name: 'John', // age: 30, // registeredAt: 2017-11-10T23:00:00.000Z, // kids: [ 'Dmitri', 'Mara' ], // id: 'users/1-A' } ]
相关测试
take()
限制查询结果数量。
// RQL // from users order by age // Query $session->query(User::class) ->orderBy("age") ->take(2) // only the first 2 entries will be returned ->toList(); // Sample results // [ User { // name: 'Stefanie', // age: 25, // registeredAt: 2015-07-29T22:00:00.000Z, // id: 'users/2-A' }, // User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' } ]
相关测试
skip()
从开始位置跳过指定数量的结果。
// RQL // from users order by age // Query $session->query(User::class) ->orderBy("age") ->take(1) // return only 1 result ->skip(1) // skip the first result, return the second result ->toList(); // Sample results // [ User { // name: 'Thomas', // age: 25, // registeredAt: 2016-04-24T22:00:00.000Z, // id: 'users/3-A' } ]
相关测试
获取查询统计信息
使用statistics()
方法获取查询统计信息。
// Query $stats = new QueryStatistics(); $results = $session->query(User::class) ->whereGreaterThan("age", 29) ->statistics($stats) ->toList(); // Sample results // QueryStatistics { // isStale: false, // durationInMs: 744, // totalResults: 1, // skippedResults: 0, // timestamp: 2018-09-24T05:34:15.260Z, // indexName: 'Auto/users/Byage', // indexTimestamp: 2018-09-24T05:34:15.260Z, // lastQueryTime: 2018-09-24T05:34:15.260Z, // resultEtag: 8426908718162809000 }
all() / first() / single() / count()
all()
- 返回所有结果
first()
- 只返回第一个结果
single()
- 返回第一个结果,如果有多条记录将抛出错误
count()
- 返回结果中的条目数(不受take()
的影响)
相关测试
附件
存储附件
$doc = new User(); $doc->setName('John'); // Store a document, the entity will be tracked. $session->store($doc); // Get read stream or buffer to store $fileStream = file_get_contents("../photo.png"); // Store attachment using entity $session->advanced()->attachments()->store($doc, "photo.png", $fileStream, "image/png"); // OR store attachment using document ID $session->advanced()->attachments()->store($doc->getId(), "photo.png", $fileStream, "image/png"); // Persist all changes $session->saveChanges();
相关测试
获取附件
// Get an attachment $attachment = $session->advanced()->attachments()->get($documentId, "photo.png") // Attachment.details contains information about the attachment: // { // name: 'photo.png', // documentId: 'users/1-A', // contentType: 'image/png', // hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=', // changeVector: '"A:3-K5TR36dafUC98AItzIa6ow"', // size: 4579 // } // Attachment.data is a Readable. $fileBytes = $attachment->getData(); file_put_contents('../photo.png', $fileBytes);
相关测试
检查附件是否存在
$session->advanced()->attachments()->exists($doc->getId(), "photo.png"); // true $session->advanced()->attachments()->exists($doc->getId(), "not_there.avi"); // false
相关测试
获取附件名称
// Use a loaded entity to determine attachments' names $session->advanced()->attachments()->getNames($doc); // Sample results: // [ { name: 'photo.png', // hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=', // contentType: 'image/png', // size: 4579 } ]
相关测试
时间序列
存储时间序列
$session = $store->openSession(); // Create a document with time series $session->store(new User(), "users/1"); $tsf = $session->timeSeriesFor("users/1", "heartbeat"); // Append a new time series entry $tsf->append(new DateTime(), 120); $session->saveChanges();
相关测试
canCreateSimpleTimeSeries
使用不同的标签
可以存储和读取多个时间戳
可以存储大量值
应在删除文档时删除时间序列
获取文档的时间序列
$session = $store->openSession(); // Get time series for document by time series name $tsf = $session->timeSeriesFor("users/1", "heartbeat"); // Get all time series entries $heartbeats = $tsf->get();
相关测试
canCreateSimpleTimeSeries
可以存储大量值
可以请求不存在的时间序列范围
可以获取时间序列名称2
可以跳过和获取时间序列
修订
注意:在尝试以下操作之前,请确保已启用修订。
$user = new User(); $user->setName("Marcin"); $user->setAge(30); $user->setPet("Cat"); $session = $store->openSession(); // Store a document $session->store($user, "users/1"); $session->saveChanges(); // Modify the document to create a new revision $user->setName("Roman"); $user->setAge(40); $session->saveChanges(); // Get revisions $revisions = $session->advanced()->revisions()->getFor("users/1"); // Sample results: // [ { name: 'Roman', // age: 40, // pet: 'Cat', // '@metadata': [Object], // id: 'users/1' }, // { name: 'Marcin', // age: 30, // pet: 'Cat', // '@metadata': [Object], // id: 'users/1' } // ]
建议
为类似/拼写错误的术语建议选项
// Some documents in users collection with misspelled name term // [ User { // name: 'Johne', // age: 30, // ... // id: 'users/1-A' }, // User { // name: 'Johm', // age: 31, // ... // id: 'users/2-A' }, // User { // name: 'Jon', // age: 32, // ... // id: 'users/3-A' }, // ] // Static index definition class UsersIndex extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->map = "from user in docs.users select new { user.name }"; // Enable the suggestion feature on index-field 'name' $this->suggestion("name"); } } // ... $session = $store->openSession(); // Query for similar terms to 'John' // Note: the term 'John' itself will Not be part of the results $suggestedNameTerms = $session->query(User::class, UsersIndex::class) ->suggestUsing(function($x) { return $x->byField("name", "John"); }) ->execute(); // Sample results: // { name: { name: 'name', suggestions: [ 'johne', 'johm', 'jon' ] } }
高级修补
// Increment 'age' field by 1 $session->advanced()->increment("users/1", "age", 1); // Set 'underAge' field to false $session->advanced->patch("users/1", "underAge", false); $session->saveChanges();
相关测试
使用类表示实体
- 将您的模型定义为类。属性应该是公共属性
class Product { public ?string $id = null, public string $title = '', public int $price = 0, public string $currency = 'USD', public int $storage = 0, public string $manufacturer = '', public bool $in_stock = false, public ?DateTime $last_update = null public function __construct( $id = null, $title = '', $price = 0, $currency = 'USD', $storage = 0, $manufacturer = '', $in_stock = false, $last_update = null ) { $this->id = $id; $this->title = $title; $this->price = $price; $this->currency = $currency; $this->storage = $storage; $this->manufacturer = $manufacturer; $this->in_stock = $in_stock; $this->last_update = $last_update ?? new DateTime(); } }
- 要存储文档,请将其实例传递给
store()
。
集合名称将自动从实体的类名称中检测到。
use models\Product; $product = new Product( null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00')); $product = $session->store($product); var_dump($product instanceof Product); // true var_dump(str_starts_with($product->id, 'products/')); // true $session->saveChanges();
- 加载数据文档
$product = $session->load('products/1-A'); var_dump($product instanceof Product); // true var_dump($product->id); // products/1-A
- 查询文档
$products = $session->query(Product::class)->toList(); foreach($products as $product) { var_dump($product instanceof Product); // true var_dump(str_starts_with($product->id, 'products/')); // true });
与PHP的使用
PHP类型已嵌入到包中。确保在使用完毕后关闭会话。
// file models/product.php class Product { public ?string $id = null, public string $title = '', public int $price = 0, public string $currency = 'USD', public int $storage = 0, public string $manufacturer = '', public bool $in_stock = false, public ?DateTime $last_update = null public function __construct( $id = null, $title = '', $price = 0, $currency = 'USD', $storage = 0, $manufacturer = '', $in_stock = false, $last_update = null ) { $this->id = $id; $this->title = $title; $this->price = $price; $this->currency = $currency; $this->storage = $storage; $this->manufacturer = $manufacturer; $this->in_stock = $in_stock; $this->last_update = $last_update ?? new DateTime(); } } // file app.php use models\Product; use RavenDB\Documents\DocumentStore; use RavenDB\Documents\Session\DocumentSession; $store = new DocumentStore('url', 'database name'); try { $store->initialize(); $productId = null; /** @var DocumentSession $session */ $session = $store->openSession(); try { $product = new Product( null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00')); $session->store($product); $session->saveChanges(); var_dump($product instanceof Product); // true var_dump(str_starts_with($product->id, 'products/')); // true $productId = $product->id; } finally { $session->close(); } $session = $store->openSession(); try { /** @var Product $product */ $product = $session->load(Product::class, $productId); var_dump($product instanceof Product); // true var_dump($product->id); // products/1-A /** @var array<Product> $products */ $products = $session->query(Query::collection('Products')) ->waitForNonStaleResults() ->whereEquals('manufacturer', 'Apple') ->whereEquals('in_stock', true) ->whereBetween('last_update', new DateTime('- 1 week'), new DateTime()) ->whereGreaterThanOrEqual('storage', 64) ->toList(); foreach ($products as $product) { var_dump($product instanceof Product); // true var_dump(str_starts_with($product->id, 'products/')); // true } } finally { $session->close(); } } finally { $store->close(); }
与安全服务器一起工作
您的证书和服务器证书应保存为PEM格式到您的机器。
- 创建AuthOptions
$authOptions = AuthOptions::pem( '../clientCertPath.pem', 'clientCertPass', '../serverCaCertPath.pem' );
- 将认证选项传递给
DocumentStore
对象
$store = new DocumentStore('url', 'databaseName'); $store->setAuthOptions($authOptions); // use auth options to connect on database $store->initialize();
运行测试
克隆存储库
git clone https://github.com/ravendb/ravendb-php-client
安装依赖项
composer install
运行RavenDB服务器
https://a.phptest.development.run
设置环境变量。
# Set the following environment variables: # # - Certificate hostname # RAVENDB_PHP_TEST_HTTPS_SERVER_URL=https://a.phptest.development.run # # RAVENDB_PHP_TEST_CA_PATH= # # - Certificate path for tests requiring a secure server: # RAVENDB_PHP_TEST_CERTIFICATE_PATH= # # - Certificate for client # RAVENDB_TEST_CLIENT_CERT_PATH= # RAVENDB_TEST_CLIENT_CERT_PASSPHRASE= # # - For some tests, Developers licence is required in order to run them all # RAVEN_LICENSE=
运行PHPUnit
./vendor/bin/phpunit
错误跟踪器
http://issues.hibernatingrhinos.com/issues/RDBC
许可
MIT许可(MIT)。有关更多信息,请参阅许可文件。