Compare commits
2 Commits
v3.0.0-bet
...
fix/expose
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4283bebc37 | ||
|
|
4082680099 |
1
.github/workflows/pr-title.yml
vendored
1
.github/workflows/pr-title.yml
vendored
@@ -5,6 +5,7 @@ on:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
@@ -122,19 +122,3 @@ This is how you can preview changes you made locally to the docs:
|
||||
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
|
||||
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
|
||||
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
|
||||
|
||||
## Internationalization (i18n)
|
||||
|
||||
If your PR adds a string to the UI, we need to make sure to translate it into all the languages that Payload supports. To do that:
|
||||
|
||||
- Find the appropriate internationalization file for your package. These are typically located in `packages/translations/src/languages`, although some packages (e.g., richtext-lexical) have separate i18n files for each feature.
|
||||
- Add the string to the English locale "en".
|
||||
- Translate it to other languages. You can use the `translateNewKeys` script if you have an OpenAI API key in your `.env` (under `OPENAI_KEY`), or you can use ChatGPT or Google translate - whatever is easier for you. For payload core translations (in packages/translations) you can run the `translateNewKeys` script using `cd packages/translations && pnpm translateNewKeys`. For lexical translations, you can run it using `cd packages/richtext-lexical && pnpm translateNewKeys`. External contributors can skip this step and leave it to us.
|
||||
|
||||
To display translation strings in the UI, make sure to use the `t` utility of the `useTranslation` hook:
|
||||
|
||||
```ts
|
||||
const { t } = useTranslation()
|
||||
// ...
|
||||
t('yourStringKey')
|
||||
```
|
||||
|
||||
@@ -79,7 +79,7 @@ Returns a boolean which allows/denies access to the `create` request.
|
||||
To add create Access Control to a Collection, use the `create` property in the [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const CollectionWithCreateAccess: CollectionConfig = {
|
||||
// ...
|
||||
|
||||
@@ -33,7 +33,7 @@ Access Control is specific to the operation of the request.
|
||||
To add Access Control to a Field, use the `access` property in the [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload';
|
||||
import { CollectionConfig } from 'payload';
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
|
||||
@@ -11,7 +11,7 @@ The behavior of [Collections](../configuration/collections) within the [Admin Pa
|
||||
To configure Admin Options for Collections, use the `admin` property in your Collection Config:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
// ...
|
||||
@@ -89,7 +89,7 @@ It is possible to display a Preview Button within the Edit View of the Admin Pan
|
||||
To configure the Preview Button, set the `admin.preview` property to a function in your [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
// ...
|
||||
@@ -126,7 +126,7 @@ All Collections receive their own List View which displays a paginated list of d
|
||||
To configure pagination options, use the `admin.pagination` property in your [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
// ...
|
||||
@@ -155,7 +155,7 @@ In the List View, there is a "search" box that allows you to quickly find a docu
|
||||
To define which fields should be searched, use the `admin.listSearchableFields` property in your [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
// ...
|
||||
|
||||
@@ -217,7 +217,6 @@ Client Component:
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import type { TextFieldClientComponent } from 'payload'
|
||||
import { TextField } from '@payloadcms/ui'
|
||||
|
||||
export const MyTextField: TextFieldClientComponent = ({ field }) => {
|
||||
return <TextField field={field} />
|
||||
|
||||
@@ -29,7 +29,7 @@ The lockDocuments property exists on both the Collection Config and the Global C
|
||||
Here’s an example configuration for document locking:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
|
||||
@@ -36,7 +36,7 @@ To customize Root Metadata, use the `admin.meta` key in your Payload Config:
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
url: '/favicon.png',
|
||||
href: '/favicon.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -80,12 +80,12 @@ To customize icons, use the `icons` key within the `admin.meta` object in your P
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
url: '/favicon.png',
|
||||
href: '/favicon.png',
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
type: 'image/png',
|
||||
url: '/apple-touch-icon.png',
|
||||
href: '/apple-touch-icon.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -140,7 +140,7 @@ The following options are available for Open Graph Metadata:
|
||||
| Key | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| **`description`** | `string` | The description of the Admin Panel. |
|
||||
| **`images`** | `OGImageConfig` or `OGImageConfig[]` | An array of image objects. |
|
||||
| **`images`** | `OGImageConfig | OGImageConfig[]` | An array of image objects. |
|
||||
| **`siteName`** | `string` | The name of the site. |
|
||||
| **`title`** | `string` | The title of the Admin Panel. |
|
||||
|
||||
@@ -151,7 +151,7 @@ Collection Metadata is the metadata that is applied to all pages within any give
|
||||
To customize Collection Metadata, use the `admin.meta` key within your Collection Config:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
// ...
|
||||
|
||||
@@ -38,7 +38,7 @@ At its core a strategy simply takes information from the incoming request and re
|
||||
Your `authenticate` method should return an object containing a Payload user document and any optional headers that you'd like Payload to set for you when we return a response.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
|
||||
@@ -15,7 +15,7 @@ Email Verification forces users to prove they have access to the email address t
|
||||
To enable Email Verification, use the `auth.verify` property on your [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
@@ -42,7 +42,7 @@ The following options are available:
|
||||
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
@@ -74,7 +74,7 @@ export const Customers: CollectionConfig = {
|
||||
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
@@ -95,7 +95,7 @@ export const Customers: CollectionConfig = {
|
||||
You can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
@@ -119,7 +119,7 @@ The following options are available:
|
||||
This function allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
@@ -179,7 +179,7 @@ The following arguments are passed to the `generateEmailHTML` function:
|
||||
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Customers: CollectionConfig = {
|
||||
// ...
|
||||
|
||||
@@ -25,7 +25,7 @@ When Authentication is enabled on a [Collection](../configuration/collections),
|
||||
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
// ...
|
||||
@@ -48,7 +48,7 @@ Any [Collection](../configuration/collections) can opt-in to supporting Authenti
|
||||
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Admins: CollectionConfig = {
|
||||
// ...
|
||||
|
||||
@@ -98,14 +98,14 @@ From there, you are ready to make updates to your project. When you are ready to
|
||||
|
||||
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload Config:
|
||||
|
||||
`yarn add @payloadcms/payload-cloud`
|
||||
`yarn add @payloadcms/plugin-cloud`
|
||||
|
||||
```js
|
||||
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
|
||||
import { payloadCloud } from '@payloadcms/plugin-cloud'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
plugins: [payloadCloudPlugin()],
|
||||
plugins: [payloadCloud()],
|
||||
// rest of config
|
||||
})
|
||||
```
|
||||
@@ -115,11 +115,6 @@ export default buildConfig({
|
||||
over Payload Cloud's email service.
|
||||
</Banner>
|
||||
|
||||
<Banner type="info">
|
||||
Good to know: the Payload Cloud Plugin was previously named `@payloadcms/plugin-cloud`. If you are
|
||||
using this plugin, you should update to the new package name.
|
||||
</Banner>
|
||||
|
||||
#### **Optional configuration**
|
||||
|
||||
If you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.
|
||||
|
||||
@@ -37,7 +37,7 @@ It's often best practice to write your Collections in separate files and then im
|
||||
Here is what a simple Collection Config might look like:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
@@ -57,26 +57,26 @@ export const Posts: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |
|
||||
| **`access`** | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
|
||||
| **`auth`** | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`disableDuplicate`** | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
|
||||
| **`defaultSort`** | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Multiple fields can be specified by using a string array. |
|
||||
| **`dbName`** | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
|
||||
| **`endpoints`** | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
|
||||
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
|
||||
| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
|
||||
| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
|
||||
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
|
||||
| **`lockDocuments`** | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
|
||||
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| **`timestamps`** | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
|
||||
| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
|
||||
| Option | Description |
|
||||
|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |
|
||||
| **`access`** | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
|
||||
| **`auth`** | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`disableDuplicate`** | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
|
||||
| **`defaultSort`** | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. |
|
||||
| **`dbName`** | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
|
||||
| **`endpoints`** | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
|
||||
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
|
||||
| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
|
||||
| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
|
||||
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
|
||||
| **`lockDocuments`** | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
|
||||
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| **`timestamps`** | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
|
||||
| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ While Payload's built-in features come fully translated, you may also want to tr
|
||||
To do this, provide the translations wherever applicable, keyed to the language code:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Articles: CollectionConfig = {
|
||||
slug: 'articles',
|
||||
|
||||
@@ -216,7 +216,7 @@ Cross-origin resource sharing (CORS) can be configured with either a whitelist a
|
||||
Here's an example showing how to allow incoming requests from any domain:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
@@ -227,7 +227,7 @@ export default buildConfig({
|
||||
Here's an example showing how to append a new header (`x-custom-header`) in `Access-Control-Allow-Headers`:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
|
||||
@@ -24,7 +24,7 @@ Arrays are useful for many different types of content from simple to complex, su
|
||||
To create an Array Field, set the `type` to `array` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyArrayField: Field = {
|
||||
// ...
|
||||
@@ -69,7 +69,7 @@ _\* An asterisk denotes that a property is required._
|
||||
To customize the appearance and behavior of the Array Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyArrayField: Field = {
|
||||
// ...
|
||||
@@ -92,7 +92,7 @@ The Array Field inherits all of the default options from the base [Field Admin C
|
||||
In this example, we have an Array Field called `slider` that contains a set of fields for a simple image slider. Each row in the array has a `title`, `image`, and `caption`. We also customize the row label to display the title if it exists, or a default label if it doesn't.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -24,7 +24,7 @@ Blocks are a great way to create a flexible content model that can be used to bu
|
||||
To add a Blocks Field, set the `type` to `blocks` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyBlocksField: Field = {
|
||||
// ...
|
||||
@@ -67,7 +67,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Blocks Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyBlocksField: Field = {
|
||||
// ...
|
||||
|
||||
@@ -18,7 +18,7 @@ The Checkbox Field saves a boolean in the database.
|
||||
To add a Checkbox Field, set the `type` to `checkbox` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyCheckboxField: Field = {
|
||||
// ...
|
||||
@@ -53,7 +53,7 @@ _\* An asterisk denotes that a property is required._
|
||||
Here is an example of a Checkbox Field in a Collection:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -19,7 +19,7 @@ The Code Field saves a string in the database, but provides the [Admin Panel](..
|
||||
To add a Code Field, set the `type` to `code` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyBlocksField: Field = {
|
||||
// ...
|
||||
@@ -57,7 +57,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Code Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyCodeField: Field = {
|
||||
// ...
|
||||
@@ -79,7 +79,7 @@ The Code Field inherits all of the default options from the base [Field Admin Co
|
||||
`collections/ExampleCollection.ts
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Collapsible Field is presentational-only and only affects the Admin Panel. B
|
||||
To add a Collapsible Field, set the `type` to `collapsible` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyCollapsibleField: Field = {
|
||||
// ...
|
||||
@@ -47,7 +47,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Collapsible Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyCollapsibleField: Field = {
|
||||
// ...
|
||||
@@ -68,7 +68,7 @@ The Collapsible Field inherits all of the default options from the base [Field A
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Date Field saves a Date in the database and provides the [Admin Panel](../ad
|
||||
To add a Date Field, set the `type` to `date` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyDateField: Field = {
|
||||
// ...
|
||||
@@ -53,7 +53,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Date Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyDateField: Field = {
|
||||
// ...
|
||||
@@ -97,7 +97,7 @@ When only `pickerAppearance` is set, an equivalent format will be rendered in th
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Email Field enforces that the value provided is a valid email address.
|
||||
To create an Email Field, set the `type` to `email` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyEmailField: Field = {
|
||||
// ...
|
||||
@@ -54,7 +54,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Email Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyEmailField: Field = {
|
||||
// ...
|
||||
@@ -76,7 +76,7 @@ The Email Field inherits all of the default options from the base [Field Admin C
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Group Field allows [Fields](./overview) to be nested under a common property
|
||||
To add a Group Field, set the `type` to `group` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyGroupField: Field = {
|
||||
// ...
|
||||
@@ -58,7 +58,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Group Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyGroupField: Field = {
|
||||
// ...
|
||||
@@ -79,7 +79,7 @@ The Group Field inherits all of the default options from the base [Field Admin C
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -6,10 +6,8 @@ desc: The Join field provides the ability to work on related documents. Learn ho
|
||||
keywords: join, relationship, junction, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
The Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can
|
||||
edit and view collections
|
||||
having reference to a specific collection document. The field itself acts as a virtual field, in that no new data is
|
||||
stored on the collection with a Join
|
||||
The Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can edit and view collections
|
||||
having reference to a specific collection document. The field itself acts as a virtual field, in that no new data is stored on the collection with a Join
|
||||
field. Instead, the Admin UI surfaces the related documents for a better editing experience and is surfaced by Payload's
|
||||
APIs.
|
||||
|
||||
@@ -21,10 +19,10 @@ The Join field is useful in scenarios including:
|
||||
- Displaying where a document or upload is used in other documents
|
||||
|
||||
<LightDarkImage
|
||||
srcLight="https://payloadcms.com/images/docs/fields/join.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/join-dark.png"
|
||||
alt="Shows Join field in the Payload Admin Panel"
|
||||
caption="Admin Panel screenshot of Join field"
|
||||
srcLight="https://payloadcms.com/images/docs/fields/join.png"
|
||||
srcDark="https://payloadcms.com/images/docs/fields/join-dark.png"
|
||||
alt="Shows Join field in the Payload Admin Panel"
|
||||
caption="Admin Panel screenshot of Join field"
|
||||
/>
|
||||
|
||||
For the Join field to work, you must have an existing [relationship](./relationship) or [upload](./upload) field in the
|
||||
@@ -32,7 +30,7 @@ collection you are joining. This will reference the collection and path of the f
|
||||
To add a Relationship Field, set the `type` to `join` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyJoinField: Field = {
|
||||
// highlight-start
|
||||
@@ -113,11 +111,9 @@ related docs from a new pseudo-junction collection called `categories_posts`. No
|
||||
third junction collection, and can be surfaced on both Posts and Categories. But, importantly, you could add
|
||||
additional "context" fields to this shared junction collection.
|
||||
|
||||
For example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add
|
||||
custom "context" fields like `featured` or `spotlight`,
|
||||
For example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add custom "context" fields like `featured` or `spotlight`,
|
||||
which would allow you to store additional information directly on relationships.
|
||||
The `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a
|
||||
powerful Admin UI.
|
||||
The `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a powerful Admin UI.
|
||||
|
||||
## Config Options
|
||||
|
||||
@@ -130,11 +126,11 @@ powerful Admin UI.
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |
|
||||
| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -154,12 +150,12 @@ object with:
|
||||
{
|
||||
"id": "66e3431a3f23e684075aaeb9",
|
||||
// other fields...
|
||||
"category": "66e3431a3f23e684075aae9c"
|
||||
}
|
||||
"category": "66e3431a3f23e684075aae9c",
|
||||
},
|
||||
// { ... }
|
||||
],
|
||||
"hasNextPage": false
|
||||
}
|
||||
},
|
||||
// other fields...
|
||||
}
|
||||
```
|
||||
@@ -217,8 +213,7 @@ You can specify as many `joins` parameters as needed for the same or different j
|
||||
|
||||
### GraphQL
|
||||
|
||||
The GraphQL API supports the same query options as the local and REST APIs. You can specify the query options for each
|
||||
join field in your query.
|
||||
The GraphQL API supports the same query options as the local and REST APIs. You can specify the query options for each join field in your query.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -231,9 +226,9 @@ query {
|
||||
limit: 5
|
||||
where: {
|
||||
author: {
|
||||
equals: "66e3431a3f23e684075aaeb9"
|
||||
equals: "66e3431a3f23e684075aaeb9"
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
docs {
|
||||
title
|
||||
|
||||
@@ -19,7 +19,7 @@ The JSON Field saves actual JSON in the database, which differs from the Code fi
|
||||
To add a JSON Field, set the `type` to `json` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyJSONField: Field = {
|
||||
// ...
|
||||
@@ -56,7 +56,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the JSON Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyJSONField: Field = {
|
||||
// ...
|
||||
@@ -77,7 +77,7 @@ The JSON Field inherits all of the default options from the base [Field Admin Co
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -102,7 +102,7 @@ If you only provide a URL to a schema, Payload will fetch the desired schema if
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -135,7 +135,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Number Field stores and validates numeric entry and supports additional nume
|
||||
To add a Number Field, set the `type` to `number` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyNumberField: Field = {
|
||||
// ...
|
||||
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Number Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyNumberField: Field = {
|
||||
// ...
|
||||
@@ -82,7 +82,7 @@ The Number Field inherits all of the default options from the base [Field Admin
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Point Field saves a pair of coordinates in the database and assigns an index
|
||||
To add a Point Field, set the `type` to `point` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyPointField: Field = {
|
||||
// ...
|
||||
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Radio Field allows for the selection of one value from a predefined set of p
|
||||
To add a Radio Field, set the `type` to `radio` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyRadioField: Field = {
|
||||
// ...
|
||||
@@ -69,7 +69,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Radio Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyRadioField: Field = {
|
||||
// ...
|
||||
@@ -90,7 +90,7 @@ The Radio Field inherits all of the default options from the base [Field Admin C
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -24,7 +24,7 @@ The Relationship field is used in a variety of ways, including:
|
||||
To add a Relationship Field, set the `type` to `relationship` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyRelationshipField: Field = {
|
||||
// ...
|
||||
@@ -74,7 +74,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Relationship Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyRelationshipField: Field = {
|
||||
// ...
|
||||
@@ -159,7 +159,7 @@ called with an argument object with the following properties:
|
||||
## Example
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -63,7 +63,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyRichTextField: Field = {
|
||||
// ...
|
||||
|
||||
@@ -18,7 +18,7 @@ The Row Field is presentational-only and only affects the [Admin Panel](../admin
|
||||
To add a Row Field, set the `type` to `row` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyRowField: Field = {
|
||||
// ...
|
||||
@@ -46,7 +46,7 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Select Field provides a dropdown-style interface for choosing options from a
|
||||
To add a Select Field, set the `type` to `select` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MySelectField: Field = {
|
||||
// ...
|
||||
@@ -71,7 +71,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MySelectField: Field = {
|
||||
// ...
|
||||
@@ -93,7 +93,7 @@ The Select Field inherits all of the default options from the base [Field Admin
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -156,7 +156,8 @@ You can import the existing Select component directly from Payload, then extend
|
||||
|
||||
```ts
|
||||
import * as React from 'react';
|
||||
import { SelectInput, useAuth, useField } from '@payloadcms/ui';
|
||||
import { SelectInput, useField } from 'payload/components/forms';
|
||||
import { useAuth } from 'payload/components/utilities';
|
||||
|
||||
type CustomSelectProps = {
|
||||
path: string;
|
||||
|
||||
@@ -18,7 +18,7 @@ The Tabs Field is presentational-only and only affects the [Admin Panel](../admi
|
||||
To add a Tabs Field, set the `type` to `tabs` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyTabsField: Field = {
|
||||
// ...
|
||||
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Text Field is one of the most commonly used fields. It saves a string to the
|
||||
To add a Text Field, set the `type` to `text` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyTextField: Field = {
|
||||
// ...
|
||||
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyTextField: Field = {
|
||||
// ...
|
||||
@@ -82,7 +82,7 @@ The Text Field inherits all of the default options from the base [Field Admin Co
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ The Textarea Field is nearly identical to the [Text Field](./text) but it featur
|
||||
To add a Textarea Field, set the `type` to `textarea` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyTextareaField: Field = {
|
||||
// ...
|
||||
@@ -56,7 +56,7 @@ _\* An asterisk denotes that a property is required._
|
||||
The customize the appearance and behavior of the Textarea Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyTextareaField: Field = {
|
||||
// ...
|
||||
@@ -79,7 +79,7 @@ The Textarea Field inherits all of the default options from the base [Field Admi
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -18,7 +18,7 @@ With the UI Field, you can:
|
||||
To add a UI Field, set the `type` to `ui` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyUIField: Field = {
|
||||
// ...
|
||||
@@ -44,7 +44,7 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -25,7 +25,7 @@ caption="Admin Panel screenshot of an Upload field"
|
||||
To create an Upload Field, set the `type` to `upload` in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
export const MyUploadField: Field = {
|
||||
// ...
|
||||
@@ -73,7 +73,7 @@ _\* An asterisk denotes that a property is required._
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
|
||||
@@ -54,12 +54,12 @@ To install a Database Adapter, you can run **one** of the following commands:
|
||||
|
||||
- To install the [MongoDB Adapter](../database/mongodb), run:
|
||||
```bash
|
||||
pnpm i @payloadcms/db-mongodb@beta
|
||||
pnpm i @payloadcms/db-mongodb
|
||||
```
|
||||
|
||||
- To install the [Postgres Adapter](../database/postgres), run:
|
||||
```bash
|
||||
pnpm i @payloadcms/db-postgres@beta
|
||||
pnpm i @payloadcms/db-postgres
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
|
||||
@@ -13,7 +13,7 @@ In Payload the schema is controlled by your collections and globals. All you nee
|
||||
Install `@payloadcms/graphql` as a dev dependency:
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/graphql@beta -D
|
||||
pnpm add @payloadcms/graphql --save-dev
|
||||
```
|
||||
|
||||
Run the following command to generate the schema:
|
||||
|
||||
@@ -36,7 +36,7 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-form-builder@beta
|
||||
pnpm add @payloadcms/plugin-form-builder
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -48,7 +48,7 @@ Install the plugin using any JavaScript package manager like [Yarn](https://yarn
|
||||
or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-nested-docs@beta
|
||||
pnpm add @payloadcms/plugin-nested-docs
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
@@ -177,7 +177,7 @@ You can also extend the built-in `parent` and `breadcrumbs` fields per collectio
|
||||
and `createBreadcrumbField` methods. They will merge your customizations overtop the plugin's base field configurations.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
import { createParentField } from '@payloadcms/plugin-nested-docs/fields'
|
||||
import { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs/fields'
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ For example, if you have a page at `/about` and you want to change it to `/about
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-redirects@beta
|
||||
pnpm add @payloadcms/plugin-redirects
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -39,7 +39,7 @@ This plugin is a great way to implement a fast, immersive search experience such
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-search@beta
|
||||
pnpm add @payloadcms/plugin-search
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -39,7 +39,7 @@ This multi-faceted software offers a range of features that will help you manage
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-sentry@beta
|
||||
pnpm add @payloadcms/plugin-sentry
|
||||
```
|
||||
|
||||
## Sentry for Next.js setup
|
||||
|
||||
@@ -37,7 +37,7 @@ To help you visualize what your page might look like in a search engine, a previ
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-seo@beta
|
||||
pnpm add @payloadcms/plugin-seo
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -39,7 +39,7 @@ The beauty of this plugin is the entirety of your application's content and busi
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-stripe@beta
|
||||
pnpm add @payloadcms/plugin-stripe
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Payload sort allows you to order your documents by a field in ascending or
|
||||
keywords: query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
Documents in Payload can be easily sorted by a specific [Field](../fields/overview). When querying Documents, you can pass the name of any top-level field, and the response will sort the Documents by that field in _ascending_ order. If prefixed with a minus symbol ("-"), they will be sorted in _descending_ order. In Local API multiple fields can be specificed by using an array of strings. In REST API multiple fields can be specified by separating fields with comma. The minus symbol can be in front of individual fields.
|
||||
Documents in Payload can be easily sorted by a specific [Field](../fields/overview). When querying Documents, you can pass the name of any top-level field, and the response will sort the Documents by that field in _ascending_ order. If prefixed with a minus symbol ("-"), they will be sorted in _descending_ order.
|
||||
|
||||
Because sorting is handled by the database, the field cannot be a [Virtual Field](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges). It must be stored in the database to be searchable.
|
||||
|
||||
@@ -30,19 +30,6 @@ const getPosts = async () => {
|
||||
}
|
||||
```
|
||||
|
||||
To sort by multiple fields, you can use the `sort` option with fields in an array:
|
||||
|
||||
```ts
|
||||
const getPosts = async () => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
sort: ['priority', '-createdAt'], // highlight-line
|
||||
})
|
||||
|
||||
return posts
|
||||
}
|
||||
```
|
||||
|
||||
## REST API
|
||||
|
||||
To sort in the [REST API](../rest-api/overview), you can use the `sort` parameter in your query:
|
||||
@@ -53,14 +40,6 @@ fetch('https://localhost:3000/api/posts?sort=-createdAt') // highlight-line
|
||||
.then((data) => console.log(data))
|
||||
```
|
||||
|
||||
To sort by multiple fields, you can use the `sort` parameter with fields separated by comma:
|
||||
|
||||
```ts
|
||||
fetch('https://localhost:3000/api/posts?sort=priority,-createdAt') // highlight-line
|
||||
.then((response) => response.json())
|
||||
.then((data) => console.log(data))
|
||||
```
|
||||
|
||||
## GraphQL API
|
||||
|
||||
To sort in the [GraphQL API](../graphql/overview), you can use the `sort` parameter in your query:
|
||||
|
||||
@@ -591,7 +591,7 @@ Each endpoint object needs to have:
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
// a collection of 'orders' with an additional route for tracking details, reachable at /api/orders/:id/tracking
|
||||
export const Orders: CollectionConfig = {
|
||||
|
||||
@@ -34,7 +34,7 @@ export default buildConfig({
|
||||
And here's an example for how to install the Slate editor on a field-by-field basis, while customizing its options:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
|
||||
@@ -43,7 +43,7 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
|
||||
</Banner>
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
@@ -217,7 +217,7 @@ You can specify how Payload retrieves admin thumbnails for your upload-enabled C
|
||||
1. `adminThumbnail` as a **string**, equal to one of your provided image size names.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
@@ -246,7 +246,7 @@ export const Media: CollectionConfig = {
|
||||
2. `adminThumbnail` as a **function** that takes the document's data and sends back a full URL to load the thumbnail.
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
@@ -267,7 +267,7 @@ Some example values are: `image/*`, `audio/*`, `video/*`, `image/png`, `applicat
|
||||
**Example mimeTypes usage:**
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
|
||||
@@ -22,7 +22,7 @@ Payload offers additional storage adapters to handle file uploads. These adapter
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-vercel-blob@beta
|
||||
pnpm add @payloadcms/storage-vercel-blob
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -71,7 +71,7 @@ export default buildConfig({
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-s3@beta
|
||||
pnpm add @payloadcms/storage-s3
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -119,7 +119,7 @@ See the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3Clie
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-azure@beta
|
||||
pnpm add @payloadcms/storage-azure
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -168,7 +168,7 @@ export default buildConfig({
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-gcs@beta
|
||||
pnpm add @payloadcms/storage-gcs
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -218,7 +218,7 @@ export default buildConfig({
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-uploadthing@beta
|
||||
pnpm add @payloadcms/storage-uploadthing
|
||||
```
|
||||
|
||||
### Usage
|
||||
@@ -261,7 +261,7 @@ If you need to create a custom storage adapter, you can use the [`@payloadcms/pl
|
||||
|
||||
### Installation
|
||||
|
||||
`pnpm add @payloadcms/plugin-cloud-storage@beta`
|
||||
`pnpm add @payloadcms/plugin-cloud-storage`
|
||||
|
||||
### Usage
|
||||
|
||||
|
||||
24
package.json
24
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -29,7 +29,7 @@
|
||||
"build:live-preview-vue": "turbo build --filter \"@payloadcms/live-preview-vue\"",
|
||||
"build:next": "turbo build --filter \"@payloadcms/next\"",
|
||||
"build:payload": "turbo build --filter payload",
|
||||
"build:payload-cloud": "turbo build --filter \"@payloadcms/payload-cloud\"",
|
||||
"build:plugin-cloud": "turbo build --filter \"@payloadcms/plugin-cloud\"",
|
||||
"build:plugin-cloud-storage": "turbo build --filter \"@payloadcms/plugin-cloud-storage\"",
|
||||
"build:plugin-form-builder": "turbo build --filter \"@payloadcms/plugin-form-builder\"",
|
||||
"build:plugin-nested-docs": "turbo build --filter \"@payloadcms/plugin-nested-docs\"",
|
||||
@@ -105,12 +105,12 @@
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@libsql/client": "0.14.0",
|
||||
"@next/bundle-analyzer": "15.0.0",
|
||||
"@next/bundle-analyzer": "15.0.0-canary.173",
|
||||
"@payloadcms/db-postgres": "workspace:*",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@payloadcms/eslint-plugin": "workspace:*",
|
||||
"@payloadcms/live-preview-react": "workspace:*",
|
||||
"@playwright/test": "1.48.1",
|
||||
"@playwright/test": "1.46.0",
|
||||
"@sentry/nextjs": "^8.33.1",
|
||||
"@sentry/node": "^8.33.1",
|
||||
"@swc-node/register": "1.10.9",
|
||||
@@ -145,15 +145,15 @@
|
||||
"lint-staged": "15.2.7",
|
||||
"minimist": "1.2.8",
|
||||
"mongodb-memory-server": "^9.0",
|
||||
"next": "15.0.0",
|
||||
"next": "15.0.0-canary.173",
|
||||
"open": "^10.1.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"playwright": "1.48.1",
|
||||
"playwright-core": "1.48.1",
|
||||
"playwright": "1.46.0",
|
||||
"playwright-core": "1.46.0",
|
||||
"prettier": "3.3.3",
|
||||
"prompts": "2.4.2",
|
||||
"react": "19.0.0-rc-65a56d0e-20241020",
|
||||
"react-dom": "19.0.0-rc-65a56d0e-20241020",
|
||||
"react": "19.0.0-rc-3edc000d-20240926",
|
||||
"react-dom": "19.0.0-rc-3edc000d-20240926",
|
||||
"rimraf": "3.0.2",
|
||||
"semver": "^7.5.4",
|
||||
"sharp": "0.32.6",
|
||||
@@ -164,11 +164,11 @@
|
||||
"tempy": "1.0.1",
|
||||
"tsx": "4.19.1",
|
||||
"turbo": "^2.1.3",
|
||||
"typescript": "5.6.3"
|
||||
"typescript": "5.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-3edc000d-20240926",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-3edc000d-20240926"
|
||||
},
|
||||
"packageManager": "pnpm@9.7.1",
|
||||
"engines": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -54,7 +54,7 @@ function getEnvironmentPackageManager(): PackageManager {
|
||||
|
||||
async function commandExists(command: string): Promise<boolean> {
|
||||
try {
|
||||
await execa.command(process.platform === 'win32' ? `where ${command}` : `command -v ${command}`)
|
||||
await execa.command(`command -v ${command}`)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
|
||||
@@ -226,7 +226,7 @@ async function installDeps(projectDir: string, packageManager: PackageManager, d
|
||||
'payload',
|
||||
'@payloadcms/next',
|
||||
'@payloadcms/richtext-lexical',
|
||||
'@payloadcms/payload-cloud',
|
||||
'@payloadcms/plugin-cloud',
|
||||
].map((pkg) => `${pkg}@beta`)
|
||||
|
||||
packagesToInstall.push(`@payloadcms/db-${dbType}@beta`)
|
||||
|
||||
@@ -82,8 +82,8 @@ const vercelBlobStorageReplacement: StorageAdapterReplacement = {
|
||||
|
||||
const payloadCloudReplacement: StorageAdapterReplacement = {
|
||||
configReplacement: [' payloadCloudPlugin(),'],
|
||||
importReplacement: "import { payloadCloudPlugin } from '@payloadcms/payload-cloud'",
|
||||
packageName: '@payloadcms/payload-cloud',
|
||||
importReplacement: "import { payloadCloudPlugin } from '@payloadcms/plugin-cloud'",
|
||||
packageName: '@payloadcms/plugin-cloud',
|
||||
}
|
||||
|
||||
// Removes placeholders
|
||||
|
||||
@@ -18,7 +18,7 @@ const dbChoiceRecord: Record<DbType, DbChoice> = {
|
||||
},
|
||||
postgres: {
|
||||
dbConnectionPrefix: 'postgres://postgres:<password>@127.0.0.1:5432/',
|
||||
title: 'PostgreSQL',
|
||||
title: 'PostgreSQL (beta)',
|
||||
value: 'postgres',
|
||||
},
|
||||
sqlite: {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import type { CliArgs, DbType, ProjectTemplate } from '../types.js'
|
||||
import type { CliArgs, ProjectTemplate } from '../types.js'
|
||||
|
||||
import { debug, error } from '../utils/log.js'
|
||||
|
||||
/** Parse and swap .env.example values and write .env */
|
||||
export async function writeEnvFile(args: {
|
||||
cliArgs: CliArgs
|
||||
databaseType?: DbType
|
||||
databaseUri: string
|
||||
payloadSecret: string
|
||||
projectDir: string
|
||||
template?: ProjectTemplate
|
||||
}): Promise<void> {
|
||||
const { cliArgs, databaseType, databaseUri, payloadSecret, projectDir, template } = args
|
||||
const { cliArgs, databaseUri, payloadSecret, projectDir, template } = args
|
||||
|
||||
if (cliArgs['--dry-run']) {
|
||||
debug(`DRY RUN: .env file created`)
|
||||
@@ -42,7 +41,7 @@ export async function writeEnvFile(args: {
|
||||
}
|
||||
|
||||
const split = line.split('=')
|
||||
let key = split[0]
|
||||
const key = split[0]
|
||||
let value = split[1]
|
||||
|
||||
if (
|
||||
@@ -51,9 +50,6 @@ export async function writeEnvFile(args: {
|
||||
key === 'DATABASE_URI' ||
|
||||
key === 'POSTGRES_URL'
|
||||
) {
|
||||
if (databaseType === 'vercel-postgres') {
|
||||
key = 'POSTGRES_URL'
|
||||
}
|
||||
value = databaseUri
|
||||
}
|
||||
if (key === 'PAYLOAD_SECRET' || key === 'PAYLOAD_SECRET_KEY') {
|
||||
|
||||
@@ -180,7 +180,6 @@ export class Main {
|
||||
|
||||
await writeEnvFile({
|
||||
cliArgs: this.args,
|
||||
databaseType: dbDetails.type,
|
||||
databaseUri: dbDetails.dbUri,
|
||||
payloadSecret: generateSecret(),
|
||||
projectDir,
|
||||
@@ -223,7 +222,6 @@ export class Main {
|
||||
})
|
||||
await writeEnvFile({
|
||||
cliArgs: this.args,
|
||||
databaseType: dbDetails.type,
|
||||
databaseUri: dbDetails.dbUri,
|
||||
payloadSecret,
|
||||
projectDir,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { Field, SanitizedConfig, Sort } from 'payload'
|
||||
import type { Field, SanitizedConfig } from 'payload'
|
||||
|
||||
import { getLocalizedSortProperty } from './getLocalizedSortProperty.js'
|
||||
|
||||
@@ -7,7 +7,7 @@ type Args = {
|
||||
config: SanitizedConfig
|
||||
fields: Field[]
|
||||
locale: string
|
||||
sort: Sort
|
||||
sort: string
|
||||
timestamps: boolean
|
||||
}
|
||||
|
||||
@@ -25,41 +25,32 @@ export const buildSortParam = ({
|
||||
sort,
|
||||
timestamps,
|
||||
}: Args): PaginateOptions['sort'] => {
|
||||
let sortProperty: string
|
||||
let sortDirection: SortDirection = 'desc'
|
||||
|
||||
if (!sort) {
|
||||
if (timestamps) {
|
||||
sort = '-createdAt'
|
||||
sortProperty = 'createdAt'
|
||||
} else {
|
||||
sort = '-id'
|
||||
sortProperty = '_id'
|
||||
}
|
||||
} else if (sort.indexOf('-') === 0) {
|
||||
sortProperty = sort.substring(1)
|
||||
} else {
|
||||
sortProperty = sort
|
||||
sortDirection = 'asc'
|
||||
}
|
||||
|
||||
if (typeof sort === 'string') {
|
||||
sort = [sort]
|
||||
}
|
||||
|
||||
const sorting = sort.reduce<PaginateOptions['sort']>((acc, item) => {
|
||||
let sortProperty: string
|
||||
let sortDirection: SortDirection
|
||||
if (item.indexOf('-') === 0) {
|
||||
sortProperty = item.substring(1)
|
||||
sortDirection = 'desc'
|
||||
} else {
|
||||
sortProperty = item
|
||||
sortDirection = 'asc'
|
||||
}
|
||||
if (sortProperty === 'id') {
|
||||
acc['_id'] = sortDirection
|
||||
return acc
|
||||
}
|
||||
const localizedProperty = getLocalizedSortProperty({
|
||||
if (sortProperty === 'id') {
|
||||
sortProperty = '_id'
|
||||
} else {
|
||||
sortProperty = getLocalizedSortProperty({
|
||||
config,
|
||||
fields,
|
||||
locale,
|
||||
segments: sortProperty.split('.'),
|
||||
})
|
||||
acc[localizedProperty] = sortDirection
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
return sorting
|
||||
return { [sortProperty]: sortDirection }
|
||||
}
|
||||
|
||||
@@ -78,11 +78,7 @@ export const sanitizeQueryValue = ({
|
||||
return { operator: formattedOperator, val: undefined }
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(val) || (typeof val === 'string' && val.split(',').length > 1)) {
|
||||
if (typeof val === 'string') {
|
||||
formattedValue = createArrayFromCommaDelineated(val)
|
||||
}
|
||||
|
||||
} else if (Array.isArray(val)) {
|
||||
formattedValue = formattedValue.reduce((formattedValues, inVal) => {
|
||||
const newValues = [inVal]
|
||||
if (!hasCustomID) {
|
||||
|
||||
@@ -6,23 +6,12 @@ import { combineQueries, flattenWhereToOperators } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
collection,
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
|
||||
) {
|
||||
const VersionModel = this.versions[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
@@ -100,29 +89,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
paginationOptions.options.limit = limit
|
||||
}
|
||||
|
||||
let result
|
||||
|
||||
const aggregate = await buildJoinAggregation({
|
||||
adapter: this,
|
||||
collection,
|
||||
collectionConfig,
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
query: versionQuery,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
// build join aggregation
|
||||
if (aggregate) {
|
||||
result = await VersionModel.aggregatePaginate(
|
||||
VersionModel.aggregate(aggregate),
|
||||
paginationOptions,
|
||||
)
|
||||
} else {
|
||||
result = await VersionModel.paginate(versionQuery, paginationOptions)
|
||||
}
|
||||
|
||||
const result = await VersionModel.paginate(versionQuery, paginationOptions)
|
||||
const docs = JSON.parse(JSON.stringify(result.docs))
|
||||
|
||||
return {
|
||||
|
||||
@@ -15,8 +15,6 @@ type BuildJoinAggregationArgs = {
|
||||
locale: string
|
||||
// the where clause for the top collection
|
||||
query?: Where
|
||||
/** whether the query is from drafts */
|
||||
versions?: boolean
|
||||
}
|
||||
|
||||
export const buildJoinAggregation = async ({
|
||||
@@ -27,7 +25,6 @@ export const buildJoinAggregation = async ({
|
||||
limit,
|
||||
locale,
|
||||
query,
|
||||
versions,
|
||||
}: BuildJoinAggregationArgs): Promise<PipelineStage[] | undefined> => {
|
||||
if (Object.keys(collectionConfig.joins).length === 0 || joins === false) {
|
||||
return
|
||||
@@ -57,8 +54,8 @@ export const buildJoinAggregation = async ({
|
||||
const joinModel = adapter.collections[join.field.collection]
|
||||
|
||||
const {
|
||||
limit: limitJoin = join.field.defaultLimit ?? 10,
|
||||
sort: sortJoin = join.field.defaultSort || collectionConfig.defaultSort,
|
||||
limit: limitJoin = 10,
|
||||
sort: sortJoin,
|
||||
where: whereJoin,
|
||||
} = joins?.[join.schemaPath] || {}
|
||||
|
||||
@@ -66,7 +63,7 @@ export const buildJoinAggregation = async ({
|
||||
config: adapter.payload.config,
|
||||
fields: adapter.payload.collections[slug].config.fields,
|
||||
locale,
|
||||
sort: sortJoin,
|
||||
sort: sortJoin || collectionConfig.defaultSort,
|
||||
timestamps: true,
|
||||
})
|
||||
const sortProperty = Object.keys(sort)[0]
|
||||
@@ -93,7 +90,7 @@ export const buildJoinAggregation = async ({
|
||||
|
||||
if (adapter.payload.config.localization && locale === 'all') {
|
||||
adapter.payload.config.localization.localeCodes.forEach((code) => {
|
||||
const as = `${versions ? `version.${join.schemaPath}` : join.schemaPath}${code}`
|
||||
const as = `${join.schemaPath}${code}`
|
||||
|
||||
aggregate.push(
|
||||
{
|
||||
@@ -101,7 +98,7 @@ export const buildJoinAggregation = async ({
|
||||
as: `${as}.docs`,
|
||||
foreignField: `${join.field.on}${code}`,
|
||||
from: slug,
|
||||
localField: versions ? 'parent' : '_id',
|
||||
localField: '_id',
|
||||
pipeline,
|
||||
},
|
||||
},
|
||||
@@ -134,7 +131,7 @@ export const buildJoinAggregation = async ({
|
||||
} else {
|
||||
const localeSuffix =
|
||||
join.field.localized && adapter.payload.config.localization && locale ? `.${locale}` : ''
|
||||
const as = `${versions ? `version.${join.schemaPath}` : join.schemaPath}${localeSuffix}`
|
||||
const as = `${join.schemaPath}${localeSuffix}`
|
||||
|
||||
aggregate.push(
|
||||
{
|
||||
@@ -142,7 +139,7 @@ export const buildJoinAggregation = async ({
|
||||
as: `${as}.docs`,
|
||||
foreignField: `${join.field.on}${localeSuffix}`,
|
||||
from: slug,
|
||||
localField: versions ? 'parent' : '_id',
|
||||
localField: '_id',
|
||||
pipeline,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -43,7 +43,7 @@
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"prepack": "pnpm clean && pnpm turbo build",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build --filter=./",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build",
|
||||
"renamePredefinedMigrations": "node --no-deprecation --import @swc-node/register/esm-register ./scripts/renamePredefinedMigrations.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -66,7 +66,7 @@ export const connect: Connect = async function connect(
|
||||
}
|
||||
|
||||
const logger = this.logger || false
|
||||
this.drizzle = drizzle({ client: this.pool, logger, schema: this.schema })
|
||||
this.drizzle = drizzle(this.pool, { logger, schema: this.schema })
|
||||
|
||||
if (!hotReload) {
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { DrizzleConfig } from 'drizzle-orm'
|
||||
import type { PgSchema, PgTableFn, PgTransactionConfig } from 'drizzle-orm/pg-core'
|
||||
import type { Pool, PoolConfig } from 'pg'
|
||||
import type { Client, Pool, PoolConfig } from 'pg'
|
||||
|
||||
export type Args = {
|
||||
/**
|
||||
@@ -62,7 +62,9 @@ declare module 'payload' {
|
||||
afterSchemaInit: PostgresSchemaHook[]
|
||||
beforeSchemaInit: PostgresSchemaHook[]
|
||||
beginTransaction: (options?: PgTransactionConfig) => Promise<null | number | string>
|
||||
drizzle: PostgresDB
|
||||
drizzle: {
|
||||
$client: Client | Pool
|
||||
} & PostgresDB
|
||||
enums: Record<string, GenericEnum>
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type { Connect } from 'payload'
|
||||
|
||||
import { createClient } from '@libsql/client'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ChainedMethods } from '@payloadcms/drizzle/types'
|
||||
|
||||
import { chainMethods } from '@payloadcms/drizzle'
|
||||
import { count } from 'drizzle-orm'
|
||||
import { count, sql } from 'drizzle-orm'
|
||||
|
||||
import type { CountDistinct, SQLiteAdapter } from './types.js'
|
||||
|
||||
@@ -22,7 +22,11 @@ export const countDistinct: CountDistinct = async function countDistinct(
|
||||
methods: chainedMethods,
|
||||
query: db
|
||||
.select({
|
||||
count: count(),
|
||||
count:
|
||||
joins.length > 0
|
||||
? sql`count
|
||||
(DISTINCT ${this.tables[tableName].id})`.mapWith(Number)
|
||||
: count(),
|
||||
})
|
||||
.from(this.tables[tableName])
|
||||
.where(where),
|
||||
|
||||
@@ -70,6 +70,7 @@ export const init: Init = async function init(this: SQLiteAdapter) {
|
||||
disableNotNull: !!collection?.versions?.drafts,
|
||||
disableUnique: false,
|
||||
fields: collection.fields,
|
||||
joins: collection.joins,
|
||||
locales,
|
||||
tableName,
|
||||
timestamps: collection.timestamps,
|
||||
|
||||
@@ -61,6 +61,7 @@ type Args = {
|
||||
disableRelsTableUnique?: boolean
|
||||
disableUnique: boolean
|
||||
fields: Field[]
|
||||
joins?: SanitizedJoins
|
||||
locales?: [string, ...string[]]
|
||||
rootRelationships?: Set<string>
|
||||
rootRelationsToBuild?: RelationMap
|
||||
@@ -94,6 +95,7 @@ export const buildTable = ({
|
||||
disableRelsTableUnique,
|
||||
disableUnique = false,
|
||||
fields,
|
||||
joins,
|
||||
locales,
|
||||
rootRelationships,
|
||||
rootRelationsToBuild,
|
||||
@@ -142,6 +144,7 @@ export const buildTable = ({
|
||||
disableUnique,
|
||||
fields,
|
||||
indexes,
|
||||
joins,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
|
||||
@@ -44,6 +44,7 @@ type Args = {
|
||||
fields: (Field | TabAsField)[]
|
||||
forceLocalized?: boolean
|
||||
indexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
joins?: SanitizedJoins
|
||||
locales: [string, ...string[]]
|
||||
localesColumns: Record<string, SQLiteColumnBuilder>
|
||||
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
@@ -83,6 +84,7 @@ export const traverseFields = ({
|
||||
fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
@@ -667,6 +669,7 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
@@ -722,6 +725,7 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
forceLocalized: field.localized,
|
||||
indexes,
|
||||
joins,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
@@ -778,6 +782,7 @@ export const traverseFields = ({
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
@@ -834,6 +839,7 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
@@ -931,6 +937,30 @@ export const traverseFields = ({
|
||||
|
||||
break
|
||||
|
||||
case 'join': {
|
||||
// fieldName could be 'posts' or 'group_posts'
|
||||
// using `on` as the key for the relation
|
||||
const localized = adapter.payload.config.localization && field.localized
|
||||
const fieldSchemaPath = `${fieldPrefix || ''}${field.name}`
|
||||
let target: string
|
||||
const joinConfig = joins[field.collection].find(
|
||||
({ schemaPath }) => fieldSchemaPath === schemaPath,
|
||||
)
|
||||
if (joinConfig.targetField.hasMany) {
|
||||
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${adapter.relationshipsSuffix}`
|
||||
} else {
|
||||
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${localized ? adapter.localesSuffix : ''}`
|
||||
}
|
||||
relationsToBuild.set(fieldName, {
|
||||
type: 'many',
|
||||
// joins are not localized on the parent table
|
||||
localized: false,
|
||||
relationName: field.on.replaceAll('.', '_'),
|
||||
target,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -167,7 +167,9 @@ declare module 'payload' {
|
||||
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
|
||||
DrizzleAdapter {
|
||||
beginTransaction: (options?: SQLiteTransactionConfig) => Promise<null | number | string>
|
||||
drizzle: LibSQLDatabase
|
||||
drizzle: { $client: Client } & LibSQLDatabase<
|
||||
Record<string, GenericRelation | GenericTable> & Record<string, unknown>
|
||||
>
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
* Used for returning properly formed errors from unique fields
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-vercel-postgres",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "Vercel Postgres adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -26,8 +26,7 @@ export const connect: Connect = async function connect(
|
||||
const logger = this.logger || false
|
||||
// Passed the poolOptions if provided,
|
||||
// else have vercel/postgres detect the connection string from the environment
|
||||
this.drizzle = drizzle({
|
||||
client: this.poolOptions ? new VercelPool(this.poolOptions) : sql,
|
||||
this.drizzle = drizzle(this.poolOptions ? new VercelPool(this.poolOptions) : sql, {
|
||||
logger,
|
||||
schema: this.schema,
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
PostgresSchemaHook,
|
||||
} from '@payloadcms/drizzle/postgres'
|
||||
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { VercelPool, VercelPostgresPoolConfig } from '@vercel/postgres'
|
||||
import type { VercelClient, VercelPool, VercelPostgresPoolConfig } from '@vercel/postgres'
|
||||
import type { DrizzleConfig } from 'drizzle-orm'
|
||||
import type { PgSchema, PgTableFn, PgTransactionConfig } from 'drizzle-orm/pg-core'
|
||||
|
||||
@@ -56,6 +56,9 @@ export type Args = {
|
||||
}
|
||||
|
||||
export type VercelPostgresAdapter = {
|
||||
drizzle: {
|
||||
$client: VercelClient | VercelPool
|
||||
} & PostgresDB
|
||||
pool?: VercelPool
|
||||
poolOptions?: Args['pool']
|
||||
} & BasePostgresAdapter
|
||||
@@ -67,7 +70,9 @@ declare module 'payload' {
|
||||
afterSchemaInit: PostgresSchemaHook[]
|
||||
beforeSchemaInit: PostgresSchemaHook[]
|
||||
beginTransaction: (options?: PgTransactionConfig) => Promise<null | number | string>
|
||||
drizzle: PostgresDB
|
||||
drizzle: {
|
||||
$client: VercelClient | VercelPool
|
||||
} & PostgresDB
|
||||
enums: Record<string, GenericEnum>
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -21,7 +21,7 @@ export const find: Find = async function find(
|
||||
},
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const sort = sortArg !== undefined && sortArg !== null ? sortArg : collectionConfig.defaultSort
|
||||
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ type BuildFindQueryArgs = {
|
||||
joins?: BuildQueryJoinAliases
|
||||
locale?: string
|
||||
tableName: string
|
||||
versions?: boolean
|
||||
}
|
||||
|
||||
export type Result = {
|
||||
@@ -35,7 +34,6 @@ export const buildFindManyArgs = ({
|
||||
joins = [],
|
||||
locale,
|
||||
tableName,
|
||||
versions,
|
||||
}: BuildFindQueryArgs): Record<string, unknown> => {
|
||||
const result: Result = {
|
||||
extras: {},
|
||||
@@ -99,7 +97,6 @@ export const buildFindManyArgs = ({
|
||||
tablePath: '',
|
||||
topLevelArgs: result,
|
||||
topLevelTableName: tableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
@@ -14,7 +14,6 @@ type Args = {
|
||||
adapter: DrizzleAdapter
|
||||
fields: Field[]
|
||||
tableName: string
|
||||
versions?: boolean
|
||||
} & Omit<FindArgs, 'collection'>
|
||||
|
||||
export const findMany = async function find({
|
||||
@@ -29,7 +28,6 @@ export const findMany = async function find({
|
||||
skip,
|
||||
sort,
|
||||
tableName,
|
||||
versions,
|
||||
where: whereArg,
|
||||
}: Args) {
|
||||
const db = adapter.sessions[await req.transactionID]?.db || adapter.drizzle
|
||||
@@ -59,9 +57,9 @@ export const findMany = async function find({
|
||||
|
||||
const selectDistinctMethods: ChainedMethods = []
|
||||
|
||||
if (orderBy) {
|
||||
if (orderBy?.order && orderBy?.column) {
|
||||
selectDistinctMethods.push({
|
||||
args: [() => orderBy.map(({ column, order }) => order(column))],
|
||||
args: [orderBy.order(orderBy.column)],
|
||||
method: 'orderBy',
|
||||
})
|
||||
}
|
||||
@@ -73,7 +71,6 @@ export const findMany = async function find({
|
||||
joinQuery,
|
||||
joins,
|
||||
tableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
selectDistinctMethods.push({ args: [offset], method: 'offset' })
|
||||
@@ -114,7 +111,7 @@ export const findMany = async function find({
|
||||
} else {
|
||||
findManyArgs.limit = limit
|
||||
findManyArgs.offset = offset
|
||||
findManyArgs.orderBy = () => orderBy.map(({ column, order }) => order(column))
|
||||
findManyArgs.orderBy = orderBy.order(orderBy.column)
|
||||
|
||||
if (where) {
|
||||
findManyArgs.where = where
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DBQueryConfig } from 'drizzle-orm'
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type { Field, JoinQuery } from 'payload'
|
||||
|
||||
@@ -25,7 +26,6 @@ type TraverseFieldArgs = {
|
||||
tablePath: string
|
||||
topLevelArgs: Record<string, unknown>
|
||||
topLevelTableName: string
|
||||
versions?: boolean
|
||||
}
|
||||
|
||||
export const traverseFields = ({
|
||||
@@ -42,7 +42,6 @@ export const traverseFields = ({
|
||||
tablePath,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
versions,
|
||||
}: TraverseFieldArgs) => {
|
||||
fields.forEach((field) => {
|
||||
if (fieldIsVirtual(field)) {
|
||||
@@ -100,7 +99,6 @@ export const traverseFields = ({
|
||||
tablePath: tabTablePath,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
versions,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -225,7 +223,6 @@ export const traverseFields = ({
|
||||
tablePath: `${tablePath}${toSnakeCase(field.name)}_`,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -236,158 +233,87 @@ export const traverseFields = ({
|
||||
if (joinQuery === false) {
|
||||
break
|
||||
}
|
||||
|
||||
const {
|
||||
limit: limitArg = field.defaultLimit ?? 10,
|
||||
sort = field.defaultSort,
|
||||
limit: limitArg = 10,
|
||||
sort,
|
||||
where,
|
||||
} = joinQuery[`${path.replaceAll('_', '.')}${field.name}`] || {}
|
||||
let limit = limitArg
|
||||
|
||||
if (limit !== 0) {
|
||||
// get an additional document and slice it later to determine if there is a next page
|
||||
limit += 1
|
||||
}
|
||||
|
||||
const fields = adapter.payload.collections[field.collection].config.fields
|
||||
|
||||
const joinCollectionTableName = adapter.tableNameMap.get(toSnakeCase(field.collection))
|
||||
let joinTableName = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${
|
||||
field.localized && adapter.payload.config.localization ? adapter.localesSuffix : ''
|
||||
}`
|
||||
|
||||
const joins: BuildQueryJoinAliases = []
|
||||
|
||||
const buildQueryResult = buildQuery({
|
||||
adapter,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
sort,
|
||||
tableName: joinCollectionTableName,
|
||||
where,
|
||||
})
|
||||
|
||||
let subQueryWhere = buildQueryResult.where
|
||||
const orderBy = buildQueryResult.orderBy
|
||||
|
||||
let joinLocalesCollectionTableName: string | undefined
|
||||
|
||||
const currentIDColumn = versions
|
||||
? adapter.tables[currentTableName].parent
|
||||
: adapter.tables[currentTableName].id
|
||||
|
||||
// Handle hasMany _rels table
|
||||
if (field.hasMany) {
|
||||
const joinRelsCollectionTableName = `${joinCollectionTableName}${adapter.relationshipsSuffix}`
|
||||
|
||||
const db = adapter.drizzle as LibSQLDatabase
|
||||
if (field.localized) {
|
||||
joinLocalesCollectionTableName = joinRelsCollectionTableName
|
||||
joinTableName = adapter.tableNameMap.get(toSnakeCase(field.collection))
|
||||
}
|
||||
const joinTable = `${joinTableName}${adapter.relationshipsSuffix}`
|
||||
|
||||
let columnReferenceToCurrentID: string
|
||||
|
||||
if (versions) {
|
||||
columnReferenceToCurrentID = `${topLevelTableName
|
||||
.replace('_', '')
|
||||
.replace(new RegExp(`${adapter.versionsSuffix}$`), '')}_id`
|
||||
} else {
|
||||
columnReferenceToCurrentID = `${topLevelTableName}_id`
|
||||
}
|
||||
|
||||
joins.push({
|
||||
type: 'innerJoin',
|
||||
condition: and(
|
||||
eq(
|
||||
adapter.tables[joinRelsCollectionTableName].parent,
|
||||
adapter.tables[joinCollectionTableName].id,
|
||||
),
|
||||
eq(
|
||||
sql.raw(`"${joinRelsCollectionTableName}"."${columnReferenceToCurrentID}"`),
|
||||
currentIDColumn,
|
||||
),
|
||||
eq(adapter.tables[joinRelsCollectionTableName].path, field.on),
|
||||
),
|
||||
table: adapter.tables[joinRelsCollectionTableName],
|
||||
})
|
||||
} else {
|
||||
// Handle localized without hasMany
|
||||
|
||||
const foreignColumn = field.on.replaceAll('.', '_')
|
||||
|
||||
if (field.localized) {
|
||||
joinLocalesCollectionTableName = `${joinCollectionTableName}${adapter.localesSuffix}`
|
||||
|
||||
joins.push({
|
||||
const joins: BuildQueryJoinAliases = [
|
||||
{
|
||||
type: 'innerJoin',
|
||||
condition: and(
|
||||
eq(adapter.tables[joinTable].parent, adapter.tables[joinTableName].id),
|
||||
eq(
|
||||
adapter.tables[joinLocalesCollectionTableName]._parentID,
|
||||
adapter.tables[joinCollectionTableName].id,
|
||||
),
|
||||
eq(
|
||||
adapter.tables[joinLocalesCollectionTableName][foreignColumn],
|
||||
currentIDColumn,
|
||||
sql.raw(`"${joinTable}"."${topLevelTableName}_id"`),
|
||||
adapter.tables[currentTableName].id,
|
||||
),
|
||||
eq(adapter.tables[joinTable].path, field.on),
|
||||
),
|
||||
table: adapter.tables[joinLocalesCollectionTableName],
|
||||
})
|
||||
// Handle without localized and without hasMany, just a condition append to where. With localized the inner join handles eq.
|
||||
} else {
|
||||
const constraint = eq(
|
||||
adapter.tables[joinCollectionTableName][foreignColumn],
|
||||
currentIDColumn,
|
||||
)
|
||||
table: adapter.tables[joinTable],
|
||||
},
|
||||
]
|
||||
|
||||
if (subQueryWhere) {
|
||||
subQueryWhere = and(subQueryWhere, constraint)
|
||||
} else {
|
||||
subQueryWhere = constraint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chainedMethods: ChainedMethods = []
|
||||
|
||||
joins.forEach(({ type, condition, table }) => {
|
||||
chainedMethods.push({
|
||||
args: [table, condition],
|
||||
method: type ?? 'leftJoin',
|
||||
const { orderBy, where: subQueryWhere } = buildQuery({
|
||||
adapter,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
sort,
|
||||
tableName: joinCollectionTableName,
|
||||
where: {},
|
||||
})
|
||||
})
|
||||
|
||||
if (limit !== 0) {
|
||||
chainedMethods.push({
|
||||
args: [limit],
|
||||
method: 'limit',
|
||||
})
|
||||
}
|
||||
const chainedMethods: ChainedMethods = []
|
||||
|
||||
const db = adapter.drizzle as LibSQLDatabase
|
||||
|
||||
const subQuery = chainMethods({
|
||||
methods: chainedMethods,
|
||||
query: db
|
||||
.select({
|
||||
id: adapter.tables[joinCollectionTableName].id,
|
||||
...(joinLocalesCollectionTableName && {
|
||||
locale:
|
||||
adapter.tables[joinLocalesCollectionTableName].locale ||
|
||||
adapter.tables[joinLocalesCollectionTableName]._locale,
|
||||
}),
|
||||
joins.forEach(({ type, condition, table }) => {
|
||||
chainedMethods.push({
|
||||
args: [table, condition],
|
||||
method: type ?? 'leftJoin',
|
||||
})
|
||||
.from(adapter.tables[joinCollectionTableName])
|
||||
.where(subQueryWhere)
|
||||
.orderBy(() => orderBy.map(({ column, order }) => order(column))),
|
||||
})
|
||||
})
|
||||
|
||||
const columnName = `${path.replaceAll('.', '_')}${field.name}`
|
||||
const subQuery = chainMethods({
|
||||
methods: chainedMethods,
|
||||
query: db
|
||||
.select({
|
||||
id: adapter.tables[joinTableName].id,
|
||||
...(field.localized && {
|
||||
locale: adapter.tables[joinTable].locale,
|
||||
}),
|
||||
})
|
||||
.from(adapter.tables[joinTableName])
|
||||
.where(subQueryWhere)
|
||||
.orderBy(orderBy.order(orderBy.column))
|
||||
.limit(limit),
|
||||
})
|
||||
|
||||
const jsonObjectSelect = field.localized
|
||||
? sql.raw(
|
||||
`'_parentID', "id", '_locale', "${adapter.tables[joinLocalesCollectionTableName].locale ? 'locale' : '_locale'}"`,
|
||||
)
|
||||
: sql.raw(`'id', "id"`)
|
||||
const columnName = `${path.replaceAll('.', '_')}${field.name}`
|
||||
|
||||
if (adapter.name === 'sqlite') {
|
||||
currentArgs.extras[columnName] = sql`
|
||||
const jsonObjectSelect = field.localized
|
||||
? sql.raw(`'_parentID', "id", '_locale', "locale"`)
|
||||
: sql.raw(`'id', "id"`)
|
||||
|
||||
if (adapter.name === 'sqlite') {
|
||||
currentArgs.extras[columnName] = sql`
|
||||
COALESCE((
|
||||
SELECT json_group_array(json_object(${jsonObjectSelect}))
|
||||
FROM (
|
||||
@@ -395,8 +321,8 @@ export const traverseFields = ({
|
||||
) AS ${sql.raw(`${columnName}_sub`)}
|
||||
), '[]')
|
||||
`.as(columnName)
|
||||
} else {
|
||||
currentArgs.extras[columnName] = sql`
|
||||
} else {
|
||||
currentArgs.extras[columnName] = sql`
|
||||
COALESCE((
|
||||
SELECT json_agg(json_build_object(${jsonObjectSelect}))
|
||||
FROM (
|
||||
@@ -404,8 +330,41 @@ export const traverseFields = ({
|
||||
) AS ${sql.raw(`${columnName}_sub`)}
|
||||
), '[]'::json)
|
||||
`.as(columnName)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
const selectFields = {}
|
||||
|
||||
const withJoin: DBQueryConfig<'many', true, any, any> = {
|
||||
columns: selectFields,
|
||||
}
|
||||
if (limit) {
|
||||
withJoin.limit = limit
|
||||
}
|
||||
|
||||
if (field.localized) {
|
||||
withJoin.columns._locale = true
|
||||
withJoin.columns._parentID = true
|
||||
} else {
|
||||
withJoin.columns.id = true
|
||||
withJoin.columns.parent = true
|
||||
}
|
||||
const { orderBy, where: joinWhere } = buildQuery({
|
||||
adapter,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
sort,
|
||||
tableName: joinTableName,
|
||||
where,
|
||||
})
|
||||
if (joinWhere) {
|
||||
withJoin.where = () => joinWhere
|
||||
}
|
||||
withJoin.orderBy = orderBy.order(orderBy.column)
|
||||
currentArgs.with[`${path.replaceAll('.', '_')}${field.name}`] = withJoin
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
const globalConfig: SanitizedGlobalConfig = this.payload.globals.config.find(
|
||||
({ slug }) => slug === global,
|
||||
)
|
||||
const sort = sortArg !== undefined && sortArg !== null ? sortArg : '-createdAt'
|
||||
const sort = typeof sortArg === 'string' ? sortArg : '-createdAt'
|
||||
|
||||
const tableName = this.tableNameMap.get(
|
||||
`_${toSnakeCase(globalConfig.slug)}${this.versionsSuffix}`,
|
||||
|
||||
@@ -22,7 +22,7 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
},
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const sort = sortArg !== undefined && sortArg !== null ? sortArg : collectionConfig.defaultSort
|
||||
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort
|
||||
|
||||
const tableName = this.tableNameMap.get(
|
||||
`_${toSnakeCase(collectionConfig.slug)}${this.versionsSuffix}`,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { count } from 'drizzle-orm'
|
||||
import { count, sql } from 'drizzle-orm'
|
||||
|
||||
import type { ChainedMethods, TransactionPg } from '../types.js'
|
||||
import type { BasePostgresAdapter, CountDistinct } from './types.js'
|
||||
@@ -22,7 +22,11 @@ export const countDistinct: CountDistinct = async function countDistinct(
|
||||
methods: chainedMethods,
|
||||
query: (db as TransactionPg)
|
||||
.select({
|
||||
count: count(),
|
||||
count:
|
||||
joins.length > 0
|
||||
? sql`count
|
||||
(DISTINCT ${this.tables[tableName].id})`.mapWith(Number)
|
||||
: count(),
|
||||
})
|
||||
.from(this.tables[tableName])
|
||||
.where(where),
|
||||
|
||||
@@ -27,9 +27,8 @@ type Args = {
|
||||
schemaName?: string
|
||||
}
|
||||
export const createDatabase = async function (this: BasePostgresAdapter, args: Args = {}) {
|
||||
// POSTGRES_URL - default Vercel env
|
||||
const connectionString =
|
||||
this.poolOptions?.connectionString ?? process.env.POSTGRES_URL ?? process.env.DATABASE_URL
|
||||
// DATABASE_URL - default Vercel env
|
||||
const connectionString = this.poolOptions?.connectionString ?? process.env.DATABASE_URL
|
||||
let managementClientConfig: ClientConfig = {}
|
||||
let dbName = args.name
|
||||
const schemaName = this.schemaName || 'public'
|
||||
|
||||
@@ -57,6 +57,7 @@ export const init: Init = async function init(this: BasePostgresAdapter) {
|
||||
disableNotNull: !!collection?.versions?.drafts,
|
||||
disableUnique: false,
|
||||
fields: collection.fields,
|
||||
joins: collection.joins,
|
||||
tableName,
|
||||
timestamps: collection.timestamps,
|
||||
versions: false,
|
||||
|
||||
@@ -50,6 +50,7 @@ type Args = {
|
||||
disableRelsTableUnique?: boolean
|
||||
disableUnique: boolean
|
||||
fields: Field[]
|
||||
joins?: SanitizedJoins
|
||||
rootRelationships?: Set<string>
|
||||
rootRelationsToBuild?: RelationMap
|
||||
rootTableIDColType?: string
|
||||
@@ -82,6 +83,7 @@ export const buildTable = ({
|
||||
disableRelsTableUnique = false,
|
||||
disableUnique = false,
|
||||
fields,
|
||||
joins,
|
||||
rootRelationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
@@ -131,6 +133,7 @@ export const buildTable = ({
|
||||
disableUnique,
|
||||
fields,
|
||||
indexes,
|
||||
joins,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName: tableName,
|
||||
|
||||
@@ -50,6 +50,7 @@ type Args = {
|
||||
fields: (Field | TabAsField)[]
|
||||
forceLocalized?: boolean
|
||||
indexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
joins?: SanitizedJoins
|
||||
localesColumns: Record<string, PgColumnBuilder>
|
||||
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
newTableName: string
|
||||
@@ -88,6 +89,7 @@ export const traverseFields = ({
|
||||
fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
@@ -670,6 +672,7 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
@@ -724,6 +727,7 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
forceLocalized: field.localized,
|
||||
indexes,
|
||||
joins,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName: `${parentTableName}_${columnName}`,
|
||||
@@ -779,6 +783,7 @@ export const traverseFields = ({
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
@@ -834,6 +839,7 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
joins,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
@@ -930,6 +936,30 @@ export const traverseFields = ({
|
||||
|
||||
break
|
||||
|
||||
case 'join': {
|
||||
// fieldName could be 'posts' or 'group_posts'
|
||||
// using `on` as the key for the relation
|
||||
const localized = adapter.payload.config.localization && field.localized
|
||||
const fieldSchemaPath = `${fieldPrefix || ''}${field.name}`
|
||||
let target: string
|
||||
const joinConfig = joins[field.collection].find(
|
||||
({ schemaPath }) => fieldSchemaPath === schemaPath,
|
||||
)
|
||||
if (joinConfig.targetField.hasMany) {
|
||||
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${adapter.relationshipsSuffix}`
|
||||
} else {
|
||||
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${localized ? adapter.localesSuffix : ''}`
|
||||
}
|
||||
relationsToBuild.set(fieldName, {
|
||||
type: 'many',
|
||||
// joins are not localized on the parent table
|
||||
localized: false,
|
||||
relationName: field.on.replaceAll('.', '_'),
|
||||
target,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import type {
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
import type { ClientConfig, QueryResult } from 'pg'
|
||||
import type { Client, ClientConfig, Pool, QueryResult } from 'pg'
|
||||
|
||||
import type { extendDrizzleTable, Operators } from '../index.js'
|
||||
import type { BuildQueryJoinAliases, DrizzleAdapter, TransactionPg } from '../types.js'
|
||||
@@ -134,7 +134,9 @@ export type BasePostgresAdapter = {
|
||||
defaultDrizzleSnapshot: DrizzleSnapshotJSON
|
||||
deleteWhere: DeleteWhere
|
||||
disableCreateDatabase: boolean
|
||||
drizzle: PostgresDB
|
||||
drizzle: {
|
||||
$client: Client | Pool
|
||||
} & PostgresDB
|
||||
dropDatabase: DropDatabase
|
||||
enums: Record<string, GenericEnum>
|
||||
execute: Execute<unknown>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||
|
||||
import type { GenericTable } from '../types.js'
|
||||
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
||||
|
||||
import { getNameFromDrizzleTable } from '../utilities/getNameFromDrizzleTable.js'
|
||||
|
||||
export const addJoinTable = ({
|
||||
type,
|
||||
condition,
|
||||
joins,
|
||||
table,
|
||||
}: {
|
||||
condition: SQL
|
||||
joins: BuildQueryJoinAliases
|
||||
table: GenericTable | PgTableWithColumns<any>
|
||||
type?: 'innerJoin' | 'leftJoin' | 'rightJoin'
|
||||
}) => {
|
||||
const name = getNameFromDrizzleTable(table)
|
||||
|
||||
if (!joins.some((eachJoin) => getNameFromDrizzleTable(eachJoin.table) === name)) {
|
||||
joins.push({ type, condition, table })
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Field, Sort } from 'payload'
|
||||
import type { Field } from 'payload'
|
||||
|
||||
import { asc, desc } from 'drizzle-orm'
|
||||
|
||||
@@ -13,7 +13,7 @@ type Args = {
|
||||
joins: BuildQueryJoinAliases
|
||||
locale?: string
|
||||
selectFields: Record<string, GenericColumn>
|
||||
sort?: Sort
|
||||
sort?: string
|
||||
tableName: string
|
||||
}
|
||||
|
||||
@@ -29,55 +29,55 @@ export const buildOrderBy = ({
|
||||
sort,
|
||||
tableName,
|
||||
}: Args): BuildQueryResult['orderBy'] => {
|
||||
const orderBy: BuildQueryResult['orderBy'] = []
|
||||
|
||||
if (!sort) {
|
||||
const createdAt = adapter.tables[tableName]?.createdAt
|
||||
if (createdAt) {
|
||||
sort = '-createdAt'
|
||||
} else {
|
||||
sort = '-id'
|
||||
}
|
||||
const orderBy: BuildQueryResult['orderBy'] = {
|
||||
column: null,
|
||||
order: null,
|
||||
}
|
||||
|
||||
if (typeof sort === 'string') {
|
||||
sort = [sort]
|
||||
}
|
||||
if (sort) {
|
||||
let sortPath
|
||||
|
||||
for (const sortItem of sort) {
|
||||
let sortProperty: string
|
||||
let sortDirection: 'asc' | 'desc'
|
||||
if (sortItem[0] === '-') {
|
||||
sortProperty = sortItem.substring(1)
|
||||
sortDirection = 'desc'
|
||||
if (sort[0] === '-') {
|
||||
sortPath = sort.substring(1)
|
||||
orderBy.order = desc
|
||||
} else {
|
||||
sortProperty = sortItem
|
||||
sortDirection = 'asc'
|
||||
sortPath = sort
|
||||
orderBy.order = asc
|
||||
}
|
||||
|
||||
try {
|
||||
const { columnName: sortTableColumnName, table: sortTable } = getTableColumnFromPath({
|
||||
adapter,
|
||||
collectionPath: sortProperty,
|
||||
collectionPath: sortPath,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments: sortProperty.replace(/__/g, '.').split('.'),
|
||||
pathSegments: sortPath.replace(/__/g, '.').split('.'),
|
||||
selectFields,
|
||||
tableName,
|
||||
value: sortProperty,
|
||||
useAlias: true,
|
||||
value: sortPath,
|
||||
})
|
||||
if (sortTable?.[sortTableColumnName]) {
|
||||
orderBy.push({
|
||||
column: sortTable[sortTableColumnName],
|
||||
order: sortDirection === 'asc' ? asc : desc,
|
||||
})
|
||||
|
||||
selectFields[sortTableColumnName] = sortTable[sortTableColumnName]
|
||||
}
|
||||
orderBy.column = sortTable?.[sortTableColumnName]
|
||||
} catch (err) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
|
||||
if (!orderBy?.column) {
|
||||
orderBy.order = desc
|
||||
const createdAt = adapter.tables[tableName]?.createdAt
|
||||
|
||||
if (createdAt) {
|
||||
orderBy.column = createdAt
|
||||
} else {
|
||||
orderBy.column = adapter.tables[tableName].id
|
||||
}
|
||||
}
|
||||
|
||||
if (orderBy.column) {
|
||||
selectFields.sort = orderBy.column
|
||||
}
|
||||
|
||||
return orderBy
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { asc, desc, SQL } from 'drizzle-orm'
|
||||
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||
import type { Field, Sort, Where } from 'payload'
|
||||
import type { Field, Where } from 'payload'
|
||||
|
||||
import type { DrizzleAdapter, GenericColumn, GenericTable } from '../types.js'
|
||||
|
||||
@@ -18,7 +18,7 @@ type BuildQueryArgs = {
|
||||
fields: Field[]
|
||||
joins?: BuildQueryJoinAliases
|
||||
locale?: string
|
||||
sort?: Sort
|
||||
sort?: string
|
||||
tableName: string
|
||||
where: Where
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export type BuildQueryResult = {
|
||||
orderBy: {
|
||||
column: GenericColumn
|
||||
order: typeof asc | typeof desc
|
||||
}[]
|
||||
}
|
||||
selectFields: Record<string, GenericColumn>
|
||||
where: SQL
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import type { DrizzleAdapter, GenericColumn } from '../types.js'
|
||||
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
||||
|
||||
import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js'
|
||||
import { addJoinTable } from './addJoinTable.js'
|
||||
import { getTableAlias } from './getTableAlias.js'
|
||||
|
||||
type Constraint = {
|
||||
@@ -54,6 +53,7 @@ type Args = {
|
||||
* If creating a new table name for arrays and blocks, this suffix should be appended to the table name
|
||||
*/
|
||||
tableNameSuffix?: string
|
||||
useAlias?: boolean
|
||||
/**
|
||||
* The raw value of the query before sanitization
|
||||
*/
|
||||
@@ -79,6 +79,7 @@ export const getTableColumnFromPath = ({
|
||||
selectFields,
|
||||
tableName,
|
||||
tableNameSuffix = '',
|
||||
useAlias,
|
||||
value,
|
||||
}: Args): TableColumn => {
|
||||
const fieldPath = incomingSegments[0]
|
||||
@@ -140,6 +141,7 @@ export const getTableColumnFromPath = ({
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -160,6 +162,7 @@ export const getTableColumnFromPath = ({
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -178,6 +181,7 @@ export const getTableColumnFromPath = ({
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -186,17 +190,17 @@ export const getTableColumnFromPath = ({
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
newTableName = `${tableName}${adapter.localesSuffix}`
|
||||
|
||||
let condition = eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID)
|
||||
|
||||
if (locale !== 'all') {
|
||||
condition = and(condition, eq(adapter.tables[newTableName]._locale, locale))
|
||||
}
|
||||
|
||||
addJoinTable({
|
||||
condition,
|
||||
joins,
|
||||
joins.push({
|
||||
condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
if (locale !== 'all') {
|
||||
constraints.push({
|
||||
columnName: '_locale',
|
||||
table: adapter.tables[newTableName],
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
}
|
||||
return getTableColumnFromPath({
|
||||
adapter,
|
||||
@@ -213,6 +217,7 @@ export const getTableColumnFromPath = ({
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -224,24 +229,23 @@ export const getTableColumnFromPath = ({
|
||||
)
|
||||
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
const conditions = [
|
||||
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
|
||||
eq(adapter.tables[newTableName]._locale, locale),
|
||||
]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
||||
}
|
||||
|
||||
addJoinTable({
|
||||
condition: and(...conditions),
|
||||
joins,
|
||||
joins.push({
|
||||
condition: and(
|
||||
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
|
||||
eq(adapter.tables[newTableName]._locale, locale),
|
||||
),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
if (locale !== 'all') {
|
||||
constraints.push({
|
||||
columnName: '_locale',
|
||||
table: adapter.tables[newTableName],
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
addJoinTable({
|
||||
joins.push({
|
||||
condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
|
||||
joins,
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
}
|
||||
@@ -272,20 +276,20 @@ export const getTableColumnFromPath = ({
|
||||
]
|
||||
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
const conditions = [...joinConstraints]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
||||
}
|
||||
addJoinTable({
|
||||
condition: and(...conditions),
|
||||
joins,
|
||||
joins.push({
|
||||
condition: and(...joinConstraints, eq(adapter.tables[newTableName]._locale, locale)),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
if (locale !== 'all') {
|
||||
constraints.push({
|
||||
columnName: 'locale',
|
||||
table: adapter.tables[newTableName],
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
addJoinTable({
|
||||
joins.push({
|
||||
condition: and(...joinConstraints),
|
||||
joins,
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
}
|
||||
@@ -309,20 +313,23 @@ export const getTableColumnFromPath = ({
|
||||
|
||||
constraintPath = `${constraintPath}${field.name}.%.`
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
const conditions = [eq(arrayParentTable.id, adapter.tables[newTableName]._parentID)]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
||||
}
|
||||
addJoinTable({
|
||||
condition: and(...conditions),
|
||||
joins,
|
||||
joins.push({
|
||||
condition: and(
|
||||
eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
|
||||
eq(adapter.tables[newTableName]._locale, locale),
|
||||
),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
if (locale !== 'all') {
|
||||
constraints.push({
|
||||
columnName: '_locale',
|
||||
table: adapter.tables[newTableName],
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
addJoinTable({
|
||||
joins.push({
|
||||
condition: eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
|
||||
joins,
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
}
|
||||
@@ -338,6 +345,7 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -396,6 +404,7 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields: blockSelectFields,
|
||||
tableName: newTableName,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -408,21 +417,23 @@ export const getTableColumnFromPath = ({
|
||||
constraints = constraints.concat(blockConstraints)
|
||||
selectFields = { ...selectFields, ...blockSelectFields }
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
const conditions = [
|
||||
eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
),
|
||||
]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
||||
}
|
||||
|
||||
joins.push({
|
||||
condition: and(...conditions),
|
||||
condition: and(
|
||||
eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
),
|
||||
eq(adapter.tables[newTableName]._locale, locale),
|
||||
),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
if (locale) {
|
||||
constraints.push({
|
||||
columnName: '_locale',
|
||||
table: adapter.tables[newTableName],
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
joins.push({
|
||||
condition: eq(
|
||||
@@ -460,18 +471,21 @@ export const getTableColumnFromPath = ({
|
||||
|
||||
// Join in the relationships table
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
const conditions = [
|
||||
eq((aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent),
|
||||
like(aliasRelationshipTable.path, `${constraintPath}${field.name}`),
|
||||
]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(aliasRelationshipTable.locale, locale))
|
||||
}
|
||||
joins.push({
|
||||
condition: and(...conditions),
|
||||
condition: and(
|
||||
eq((aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent),
|
||||
eq(aliasRelationshipTable.locale, locale),
|
||||
like(aliasRelationshipTable.path, `${constraintPath}${field.name}`),
|
||||
),
|
||||
table: aliasRelationshipTable,
|
||||
})
|
||||
if (locale !== 'all') {
|
||||
constraints.push({
|
||||
columnName: 'locale',
|
||||
table: aliasRelationshipTable,
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Join in the relationships table
|
||||
joins.push({
|
||||
@@ -627,6 +641,7 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName: newTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
} else if (
|
||||
@@ -646,22 +661,15 @@ export const getTableColumnFromPath = ({
|
||||
tableName: `${rootTableName}${adapter.localesSuffix}`,
|
||||
})
|
||||
|
||||
const condtions = [eq(aliasLocaleTable._parentID, adapter.tables[rootTableName].id)]
|
||||
|
||||
if (locale !== 'all') {
|
||||
condtions.push(eq(aliasLocaleTable._locale, locale))
|
||||
}
|
||||
|
||||
const localesTable = adapter.tables[`${rootTableName}${adapter.localesSuffix}`]
|
||||
|
||||
addJoinTable({
|
||||
condition: and(...condtions),
|
||||
joins,
|
||||
table: localesTable,
|
||||
})
|
||||
|
||||
joins.push({
|
||||
condition: eq(localesTable[columnName], newAliasTable.id),
|
||||
condition: and(
|
||||
eq(aliasLocaleTable._parentID, adapter.tables[rootTableName].id),
|
||||
eq(aliasLocaleTable._locale, locale),
|
||||
),
|
||||
table: aliasLocaleTable,
|
||||
})
|
||||
joins.push({
|
||||
condition: eq(aliasLocaleTable[columnName], newAliasTable.id),
|
||||
table: newAliasTable,
|
||||
})
|
||||
} else {
|
||||
@@ -686,6 +694,7 @@ export const getTableColumnFromPath = ({
|
||||
pathSegments: pathSegments.slice(1),
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
useAlias,
|
||||
value,
|
||||
})
|
||||
}
|
||||
@@ -707,21 +716,24 @@ export const getTableColumnFromPath = ({
|
||||
const parentTable = aliasTable || adapter.tables[tableName]
|
||||
newTableName = `${tableName}${adapter.localesSuffix}`
|
||||
|
||||
newTable = adapter.tables[newTableName]
|
||||
newTable = useAlias
|
||||
? getTableAlias({ adapter, tableName: newTableName }).newAliasTable
|
||||
: adapter.tables[newTableName]
|
||||
|
||||
let condition = eq(parentTable.id, newTable._parentID)
|
||||
|
||||
if (locale !== 'all') {
|
||||
condition = and(condition, eq(newTable._locale, locale))
|
||||
}
|
||||
|
||||
addJoinTable({
|
||||
condition,
|
||||
joins,
|
||||
joins.push({
|
||||
condition: eq(parentTable.id, newTable._parentID),
|
||||
table: newTable,
|
||||
})
|
||||
|
||||
aliasTable = undefined
|
||||
|
||||
if (locale !== 'all') {
|
||||
constraints.push({
|
||||
columnName: '_locale',
|
||||
table: newTable,
|
||||
value: locale,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const targetTable = aliasTable || newTable
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { JoinQuery, PayloadRequest, QueryDrafts, SanitizedCollectionConfig } from 'payload'
|
||||
import type { PayloadRequest, QueryDrafts, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { buildVersionCollectionFields, combineQueries } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
@@ -9,17 +9,7 @@ import { findMany } from './find/findMany.js'
|
||||
|
||||
export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
this: DrizzleAdapter,
|
||||
{
|
||||
collection,
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
page = 1,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
sort,
|
||||
where,
|
||||
},
|
||||
{ collection, limit, locale, page = 1, pagination, req = {} as PayloadRequest, sort, where },
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const tableName = this.tableNameMap.get(
|
||||
@@ -32,7 +22,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
const result = await findMany({
|
||||
adapter: this,
|
||||
fields,
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
@@ -40,7 +29,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
req,
|
||||
sort,
|
||||
tableName,
|
||||
versions: true,
|
||||
where: combinedWhere,
|
||||
})
|
||||
|
||||
|
||||
@@ -420,8 +420,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
}
|
||||
|
||||
if (field.type === 'join') {
|
||||
const { limit = field.defaultLimit ?? 10 } =
|
||||
joinQuery?.[`${fieldPrefix.replaceAll('_', '.')}${field.name}`] || {}
|
||||
const { limit = 10 } = joinQuery?.[`${fieldPrefix.replaceAll('_', '.')}${field.name}`] || {}
|
||||
|
||||
// raw hasMany results from SQLite
|
||||
if (typeof fieldData === 'string') {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { Table } from 'drizzle-orm'
|
||||
|
||||
export const getNameFromDrizzleTable = (table: Table): string => {
|
||||
const symbol = Object.getOwnPropertySymbols(table).find((symb) =>
|
||||
symb.description.includes('Name'),
|
||||
)
|
||||
|
||||
return table[symbol]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.120",
|
||||
"version": "3.0.0-beta.116",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"eslint-plugin-react-hooks": "5.1.0-rc-a19a8ab4-20240829",
|
||||
"eslint-plugin-regexp": "2.6.0",
|
||||
"globals": "15.9.0",
|
||||
"typescript": "5.6.3",
|
||||
"typescript": "5.6.2",
|
||||
"typescript-eslint": "8.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user