Understanding Relationships in Payload CMS

11 September 2024

Understanding Relationships in Payload CMS

Relationships in Payload CMS refer to the connections established between different documents within the CMS. These relationships allow users to link various content types, thereby creating a more interconnected and dynamic content structure. Payload CMS supports multiple types of relationships, including:

  • One-to-One: A single document is linked to another single document.

  • One-to-Many: A single document is linked to multiple documents.

  • Many-to-Many: Multiple documents are linked to multiple other documents.

  • Polymorphic Relationships: Relationships that can refer to documents from different collections.

By leveraging these relationship types, Payload CMS enables users to build complex content architectures that cater to a wide range of applications, from e-commerce sites to blogs with related posts.

Use Cases for Relationships

Relationships in Payload CMS are highly versatile and can be utilized in various scenarios to enhance data management and user experience. Some practical use cases include:

  • E-commerce: Linking products to categories, manufacturers, and related products.

  • Blogging: Connecting related blog posts to increase reader engagement and time on site.

  • Digital Asset Management: Associating media files with specific projects or clients.

  • Enterprise Applications: Managing hierarchical structures such as departments and employees.

By using relationships, developers can ensure data integrity, reduce redundancy, and create cohesive content structures that are easy to manage and query.

Setting Up Relationships in Payload CMS

Payload CMS offers a comprehensive and flexible system for setting up relationships between documents. The following sections provide detailed instructions on how to configure and use relationship fields in Payload CMS.

Basic Configuration

To define a relationship field in Payload CMS, you need to specify several key configuration properties in your collection's configuration. Here are the important properties:

Config Option

Description

name

Property name when stored and retrieved from the database.

relationTo

Specifies one or many collections to assign relationships to.

hasMany

Boolean indicating if the field can have multiple relations.

minRows

Minimum allowed items during validation when a value is present. Used in conjunction with hasMany.

maxRows

Maximum allowed items during validation when a value is present. Used in conjunction with hasMany.

maxDepth

Limits depth of related documents when queried.

label

Text for the field label in the Admin panel or an object with keys for each language.

unique

Enforces unique values for each entry in the collection.

validate

Custom validation function executed on both the Admin panel and backend.

index

Create an index for faster queries.

saveToJWT

Include field data in the user JWT if nested in a config supporting Authentication.

hooks

Field-based hooks for custom logic.

access

Field-based access control to manage visibility and permissions.

hidden

Restrict field visibility from all APIs.

defaultValue

Data used as the field's default value.

localized

Enable localization for the field. Requires localization to be enabled in the Base config.

required

Make the field mandatory.

admin

Admin-specific configuration.

Admin Config

The Relationship field type includes additional properties for configuring the Admin UI:

Property

Description

isSortable

Set to true to make the field sortable within the Admin UI using drag and drop (only works when hasMany is true).

allowCreate

Set to false to disable the ability to create new documents from within the relationship field (hides the "Add new" button).

sortOptions

Define a default sorting order for the options within a Relationship field's dropdown.

Example: sortOptions
sortOptions: 'fieldName'  // Global default sort field in ascending order
sortOptions: {
  "pages": "fieldName1",
  "posts": "-fieldName2",
  "categories": "fieldName3"
}

Filtering Relationship Options

Options for relationships can be dynamically limited through the filterOptions property, which can be a query or a function returning a query:

Example Code
import { CollectionConfig } from 'payload/types';

export const ExampleCollection: CollectionConfig = {
  slug: 'example-collection',
  fields: [
    {
      name: 'purchase',
      type: 'relationship',
      relationTo: ['products', 'services'],
      filterOptions: ({ relationTo, siblingData }) => {
        if (relationTo === 'products') {
          return { stock: { greater_than: siblingData.quantity } };
        }
        if (relationTo === 'services') {
          return { isAvailable: { equals: true } };
        }
        return {};  // Default filter
      },
    },
  ],
};

How Data is Saved

