docs updated

This commit is contained in:
2026-04-20 21:13:31 +03:00
parent 4a7eb217a1
commit e058ceadee
4 changed files with 354 additions and 411 deletions
+137 -298
View File
@@ -5,374 +5,213 @@ include_toc: true
# Fields # Fields
Fields are similar to a table's columns in a relational databases. Fields define the columns of a schema. Each field has a `ui` type that controls both storage and the admin UI component rendered.
## Available fields for Collections and Files ## Common Optional Properties
Most fields share these optional properties:
| Property | Description |
|---|---|
| `required` | Whether the field must have a value to save as `published` |
| `nullable` | Allow saving as `null` |
| `help` | Help text shown below the input |
| `default` | Default value when creating a new record |
| `readonly` | Prevent editing from the UI |
| `group` | Tab group this field belongs to |
## Field Types
### text ### text
One-line text input
required One-line text input.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `min` | Minimum character count |
| `max` | Maximum character count |
| `selectOptions` | Array of options. Strings or `[{value, label}]` objects |
| `optionsFrom` | Schema name to load options from |
| `optionsField` | Field from `optionsFrom` to use as the value |
| `optionsSuggest` | Allow typing new values not in the options list |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **min**: Minimum characters
- **max**: Maximum characters
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **optionsFrom**: Schema to choose options from
- **optionsField**: Field's value to insert
- **optionsSuggest**: Allow to insert new values
- **selectOptions**: Array of options to select from. Or array of objects `[{value,label}]`
- **group**: The group that this field belongs to
### textarea ### textarea
textarea input
required Multi-line text input.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `min` | Minimum character count |
| `max` | Maximum character count |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **min**: Minimum characters
- **max**: Maximum characters
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **group**: The group that this field belongs to
### slug ### slug
Slug input. Generates automatically if left empty
required Slug input. Auto-generates from a source field if left empty.
- **name**: The id of the field **Required:** `name`, `label`, `source`
- **label**: The friendly name of the field
- **source**: The source field from which it generates
optional | Property | Description |
|---|---|
| `source` | Field name to generate the slug from |
| `min` | Minimum character count |
| `max` | Maximum character count |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **min**: Minimum characters
- **max**: Maximum characters
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **group**: The group that this field belongs to
### rich ### rich
WYSIWYG editor
required WYSIWYG rich text editor.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `min` | Minimum character count |
| `max` | Maximum character count |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **min**: Minimum characters ### markdown
- **max**: Maximum characters
- **help**: Help text Markdown editor.
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI **Required:** `name`, `label`
- **group**: The group that this field belongs to
| Property | Description |
|---|---|
| `min` | Minimum character count |
| `max` | Maximum character count |
---
### number ### number
Any numeric value
required Numeric input.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `decimals` | Number of decimal places. Default: `0` |
| `min` | Minimum value |
| `max` | Maximum value |
| `optionsFrom` | Schema name to load options from |
| `optionsField` | Field from `optionsFrom` to use as the value |
| `optionsSuggest` | Allow typing new values not in the options list |
- **decimals**: default is 0 ---
- **required**: Is the field required to save the record
- **nullable**: Can the field be saved as null
- **min**: Minimum characters
- **max**: Maximum characters
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **optionsFrom**: Schema to choose options from
- **optionsField**: Field's value to insert
- **optionsSuggest**: Allow to insert new values
- **group**: The group that this field belongs to
### checkbox ### checkbox
True or false
required Boolean true/false toggle.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional ---
- **required**: Is the field required to save the record
- **nullable**: Can the field be saved as null
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **group**: The group that this field belongs to
### color ### color
Color picker
required Color picker.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `selectOptions` | Restrict to a predefined palette |
| `optionsFrom` | Schema name to load options from |
| `optionsField` | Field from `optionsFrom` to use as the value |
| `optionsSuggest` | Allow typing new values not in the options list |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **optionsFrom**: Schema to choose options from
- **optionsField**: Field's value to insert
- **optionsSuggest**: Allow to insert new values
- **selectOptions**: Array of options to select from. Or array of objects `[{value,label}]`
- **group**: The group that this field belongs to
### date ### date
Date select
required Date selector. Stores as a date string.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `min` | Minimum date |
| `max` | Maximum date |
| `selectOptions` | Predefined date options |
| `optionsFrom` | Schema name to load options from |
| `optionsField` | Field from `optionsFrom` to use as the value |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **min**: Minimum date
- **max**: Maximum date
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **optionsFrom**: Schema to choose options from
- **optionsField**: Field's value to insert
- **optionsSuggest**: Allow to insert new values
- **selectOptions**: Array of options to select from. Or array of objects `[{value,label}]`
- **group**: The group that this field belongs to
### datetime ### datetime
Date and time selector
required Date and time selector. Stores as an ISO 8601 string.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional | Property | Description |
|---|---|
| `min` | Minimum datetime |
| `max` | Maximum datetime |
| `selectOptions` | Predefined datetime options |
| `optionsFrom` | Schema name to load options from |
| `optionsField` | Field from `optionsFrom` to use as the value |
- **required**: Is the field required to save the record ---
- **nullable**: Can the field be saved as null
- **min**: Minimum date
- **max**: Maximum date
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **optionsFrom**: Schema to choose options from
- **optionsField**: Field's value to insert
- **optionsSuggest**: Allow to insert new values
- **selectOptions**: Array of options to select from. Or array of objects `[{value,label}]`
- **group**: The group that this field belongs to
### json ### json
Json data
required Raw JSON data field.
- **name**: The id of the field **Required:** `name`, `label`
- **label**: The friendly name of the field
optional ---
- **required**: Is the field required to save the record
- **nullable**: Can the field be saved as null
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **group**: The group that this field belongs to
### markdown
Markdown editor
required
- **name**: The id of the field
- **label**: The friendly name of the field
optional
- **required**: Is the field required to save the record
- **nullable**: Can the field be saved as null
- **min**: Minimum characters
- **max**: Maximum characters
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **group**: The group that this field belongs to
### file ### file
Upload or select files
required Upload or select files from a files schema.
- **name**: The id of the field **Required:** `name`, `label`, `collections`
- **label**: The friendly name of the field
- **collections**: File collections to choose from
optional | Property | Description |
|---|---|
- **mime**: The mime types allowed to select | `collections` | Array of file schema names to choose from |
- **nullable**: Can the field be saved as null | `mime` | Allowed MIME types e.g. `["image/*"]` |
- **min**: Minimum files | `min` | Minimum number of files |
- **max**: Maximum files | `max` | Maximum number of files |
- **help**: Help text
- **group**: The group that this field belongs to
---
### reference ### reference
Reference other records
required Reference records from another collection.
- **name**: The id of the field **Required:** `name`, `label`, `collections`
- **label**: The friendly name of the field
- **collections**: Collections to choose from
optional | Property | Description |
|---|---|
| `collections` | Array of collection schema names to reference |
| `min` | Minimum number of references |
| `max` | Maximum number of references |
- **nullable**: Can the field be saved as null ---
- **min**: Minimum files
- **max**: Maximum files
- **help**: Help text
- **group**: The group that this field belongs to
## Example Field
### block ```json
The block editor {
"ui": "text",
required "name": "title",
"label": "Title",
- **name**: The id of the field "required": true,
- **label**: The friendly name of the field "min": 3,
- **schema**: The block schema name "max": 200,
"help": "The main title of the post"
optional }
```
- **required**: Is the field required to save the record
- **nullable**: Can the field be saved as null
- **help**: Help text
- **default**: Default value when creating new record
- **readonly**: Cannot edit this value from the UI
- **group**: The group that this field belongs to
)
## Available fields for the Block Editor
### heading
Single-line text
required
- **name**: The id of the field
- **label**: The friendly name of the field
optional
- **min**: Minimum date
- **max**: Maximum date
- **default**: Default value when creating new record
### textarea
Multiline text
required
- **name**: The id of the field
- **label**: The friendly name of the field
optional
- **min**: Minimum date
- **max**: Maximum date
- **default**: Default value when creating new record
### rich
WYSIWYG editor
required
- **name**: The id of the field
- **label**: The friendly name of the field
optional
- **min**: Minimum date
- **max**: Maximum date
- **default**: Default value when creating new record
### markdown
Markdown editor
required
- **name**: The id of the field
- **label**: The friendly name of the field
optional
- **min**: Minimum date
- **max**: Maximum date
- **default**: Default value when creating new record
### file
Choose files
required
- **name**: The id of the field
- **label**: The friendly name of the field
- **collections**: File collections to choose from
optional
- **mime**: The mime types allowed to select
- **min**: Minimum date
- **max**: Maximum date
- **default**: Default value when creating new record
### reference
Choose files
required
- **name**: The id of the field
- **label**: The friendly name of the field
- **collections**: Collections to choose from
optional
- **min**: Minimum date
- **max**: Maximum date
- **default**: Default value when creating new record
+133 -52
View File
@@ -5,88 +5,169 @@ include_toc: true
# Queries # Queries
## Graph or Tree The `Query` class is the main way to fetch records. Inject it via the Laravel container.
Queries can return results in 2 formats. A graph or a tree.
Graphs results are a collection of records (nodes) and a collection of edges. This format is more useful for network visualization.
The tree format is more straightforward as it returns a collection of records. Each record has a **_children** and a **_parents** field and the tree can continue upwards or downwards according to the depth you have requested.
For example to request records with their children and their children's children:
```php ```php
$query->childrenDepth(2); public function __construct(private Query $query) {}
``` ```
Maybe you only want to get a specific type of relationship:
```php ## Return Formats
$query->childrenDepth(2)->childrenFields(["categories"]);
``` Queries return results in two formats:
**Graph** — via `->run()`. Returns a `Graph` object with `records` and `edges` collections. Useful for network-style data.
**Tree** — via `->tree()`. Returns a flat `Collection` of records where each record has `_children` and `_parents` arrays populated based on the requested depth. This is the most common format.
## Chaining Methods
All methods return `$this` and can be chained:
| Method | Description |
|---|---|
| `->filter(array)` | Add an AND filter |
| `->orFilter(array)` | Add an OR filter (grouped) |
| `->limit(int)` | Max number of root records to return |
| `->skip(int)` | Offset for pagination |
| `->sort(string)` | Sort by field. Prefix with `-` for descending e.g. `"-_sys.updatedAt"` |
| `->status(array)` | Filter by status array e.g. `["published", "draft"]` |
| `->onlyPublished()` | Shorthand for `->status(["published"])` |
| `->childrenDepth(int)` | How many levels of children to load |
| `->childrenLimit(int)` | Max children per record |
| `->childrenFields(array)` | Only follow specific relationship fields |
| `->parentsDepth(int)` | How many levels of parents to load |
| `->parentsLimit(int)` | Max parents per record |
| `->parentFields(array)` | Only follow specific relationship fields |
| `->notLinked(string)` | Return only records with no parents |
| `->run()` | Execute and return a `Graph` |
| `->tree()` | Execute and return a `Collection` (tree format) |
| `->runWithCount()` | Execute and return a `Graph` with a `total` count |
## Filters ## Filters
You can filter your query with the following format: Filter keys use the format `field_operator`. When no operator suffix is given, `eq` is assumed.
```php ```php
$query->filter(["field_operator" => "value"]); $query->filter(["field_operator" => "value"]);
// example: // No operator = eq
$query->filter(["schema" => "blogPosts"]);
$query->filter(["date_lt" => "2020-09-15"]); // With operator
$query->filter(["_sys.updatedAt_gte" => "2024-01-01"]);
``` ```
Or filters are also available: ### Operator Reference
| Operator | Description |
|---|---|
| _(none)_ or `eq` | Equals (string) |
| `ne` | Not equals (string) |
| `eqnum` | Equals (numeric) |
| `neqnum` | Not equals (numeric) |
| `eqtrue` | Equals `true` |
| `eqfalse` | Equals `false` |
| `netrue` | Not equals `true` |
| `nefalse` | Not equals `false` |
| `regex` | Regular expression match |
| `in` | Value is in array (strings) |
| `nin` | Value is not in array (strings) |
| `innum` | Value is in array (numeric) |
| `ninnum` | Value is not in array (numeric) |
| `lt` | Less than |
| `lte` | Less than or equal |
| `gt` | Greater than |
| `gte` | Greater than or equal |
| `null` | Field is null |
| `nnull` | Field is not null |
| `exists` | Field key exists |
| `nexists` | Field key does not exist |
| `filter` | Raw filter passthrough |
### OR Filters
```php ```php
$query $query
->filter(["price_eqn" => 10]) ->filter(["schema" => "blogPosts"])
->orFilter(["title_regex" => "search", "slug_regex" => "search"]) ->orFilter(["title_regex" => "search", "slug_regex" => "search"]);
;
``` ```
## Operator List
- regex Each `orFilter` call groups its arguments with OR between them. Multiple `orFilter` / `filter` calls are AND-ed together.
- eq
- ne
- eqnum
- neqnum
- filter
- eqtrue
- eqfalse
- netrue
- nefalse
- in:
- innum
- nin
- ninnum
- lt
- lte
- gt
- gte
- null
- nnull
- exists
- nexists
## Example ### Cross-Schema (Children) Filters
Get 10 posts from the sports category as a tree You can filter records by properties of their related records using the `children.` prefix:
```php
$query->filter([
"schema" => "blogPosts",
"children.categories.data.slug" => "sports",
]);
```
This returns `blogPosts` that have a child in `categories` where `slug` equals `sports`.
## Nested Data Fields
Use dot notation to filter on JSON data fields and system fields:
```php
// Filter on a data field
$query->filter(["data.status_eq" => "draft"]);
// Filter on a system field
$query->filter(["_sys.updatedAt_gte" => "2024-01-01"]);
```
## Examples
**Get published blog posts, newest first:**
```php
$posts = $query
->filter(["schema" => "blogPosts"])
->onlyPublished()
->sort("-_sys.updatedAt")
->limit(10)
->tree();
```
**Get posts from a specific category with children loaded:**
```php ```php
$posts = $query $posts = $query
->filter([ ->filter([
"schema" => "posts", "schema" => "blogPosts",
"children.categories.data.slug" => "sports", "children.categories.data.slug" => "sports",
] ])
)
->childrenDepth(1) ->childrenDepth(1)
->childrenFields(["categories"])
->limit(10) ->limit(10)
->tree(); ->tree();
$posts->map(...)->toArray();
``` ```
**Paginate records:**
```php
$graph = $query
->filter(["schema" => "blogPosts"])
->limit(20)
->skip(40)
->runWithCount();
$posts = $graph->tree();
$total = $graph->total;
```
**Search across fields:**
```php
$results = $query
->filter(["schema" => "blogPosts"])
->orFilter(["title_regex" => $term, "slug_regex" => $term])
->limit(10)
->tree();
```
+83 -60
View File
@@ -5,80 +5,103 @@ include_toc: true
# Schemas # Schemas
Schemas define both the shape of your data and how the UI on the admin will behave. Schemas define both the shape of your data and how the admin UI behaves.
There are 3 types of schemas There are 2 types of schemas:
- Collections: Normal data - **collection** — Regular data records
- Files: Images and files - **files** — Images and file uploads
- Block: Used in the block editor
## Collection Reference ## Collection Reference
- **name**: The ID of the collection. Camelcase and plural is the recommended format ex. blogPosts | Field | Required | Description |
- **label**: The friendly name of the schema |---|---|---|
- **type**: The type of the collection. Should be "collection" | `name` | yes | Unique ID. Use camelCase plural e.g. `blogPosts` |
- **visible**: An array of field id to show on the content browser _optional_ | `label` | yes | Friendly display name |
- **groups**: A list if group ids to separate your field in different tabs _optional_ | `type` | yes | Must be `"collection"` |
- **fields**: The list of your fields. Look the field reference for more | `fields` | yes | Array of field definitions. See [Fields](Fields.md) |
- **isEntry**: If this schema is important, it will show be visible on the main the sidebar. Default: false _optional_ | `visible` | no | Field IDs to show in the content browser list |
- **sortBy**: The default sorting in the content browser _optional_ | `groups` | no | Group IDs to split fields into tabs |
- **cardTitle**: Mustache code to customize the preview field _optional_ | `isEntry` | no | Show in sidebar. Default: `false` |
- **cardImage**: Field name of image you want to use as a preview image _optional_ | `sortBy` | no | Default sort in browser. Prefix with `-` for descending e.g. `-_sys.updatedAt` |
- **revisions**: How many revisions are going to be kept for each record _optional_ | `cardTitle` | no | Mustache template for the record preview title e.g. `{{name}} - {{slug}}` |
- **read**: Array of user groups that have read permissions _optional_ | `cardImage` | no | Field name to use as the preview image |
- **write**: Array of user groups that have write permissions _optional_ | `revisions` | no | Number of revisions to keep per record. Default: `0` (disabled) |
| `read` | no | Roles with read access. Empty means all roles can read |
| `write` | no | Roles with write access. Empty means all roles can write |
## Files Reference ## Files Reference
- **name**: The ID of the collection. Camelcase and plural is the recommended format ex. blogPosts | Field | Required | Description |
- **label**: The friendly name of the schema |---|---|---|
- **type**: The type of the collection. Should be "files" | `name` | yes | Unique ID. Use camelCase plural e.g. `heroImages` |
- **path**: The relative directory that these files will be stored. | `label` | yes | Friendly display name |
- **groups**: A list if group ids to separate your field in different tabs _optional_ | `type` | yes | Must be `"files"` |
- **fields**: The list of your fields. Look the field reference for more | `fields` | yes | Array of field definitions. See [Fields](Fields.md) |
- **isEntry**: If this schema is important, it will show be visible on the main the sidebar _optional_ | `disk` | no | Laravel disk name. Default: `"lucent"` |
- **sortBy**: The default sorting in the content browser _optional_ | `path` | no | Subdirectory for uploads. Default: schema name |
- **cardTitle**: Mustache code to customize the preview field _optional_ | `groups` | no | Group IDs to split fields into tabs |
- **revisions**: How many revisions are going to be kept for each record _optional_ | `isEntry` | no | Show in sidebar. Default: `false` |
- **read**: Array of user groups that have read permissions _optional_ | `sortBy` | no | Default sort in browser |
- **write**: Array of user groups that have write permissions _optional_ | `cardTitle` | no | Mustache template for the record preview title |
| `cardImage` | no | Field name to use as the preview image |
| `revisions` | no | Number of revisions to keep per record |
| `read` | no | Roles with read access |
| `write` | no | Roles with write access |
A full Collection example without the fields: ## System Fields
Every record automatically has these read-only system fields available in queries:
| Field | Description |
|---|---|
| `_sys.createdAt` | ISO 8601 creation timestamp |
| `_sys.updatedAt` | ISO 8601 last update timestamp |
| `_sys.createdBy` | User ID who created the record |
| `_sys.updatedBy` | User ID who last updated the record |
| `_sys.version` | Revision version number |
| `status` | `draft` or `published` |
## Example
```json ```json
{ {
"label": "Departments", "schemas": [
"name": "departments", {
"isEntry": true, "label": "Blog Posts",
"type": "collection", "name": "blogPosts",
"visible": [ "isEntry": true,
"slug", "type": "collection",
"cover", "visible": [
"_sys.updatedAt", "title",
"status" "slug",
"_sys.updatedAt",
"status"
],
"groups": [
"Content",
"SEO"
],
"sortBy": "-_sys.createdAt",
"cardTitle": "{{title}}",
"cardImage": "cover",
"revisions": 15,
"read": [
"admin",
"editors",
"reviewers"
],
"write": [
"admin",
"editors"
],
"fields": []
}
], ],
"groups": [ "roles": ["admin", "editors", "reviewers"]
"Extra Info",
"Metadata",
"SEO"
],
"sortBy": "-_sys.createdAt",
"schemaTitle": "{{name}} {{slug}}",
"schemaImage": "cover",
"revisions": 15,
"read": [
"admin",
"editors",
"reviewers"
],
"write": [
"admin",
"editors"
],
"fields": []
} }
``` ```
+1 -1
View File
@@ -47,7 +47,7 @@ In order to make it accessible you have to create symlink:
php artisan lucent:livelink php artisan lucent:livelink
``` ```
Now your static website is accessible in http://localhost:8000/live Now your static website is accessible at `http://localhost:8000/live`
But it would not be nice to have the **live** prefix everywhere. That's why you need to make the following tweak in the nginx vhost But it would not be nice to have the **live** prefix everywhere. That's why you need to make the following tweak in the nginx vhost