An open API service indexing awesome lists of open source software.

https://github.com/ravendb/ravendb-php-client


https://github.com/ravendb/ravendb-php-client

Last synced: over 1 year ago
JSON representation

Awesome Lists containing this project

README

          

# PHP client for RavenDB NoSQL Database

## Installation

You can install library to your project via [Composer](https://getcomposer.org/)
``` bash
$ composer require ravendb/ravendb-php-client
```

## Releases

* All client versions 5.4.x are fully compatible with and support RavenDB server releases 5.4 and 6.0.

* [Click here](https://github.com/ravendb/ravendb-php-client/releases) to view all Releases and Changelog.

## Documentation

This readme provides short examples for the following:

[Getting started](#getting-started),
[Crud example](#crud-example),
[Query documents](#query-documents),
[Attachments](#attachments),
[Time series](#timeseries),
[Revisions](#revisions),
[Suggestions](#suggestions),
[Patching](#advanced-patching),
[Using classes](#using-classes-for-entities),
[PHP usage](#usage-with-php),
[Working with secure server](#working-with-a-secure-server),
[Running tests](#running-tests)

For more information go to the online [RavenDB Documentation](https://ravendb.net/docs/article-page/latest/nodejs/client-api/what-is-a-document-store).

For more information on how to use **RavenDB** with **Laravel**
check out the [Raven Laravel Demo Application](https://github.com/ravendb/samples-php-laravel)

## Getting started

1. Require the `DocumentStore` class from the ravendb package
```php
use RavenDB\Documents\DocumentStore;
```

2. Initialize the document store (you should have a single DocumentStore instance per application)
```php
$store = new DocumentStore('http://live-test.ravendb.net', 'databaseName');
$store->initialize();
```

3. Open a session
```php
$session = $store->openSession();
```

4. Call `saveChanges()` when you're done
```php
$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 example

### Store documents

```php
$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();
```

>##### Related tests:
>
>
>
> [storing docs with same ID in same session should throw](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TrackEntityTest.php#L80)

### Load documents

```php
$product = $session->load(Product::class, 'products/1-A');
echo $product->getTitle(); // iPhone X
echo $product->getId(); // products/1-A
```

### Load documents with include

```php
// 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();
}

```

>##### Related tests:
> [can load with includes](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/Documents/LoadTest/LoadTest.php#L10)

### Update documents

```php
$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
```

### Delete documents

1. Using entity
```php
$product = $session->load('products/1-A');
$session->delete($product);
$session->saveChanges();

$product = $session->load('products/1-A');
$this->assertNull($product); // null
```

2. Using document ID
```php
$session->delete('products/1-A');
```

>##### Related tests:
> [delete doc by entity](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/DeleteTest.php#L10)
> [delete doc by ID](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/DeleteTest.php#L38)
> [onBeforeDelete is called before delete by ID](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Issues/RavenDB_15492Test.php#L9)
> [cannot delete untracked entity](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TrackEntityTest.php#L15)
> [loading deleted doc returns null](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TrackEntityTest.php#L37)

## Query documents

1. Use `query()` session method:

Query by collection:
```php
$query = $session->query(Product::class, Query::collection('products'));
```
Query by index name:
```php
$query = $session->query(Product::class, Query::indexName('productsByCategory'));
```
Query by index:
```php
$query = $session->query(Product::class, Products_ByCategory::class);
```
Query by entity type:
```php
$query = $session->query(Product::class);
```

2. Build up the query - apply search conditions, set ordering, etc.
Query supports chaining calls:
```php
$query
->waitForNonStaleResults()
->usingDefaultOperator('AND')
->whereEquals('manufacturer', 'Apple')
->whereEquals('in_stock', true)
->whereBetween('last_update', new DateTime('- 1 week'), new DateTime())
->orderBy('price');
```

3. Execute the query to get results:
```php
$results = $query->toList(); // get all results
// ...
$firstResult = $query->first(); // gets first result
// ...
$single = $query->single(); // gets single result
```

### Query methods overview

#### selectFields() - projections using a single field
```php
// RQL
// from users select name

// Query
$userNames = $session->query(User::class)
->selectFields("name")
->toList();

// Sample results
// John, Stefanie, Thomas
```

>##### Related tests:
>
> [query single property](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L368)
>
> [can_project_id_field](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Issues/RavenDB_14811Test/RavenDB_14811Test.php#L58)

#### selectFields() - projections using multiple fields
```php
// 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 ] ]
```

>##### Related tests:
>
> [query with projection](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L555)
> [can_project_id_field](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Issues/RavenDB_14811Test/RavenDB_14811Test.php#L58)

#### distinct()
```php
// RQL
// from users select distinct age

// Query
$session->query(User::class)
->selectFields("age")
->distinct()
->toList();

// Sample results
// [ 30, 25 ]
```

>##### Related tests:
>
> [query distinct](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L611)

#### whereEquals() / whereNotEquals()
```php
// 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 } ]
```

>##### Related tests:
>
> [where equals](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L784)
> [where not equals](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L817)

#### whereIn()
```php
// 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' } ]
```

>##### Related tests:
>
> [query with where in](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L416)

#### whereStartsWith() / whereEndsWith()
```php
// 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 } ]
```

>##### Related tests:
> [query with where clause](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L233)

#### whereBetween()
```php
// 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' } ]
```

>##### Related tests:
>
> [query with where between](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L438)

#### whereGreaterThan() / whereGreaterThanOrEqual() / whereLessThan() / whereLessThanOrEqual()
```php
// 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' } ]
```

>##### Related tests:
>
> [query with where less than](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L463)
> [query with where less than or equal](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L486)
> [query with where greater than](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L507)
> [query with where greater than or equal](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L532)

#### whereExists()
Checks if the field exists.
```php
// 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' } ]
```

>##### Related tests:
>
> [query where exists](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L997)

#### containsAny() / containsAll()
```php
// 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' } ]
```

>##### Related tests:
>
> [queries with contains](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/ContainsTest/ContainsTest.php#L12)

#### search()
Perform full-text search.
```php
// 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' } ]
```

>##### Related tests:
>
> [query search with or](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L636)
> [query_CreateClausesForQueryDynamicallyWithOnBeforeQueryEvent](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L35)

#### openSubclause() / closeSubclause()
```php
// 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' } ]
```

>##### Related tests:
>
> [working with subclause](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/Issues/RavenDB_5669Test/RavenDB_5669Test.php#L44)

#### not()
```php
// 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' } ]
```

>##### Related tests:
>
> [query where not](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L817)

#### orElse() / andAlso()
```php
// 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' } ]
```

>##### Related tests:
>
> [working with subclause](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_5669.ts#L40)

#### usingDefaultOperator()
If neither `andAlso()` nor `orElse()` is called then the default operator between the query filtering conditions will be `AND` .
You can override that with `usingDefaultOperator` which must be called before any other where conditions.
```php
// 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()
```php
// 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' } ]
```

>##### Related tests:
>
>
> [query random order](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L817)
> [order by AlphaNumeric](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L1103)
> [query with boost - order by score](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L1026)

#### take()
Limit the number of query results.
```php
// 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' } ]
```

>##### Related tests:
>
> [query skip take](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L685)
>

#### skip()
Skip a specified number of results from the start.
```php
// 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' } ]
```

>##### Related tests:
>
> [query skip take](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L685)
>

#### Getting query statistics
Use the `statistics()` method to obtain query statistics.
```php
// 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()` - returns all results

`first()` - first result only

`single()` - first result, throws error if there's more entries

`count()` - returns the number of entries in the results (not affected by `take()`)

>##### Related tests:
> [query first and single](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L917)
> [query count](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_QueryTest/QueryTest.php#L951)

## Attachments

#### Store attachments
```php
$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();
```

>##### Related tests:
>
> [can put attachments](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/Attachments/AttachmentsSessionTest.php#L15)
>
>

#### Get attachments
```php
// 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);
```

>##### Related tests:
>
> [can get & delete attachments](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/Attachments/AttachmentsSessionTest.php#L144)

#### Check if attachment exists
```php
$session->advanced()->attachments()->exists($doc->getId(), "photo.png");
// true

$session->advanced()->attachments()->exists($doc->getId(), "not_there.avi");
// false
```

>##### Related tests:
>
> [attachment exists 2](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/Attachments/AttachmentsSessionTest.php#L419)

#### Get attachment names
```php
// 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 } ]
```
>##### Related tests:
>
> [get attachment names 2](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/Attachments/AttachmentsSessionTest.php#L376)

## TimeSeries

#### Store time series
```php
$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();
```

>##### Related tests:
>
> [canCreateSimpleTimeSeries](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L16)
> [usingDifferentTags](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L244)
> [canStoreAndReadMultipleTimestamps](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L384)
> [canStoreLargeNumberOfValues](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L441)
> [shouldDeleteTimeSeriesUponDocumentDeletion](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L796)

#### Get time series for document
```php
$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();
```

>##### Related tests:
> [canCreateSimpleTimeSeries](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L16)
> [canStoreLargeNumberOfValues](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L441)
> [canRequestNonExistingTimeSeriesRange](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L574)
> [canGetTimeSeriesNames2](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L701)
> [canSkipAndTakeTimeSeries](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/TimeSeries/TimeSeriesSessionTest.php#L850)

## Revisions

NOTE: Please make sure revisions are enabled before trying the below.

```php
$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' }
// ]
```

## Suggestions

Suggest options for similar/misspelled terms

```php
// 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' ] } }
```

## Advanced patching

```php
// 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();
```

>##### Related tests:
>
> [can patch](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_FirstClassPatchTest/FirstClassPatchTest.php#L19)
> [can patch complex](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_FirstClassPatchTest/FirstClassPatchTest.php#L112)
> [can add to array](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_FirstClassPatchTest/FirstClassPatchTest.php#L206)
> [can increment](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/_FirstClassPatchTest/FirstClassPatchTest.php#L368)
> [patchWillUpdateTrackedDocumentAfterSaveChanges](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Issues/RavenDB_11552Test.php#L17)
> [can patch single document](https://github.com/ravendb/ravendb-php-client/blob/282c7bf6d2580ba446e878498d215a38caa67799/tests/Test/Client/PatchTest.php#L17)
>

## Using classes for entities

1. Define your model as class. Attributes should be just public properties:
```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();
}
}
```

2. To store a document pass its instance to `store()`.
The collection name will automatically be detected from the entity's class name.
```php
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();
```

3. Loading a document
```php
$product = $session->load('products/1-A');
var_dump($product instanceof Product); // true
var_dump($product->id); // products/1-A
```

4. Querying for documents
```php
$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
});
```

>

## Usage with PHP

PHP typings are embedded into the package. Make sure to close session when you finish your work with it.

```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 $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();
}
```

## Working with a secure server

Your certificate and server certificate should be saved in PEM format to your machine.

1. Create AuthOptions:

```php
$authOptions = AuthOptions::pem(
'../clientCertPath.pem',
'clientCertPass',
'../serverCaCertPath.pem'
);
```

2. Pass auth options to `DocumentStore` object:

```php
$store = new DocumentStore('url', 'databaseName');
$store->setAuthOptions($authOptions); // use auth options to connect on database
$store->initialize();
```

## Running tests

Clone the repository:
```bash
git clone https://github.com/ravendb/ravendb-php-client
```

Install dependencies:
```bash
composer install
```
Run RavenDB server
```bash
https://a.phptest.development.run
```
Set environment variables.

```bash
# 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=
```

Run PHPUnit
```bash
./vendor/bin/phpunit
```

-----
##### Bug Tracker
[http://issues.hibernatingrhinos.com/issues/RDBC](http://issues.hibernatingrhinos.com/issues/RDBC)

-----
##### License

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.