Depending on the configuration, the shape of the data can vary. Below are examples for different relationship types.

Has One
{
  slug: 'example-collection',
  fields: [
    {
      name: 'owner',
      type: 'relationship',
      relationTo: 'users',
      hasMany: false,
    }
  ]
}

Data Shape:

{
  "owner": "6031ac9e1289176380734024"
}
Has One - Polymorphic
{
  slug: 'example-collection',
  fields: [
    {
      name: 'owner',
      type: 'relationship',
      relationTo: ['users', 'organizations'],
      hasMany: false,
    }
  ]
}

Data Shape:

{
  "owner": {
    "relationTo": "organizations",
    "value": "6031ac9e1289176380734024"
  }
}
Has Many
{
  slug: 'example-collection',
  fields: [
    {
      name: 'owners',
      type: 'relationship',
      relationTo: 'users',
      hasMany: true,
    }
  ]
}

Data Shape:

{
  "owners": ["6031ac9e1289176380734024", "602c3c327b811235943ee12b"]
}
Has Many - Polymorphic
{
  slug: 'example-collection',
  fields: [
    {
      name: 'owners',
      type: 'relationship',
      relationTo: ['users', 'organizations'],
      hasMany: true,
      required: true,
    }
  ]
}

Data Shape:

{
  "owners": [
    { "relationTo": "users", "value": "6031ac9e1289176380734024" },
    { "relationTo": "organizations", "value": "602c3c327b811235943ee12b" }
  ]
}

Querying and Filtering Polymorphic Relationships

Querying polymorphic relationships can vary based on how the data is stored. For example:

  • Query by Document ID:

    ?where[field.value][equals]=6031ac9e1289176380734024
  • Query by Collection Slug:

    ?where[field.relationTo][equals]=your-collection-slug

Practical Examples and Tutorials

Real-world Use Case Scenarios

Let's explore a few practical examples to understand the application of relationships in Payload CMS.

Example Tutorial from "Learn with Jason"

In a detailed session on Learn with Jason, James Mikrut, the CEO of Payload CMS, demonstrated how to set up a scalable design system for enterprise websites using Payload CMS and Next.js. This tutorial provides valuable insights into the process of setting up and managing relationships within Payload CMS.

Setting Up the Repository

Repositories:

Steps Overview:

  1. Install Dependencies:

    yarn install --legacy-peer-deps

    Set up environment variables using .env.example for MongoDB connection and Payload secret.

  2. Server Configuration: Utilize server.ts to initialize an Express server and seed initial data.

Payload Configuration

Config File:

  • File: payload.config.ts

  • Collections and Globals:

    • Example includes media, page, and users collections.

    • Globals for singleton structures like main menu and footer.

Pages Collection Example:

  • File: collections/pages.ts

  • Toggle features such as drafts and versions.

  • Access control settings for read permissions based on user login status.

  • Defining fields, including dynamic conditions to render fields based on specific criteria.

Integrating with Next.js

Next.js Structure:

  • Utilizes getStaticProps to fetch data using Apollo Client.

  • Example of caching global data to optimize build performance.

Components:

  • Hero Component: Choosing and rendering hero types based on selection.

  • Blocks Component: Dynamically rendering content blocks.

Hero Component Example:

import { HeroHighImpact } from './HeroHighImpact';
import { HeroMediumImpact } from './HeroMediumImpact';
import { HeroLowImpact } from './HeroLowImpact';

const components = {
  highImpact: HeroHighImpact,
  mediumImpact: HeroMediumImpact,
  lowImpact: HeroLowImpact
};

export const Hero = ({ type, ...props }) => {
  const HeroComponent = components[type];
  return HeroComponent ? <HeroComponent {...props} /> : null;
};

Integrating Relationships with Other Systems

Payload CMS also supports seamless integrations with other systems, such as Supabase, for enhanced functionality.

Example of Payload CMS and Supabase Integration

