# Entities
## Get started
- Entities are described by a class implementing <@\MMO\core\interfaces\iEntity>.
- It uses array properties to interact with the client. <@\MMO\core\attributes\ArrayProperty>
- Each entity class has its own collection that stores the instantiated entities
- Each collection belongs to a repository which load/search/filter the entities.
It is the bridge to the database system.
## Implementation
There are two types of entity class that you MUST extend :
- `MMO\core\entities\Entity`: A standalone entity which has its own collection and has an id.
- `MMO\core\entities\EmbeddedEntity`: An entity embedded in another. It has no collection and no id
(rather an index inside the parent entity).
Each entity that you implement MUST have its own class. The class MUST use 2 php attributes to determine how the entity
is accessed (ArrayProperty) and how it is stored (Serialize).
### Array properties
1. Array properties are used to send/receive data to the client (in JSON format).
2. These properties can be linked directly to a class's property or can be dynamic (linked to a method).
3. You access these properties with the array `[]` operator.
4. In order to keep the access on direct property only available through array operator (so coercion and changes can be tracked automatically),
you MUST NOT declare these properties `public`. Use `protected` instead.
NOTE: Array properties can be connected to direct property or to method of the class (with the setter/getter attribute).
When an attribute is direct,
- the value is automatically coerced
- the changes are set for you in the entity
- the validation is done when the property changes
When the attribute is not direct, you MUST:
- return a boolean in the setter
- call `markChanged()` on the property you changed by hand
- coerce yourself the value
- validate yourself the changes
```php
#[\MMO\core\attributes\ArrayProperty(name: 'total', getter: 'getTotal')]//readonly dynamic property
class MyEntity extends \MMO\core\entities\Entity {
#[\MMO\core\attributes\ArrayProperty(schema: '[int]')]
protected array $numbers = [];
public function getTotal(): int {
return array_sum($this->numbers);
}
}
$e = new MyEntity(['numbers' => [1, 2, 3]]);
\MMO\dump($e['numbers']);//[1,2,3]
\MMO\dump($e['total']);//6
//valid (but not recommended. this will bypass all verification and won't trigger any changes in the entity)
$e['numbers'][] = 4;
//correct way
$e->push('numbers', 4);
//not valid
$e->push('numbers', 'test');//only integer can be injected into numbers (@see schema type in the ArrayProperty)
$e->numbers[] = 4;//property is protected and can only be accessed through the array notation
$e['total'] = 5;//array property is readonly
```
### JSON
Entities are sent to the client in json format.
In order to encode them, you can :
- add an entity to a json document `MMO\core\documents\JsonDocument`. It will automatically convert the entity to json.
> NOTE: When you create a json response (`MMO\core\server\Response::JSON()`), the body of that response is a json document.
- call the JsonPrinter : `\MMO\core\printers\JsonPrinter::Encode($entity, [], \JSON_PRETTY_PRINT)`
### Serialize
## Repository
This class is specific to the database driver. It handles the commits (using `\MMO\commit(...)`) and the searches.
`JsonRepository` keeps each collection in a JSON file (`{"1": {...}, "2": {...}}`, by id):
- an entity's uid names the repository's folder with a tag, not its path: `file://3f2a9c1b/users.json#2`, the same in
every request, which `\MMO\fetch()` finds whatever other repository holds a `users.json`;
- the files name the entities they hold by their file's name alone (``): the repository reading a
file answers its references first, so its folder can move;
- an id is never given twice: once the highest was removed, the file keeps it (`"$lastId": 12`);
- an entity removed (`removePermanently()`, then `\MMO\commit()`) leaves its collection.
## Collection
Collection is just a container for entities. It holds a reference on the repository and holds the current loaded entities
from the repository.
> WARNING: searching in the collection with `find()` and `findOne()` won't look for loaded entities. It will query directly the repository.
> if you want to search in the loaded entities you need to call `localFind()` and `localFindOne()`
> or call `\MMO\commit()` before calling find functions.
## Query
It is a generic wrapper (agnostic of the driver) to hold search data.
All repository implementation MUST support the query to search for entities.
### Filters
Filter are used to test array like data. It works like mongodb filters.
- Use Filter::From([..]) to get a filter or a group of filters from an array
- use Filter::test($data) to test the data
> NOTE: to test the data itself and not its content you can use the path '', '.' or '/'.
#### Supported operator:
- \$and: ['$and' => [['xxx' => yyy], ['xxx' => yyyy], ...]]
- \$or: ['$or' => [['xxx' => yyy], ['xxx' => yyyy], ...]]
- \$not: ['$not' => ['xxx' => yyy]]
- \$in: ['xxx' => ['$in' => [...values]]] test if property `xxx` contains a value inside `values`
- \$has: ['xxx' => ['$has' => [filter]]] test if property `xxx` contains a value that matches `filter`
- \$eq: ['xxx' => ['$eq' => [value]]] equal operator. Equivalent to ['xxx' => value]
- \$gt: ['xxx' => ['$gt' => [value]]] greater than operator
- \$gte: greater or equal
- \$lt: less than
- \$lte: lees than or equal
- \$ne: not equal
- \$nin: not in
The filter is provided as a family under the format `filter[xxx]`
- example: xxx?filter[age][$gt]=20&filter[age][$lt]=50&filter[author.status]=active
- **=> (age > 20) && (age < 50) && (author.status = active)**
- example xxx?filter[$or][$and][age][>]=20&filter[$or][$and][name]=marc&filter[$or][age][$lt]=20
- **=> ((age > 20) and (name = 'marc')) or (age < 20)**