# Liquid Field Access in Modyo Channels

## Overview

When accessing content fields in Liquid templates (widgets, pages, snippets, layouts), field names **must match exactly** as they are defined in the Content Type schema.

## Critical Rule: Exact Field Name Matching

**Field names in Liquid must preserve:**
- ✅ **Spaces**: Characters remain as spaces
- ✅ **Accents**: á, é, í, ó, ú, ñ, etc.
- ✅ **Capitalization**: Title Case, UPPERCASE, lowercase
- ✅ **Special Characters**: All characters exactly as defined

### Why This Matters

Unlike many CMS platforms that normalize field names (converting to snake_case, removing accents, etc.), Modyo preserves the exact field names as entered in the Content Type definition. This allows for:
- Better internationalization support
- More descriptive field names with natural language
- Consistency between UI and API

However, it requires **exact matching** when accessing fields in Liquid.

## Common Mistakes

### ❌ Wrong Approaches

```liquid
<!-- ❌ WRONG: Using snake_case when field has spaces -->
{{ product.fields['icono_svg'] }}

<!-- ❌ WRONG: Missing accent -->
{{ product.fields['titulo'] }}

<!-- ❌ WRONG: Wrong capitalization -->
{{ product.fields['url'] }}

<!-- ❌ WRONG: Using dot notation (doesn't work for fields with spaces/accents) -->
{{ product.fields.Título }}

<!-- ❌ WRONG: Without quotes -->
{{ product.fields[titulo] }}

<!-- ❌ WRONG: Hyphen instead of space -->
{{ product.fields['icono-svg'] }}
```

### ✅ Correct Approach

```liquid
<!-- ✅ CORRECT: Exact match with Content Type -->
{{ product.fields['Icono SVG'] }}     <!-- Space preserved -->
{{ product.fields['Título'] }}        <!-- Accent preserved -->
{{ product.fields['URL'] }}           <!-- Capitalization preserved -->
{{ product.fields['Descripción'] }}   <!-- Accent and capital preserved -->
```

## Workflow: Getting Exact Field Names

### Step 1: Get Content Type Schema

Use the `type-get` tool to retrieve the exact field definitions:

```typescript
const type = await type-get({
  platformSlug: "your-platform",
  spaceId: 2516,
  typeId: 5759
});
```

### Step 2: Check Field Names in Response

Look at the `fields` array in the response:

```json
{
  "id": 5759,
  "name": "Product",
  "fields": [
    {
      "id": 31354,
      "name": "Título",           // ← Use this EXACT name
      "type": "string"
    },
    {
      "id": 31355,
      "name": "Descripción",      // ← Use this EXACT name
      "type": "text"
    },
    {
      "id": 31356,
      "name": "Icono SVG",        // ← Use this EXACT name (with space)
      "type": "text"
    },
    {
      "id": 31357,
      "name": "URL",              // ← Use this EXACT name (uppercase)
      "type": "string"
    }
  ]
}
```

### Step 3: Copy Exact Names to Liquid

**DO NOT type manually** - copy the exact field names from the schema:

```liquid
{%- assign products = spaces['my-space'].types['product'].entries -%}
{%- for product in products -%}
  <h3>{{ product.fields['Título'] }}</h3>
  <p>{{ product.fields['Descripción'] }}</p>
  <div>{{ product.fields['Icono SVG'] }}</div>
  <a href="{{ product.fields['URL'] }}">Link</a>
{%- endfor -%}
```

## Examples by Language

### Spanish Fields

```liquid
<!-- Content Type fields defined as: -->
<!-- "Título", "Descripción", "Año de Publicación" -->

{%- for book in spaces['biblioteca'].types['book'].entries -%}
  <h2>{{ book.fields['Título'] }}</h2>
  <p>{{ book.fields['Descripción'] }}</p>
  <span>{{ book.fields['Año de Publicación'] }}</span>
{%- endfor -%}
```

### English Fields with Spaces

```liquid
<!-- Content Type fields defined as: -->
<!-- "Product Name", "Short Description", "Main Image" -->

{%- for item in spaces['catalog'].types['product'].entries -%}
  <h2>{{ item.fields['Product Name'] }}</h2>
  <p>{{ item.fields['Short Description'] }}</p>
  <img src="{{ item.fields['Main Image'].url }}">
{%- endfor -%}
```

### Mixed Case Fields

```liquid
<!-- Content Type fields defined as: -->
<!-- "SKU", "URL", "API Key" -->

{%- for config in spaces['settings'].types['config'].entries -%}
  SKU: {{ config.fields['SKU'] }}
  URL: {{ config.fields['URL'] }}
  API Key: {{ config.fields['API Key'] }}
{%- endfor -%}
```

## Testing Field Access

### 1. Test in Simple Template First

Before using in complex widgets, test field access in a simple snippet:

