Skip to content

Repeater ​

File: src/Entities/Repeater.phpNamespace: Unusualify\Modularous\EntitiesExtends: Model

Eloquent model that persists the rows created by the HasRepeaters trait. Each row represents one item in a repeater field (e.g., one FAQ entry, one team member). Extends the Modularous Model base class and supports soft deletes.

Database Table ​

Configurable via modularousConfig('tables.repeaters', 'modularous_repeaters'). Defaults to modularous_repeaters.

Columns ​

ColumnTypeNullableDescription
idUUID / auto-incrementNoPrimary key (type follows modularousIncrementsMethod())
repeatable_typestring (UUID morphs)NoClass of the owning model
repeatable_idUUIDNoID of the owning model
contentJSONNoAll field values for this repeater item
rolestringYesThe input name in the module config — identifies which repeater field this row belongs to
localestring(6)NoLocale code (e.g. 'en', 'de') — indexed
created_at / updated_attimestamp—Standard timestamps
deleted_attimestampYesSoft-delete column

Fillable Attributes ​

AttributeTypeDescription
repeatable_idintParent model ID
repeatable_typestringParent model class
contentarrayRepeater data (JSON cast)
rolestringInput field role identifier
localestringLocale for translated repeaters

Relationships ​

php
// Owning model (any model using HasRepeaters)
public function repeatable(): MorphTo

No reverse eager-loading is defined on Repeater itself — access is always from the owning model via $model->repeaters($role, $locale).

getTable() ​

The table name is resolved at runtime:

php
public function getTable(): string
{
    return modularousConfig('tables.repeaters', parent::getTable());
}

How role and locale Work Together ​

A single model can have multiple repeater fields. role discriminates between them; locale discriminates between translations within the same field.

Page { id: 1 }
├── Repeater { repeatable_id: 1, role: 'faqs',        locale: 'en', content: { question: 'What?', answer: '...' } }
├── Repeater { repeatable_id: 1, role: 'faqs',        locale: 'en', content: { question: 'How?',  answer: '...' } }
├── Repeater { repeatable_id: 1, role: 'faqs',        locale: 'de', content: { question: 'Was?',  answer: '...' } }
└── Repeater { repeatable_id: 1, role: 'team_members', locale: 'en', content: { name: 'Alice', title: 'CTO' } }

Querying by role + locale ​

The HasRepeaters trait's repeaters() method applies these filters:

php
// All English FAQs on a page
$page->repeaters('faqs', 'en')->get();

// All repeaters regardless of role/locale
$page->repeaters()->get();

content JSON Schema ​

The content column stores all field values for a single repeater item as a flat or nested object, depending on the input schema defined in the module config:

json
{
    "question": "What is Modularous?",
    "answer": "A Laravel module framework.",
    "icon": null
}

For translated fields (translated: true in the schema), values are nested by locale:

json
{
    "question": {
        "en": "What is Modularous?",
        "de": "Was ist Modularous?"
    }
}

Difference from Block ​

AspectRepeaterBlock
NestingFlat rows only — no parent/childOne level of parent/child via parent_id
Type systemNo type — all rows under a role are the same shapeEach row has a type string mapping to a Blade view
RenderingVia PHP/Inertia (frontend handles layout)Via Blade views (renderBlocks())
MediaNot built-inHasFiles, HasImages on the Block model
LocaleStored as column (locale)Stored inside content JSON per field
Tablemodularous_repeaters (configurable)twill_blocks (configurable)