Setting Up Payload CMS with Supabase:

  1. Create a Supabase Project and Database: Configure environment variables for the retrieval plugin.

    export OPENAI_API_KEY=<open_ai_api_key>
    export DATASTORE=supabase
    export SUPABASE_URL=<supabase_url>
    export SUPABASE_SERVICE_ROLE_KEY=<supabase_key>
  2. Replace with Postgres-specific variables if needed.

    export DATASTORE=postgres
    export PG_HOST=<postgres_host_url>
    export PG_PASSWORD=<postgres_password>
  3. Local Instance for Development:

    supabase start
  4. Apply necessary migrations: Examples in examples/providers/supabase/migrations/.

Authentication and Permissions: Supabase provides robust authentication and permission layers.

Authentication Flow:

// Sign in with email and password
await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'securepassword',
});

// Sign out
await supabase.auth.signOut();

Permissions and Row Level Security (RLS):

-- Enable RLS for a table
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;

-- Create a policy that allows users to access their own records
CREATE POLICY user_records ON your_table FOR SELECT USING (auth.uid() = user_id);

Data Modeling and Relations:

  • Design database schema visually using Entity Relationship Diagrams (ERDs).

  • Utilize foreign keys for one-to-one, one-to-many, and many-to-many relationships.

  • Improve performance by creating indexes on frequently queried columns.

Developer Insights

Developers appreciate Payload CMS for its modern UI, extensive customization options, and flexibility in handling relationships. However, there are challenges as well, such as complex initial setup and database migrations. Here are some practical tips and insights:

  1. Use TypeScript: Strongly type Payload configurations and Next.js components to prevent errors.

  2. Modular Structure: Break down configurations and components into manageable and reusable files.

  3. Focus on Accessibility: Ensure all UI components and CMS inputs are accessible.

  4. Phased Refactoring: Avoid premature refactoring; focus on functionality first, then optimize.

Alternatives and Transitions

For those looking for an alternative solution to managing related blog posts, Wisp offers an AI-powered feature that automatically suggests related blog posts, enhancing user engagement and decreasing bounce rates.

Overview of Wisp's Related Blog Posts Feature
  • AI-Powered Content Suggestions: Wisp uses advanced AI to automatically suggest related blog posts, saving time and effort.

  • Boost Engagement: Keeps readers immersed in the content ecosystem, increasing time spent on the website and guiding them towards conversions.

  • Showcase Expertise: Demonstrates the depth of knowledge through related posts, showcasing the expertise of content creators.

  • Enhance User Experience: Provides a seamless journey for readers through intelligent article suggestions.

  • Save Time: Allows content creators to focus on producing high-quality content as the AI handles the linking of related posts.

Conclusion

Understanding and utilizing relationships in Payload CMS can significantly enhance your content management capabilities. With extensive configuration options and support for complex relationships, Payload CMS is a powerful tool for developers building sophisticated applications. For those looking to simplify the creation of related blog posts, Wisp offers an excellent alternative, leveraging AI to provide intelligent content suggestions and enhancing user engagement.

If you're looking to explore a modern and flexible CMS, give Payload CMS a try. For those specifically interested in creating related blog posts, check out Wisp for a seamless and intelligent solution.

Choosing a CMS?

Wisp is the most delightful and intuitive way to manage content on your website. Integrate with any existing website within hours!

Choosing a CMS
Related Posts
How to Add Related Content for Payload CMS

How to Add Related Content for Payload CMS

Discover how Payload CMS's Relationship fields can streamline your content management and elevate user experience seamlessly.

Read Full Story
How to Add Related Content for Directus

How to Add Related Content for Directus

Unlock the full potential of Directus! Learn how to create and manage impactful content relationships with our step-by-step guide. Enhance your user experience and SEO effortlessly!

Read Full Story
How to Add Related Content for Strapi

How to Add Related Content for Strapi

Discover how to add related content to your Strapi project. From one-to-one to polymorphic relations, this guide has you covered with practical steps & advanced techniques.

Read Full Story