```liquid
{%- assign test_entry = spaces['my-space'].types['my-type'].entries | first -%}

<h3>Testing Field Access:</h3>
<ul>
  <li>Title: {{ test_entry.fields['Título'] }}</li>
  <li>Description: {{ test_entry.fields['Descripción'] }}</li>
</ul>
```

### 2. Check for Empty Output

If a field returns empty:
1. Verify field name matches exactly (case, spaces, accents)
2. Check field exists in Content Type (`type-get`)
3. Verify entry has value for that field (`entry-get`)
4. Confirm entry is accessible (published or draft depending on context)

### 3. Debug with Field List

List all available fields to verify exact names:

```liquid
{%- assign entry = spaces['my-space'].types['my-type'].entries | first -%}

<h3>Available Fields:</h3>
{% for field in entry.fields %}
  <li>{{ field[0] }}: {{ field[1] }}</li>
{% endfor %}
```

## Best Practices

### 1. Always Check Schema First

```typescript
// ALWAYS start by checking the Content Type schema
const type = await type-get({ platformSlug, spaceId, typeId });

// Review field names before writing Liquid
type.fields.forEach(field => {
  console.log(`Field: "${field.name}" (ID: ${field.id})`);
});
```

### 2. Copy, Don't Type

**Never manually type field names** - always copy from schema to avoid typos:
```liquid
<!-- ❌ BAD: Typed manually (risk of typos) -->
{{ product.fields['Descripcion'] }}  <!-- Missing accent -->

<!-- ✅ GOOD: Copied from schema -->
{{ product.fields['Descripción'] }}  <!-- Exact match -->
```

### 3. Document Field Names

In widget code, document the required field names:

```liquid
<!--
  Required Content Type Fields (exact names):
  - "Título" (string)
  - "Descripción" (text)
  - "Icono SVG" (text)
  - "URL" (string)
-->

{%- for product in products -%}
  {{ product.fields['Título'] }}
{%- endfor -%}
```

### 4. Use Consistent Naming in Content Types

When creating Content Types, consider:
- Using consistent naming patterns
- Avoiding overly complex names
- Documenting field names in type description

## Troubleshooting

### Field Returns Empty/Null

**Problem**: Field access returns nothing

**Checklist**:
1. ✅ Field name matches exactly (spaces, accents, case)?
2. ✅ Field exists in Content Type schema?
3. ✅ Entry has a value for this field?
4. ✅ Entry is accessible (published/draft)?
5. ✅ Using bracket notation with quotes: `['Field Name']`?

**Solution**:
```typescript
// 1. Check Content Type schema
const type = await type-get({ platformSlug, spaceId, typeId });
console.log('Available fields:', type.fields.map(f => f.name));

// 2. Check specific entry
const entry = await entry-get({ platformSlug, spaceId, entryId });
console.log('Entry field values:', entry.field_values);

// 3. Use exact field name in Liquid
{{ entry.fields['Exact Field Name'] }}
```

### Field Name Has Special Characters

**Problem**: Field has parentheses, slashes, or other special characters

**Solution**: Use bracket notation with quotes (same as spaces):
```liquid
{{ entry.fields['Price (USD)'] }}
{{ entry.fields['Start/End Date'] }}
{{ entry.fields['Contact Email'] }}
```

### Multiple Entries with Same Pattern

**Problem**: Need to access same fields across many entries

**Solution**: Create a reusable snippet:
```liquid
<!-- _includes/product-card.liquid -->
<div class="product-card">
  <h3>{{ product.fields['Título'] }}</h3>
  <p>{{ product.fields['Descripción'] }}</p>
  <a href="{{ product.fields['URL'] }}">Ver más</a>
</div>

<!-- In main template -->
{%- for product in products -%}
  {% snippet 'product-card', product: product %}
{%- endfor -%}
```

## Content API vs Liquid

### Important Difference

**Content API** (JavaScript fetch):
- Field values accessed via `field_id` (numeric ID)
- Example: `entry.field_values.find(f => f.field_id === 31354).value`

**Liquid Templates**:
- Field values accessed via exact `field name` (string)
- Example: `entry.fields['Título']`

### When to Use Each

**Use Content API** when:
- Building external applications
- Need published content only
- Client-side dynamic loading

**Use Liquid** when:
- Building widgets/pages in Modyo
- Need access to draft content
- Server-side rendering
- Better performance (no extra HTTP request)

## Related Documentation

- [Modyo Snippets Architecture](/docs/MODYO_SNIPPETS_ARCHITECTURE.md)
- [Editing Published Widgets](/docs/EDITING_PUBLISHED_WIDGETS.md)
- [Modyo Liquid Markup](https://docs.modyo.com/en/platform/channels/liquid-markup.html)
- [Content Type Schema Tool](/docs/tools/TYPE_TOOLS.md)

---

**Document Version**: 1.0.0
**Last Updated**: 2025-01-09
**Purpose**: Guide for accessing content fields in Liquid templates with exact name matching
