# Relations, Bulk Generation, and Reference Patterns

In [Declaring a Single Record with SBlueprint](/apex-stem/docs/apex-blueprint-sblueprint-guide), we covered the five core methods for declaring a single record on its own. This page goes one step further and covers patterns for **structurally assembling multiple records**.

Specifically, the following topics:

- `withChildren`: nesting children inside a parent blueprint
- `times(n)` + `{#}` placeholder: bulk generation with sequence numbers
- `use` with offsets: linking only some bulked parents to children
- `{P0}` / `{P1}`: referencing ancestors (immediate parent / grandparent) inside nesting
- `parentIdField`: disambiguating which lookup to fill when the child has multiple parent lookups
- `sharedWith`: declaring manual shares, and how they follow bulk generation

## withChildren: Nesting Children Under a Parent

`withChildren(child)` lets you write "the child records of this blueprint" inline inside a single blueprint. The indentation hierarchy of your code becomes the data hierarchy directly, making parent-child relationships readable at a glance.

### Simple 1:1 Parent-Child

```apex
SBlueprint.of(Account.class)
    .template(Blueprints.accBasic())
    .alias('parentAccount')
    .withChildren(
        SBlueprint.of(Contact.class)
            .set('LastName', 'TestContact')
            .alias('childContact')
    );
```

Points:

- Copying the parent's Id into the child's `AccountId` is **automatic**. There's no need to write `.use(...)`
- Defining the child blueprint **inline** inside `withChildren` visualizes the structure of "this parent has this child hanging under it"
- If you attach `.alias(...)` on the child side, you can retrieve it via `getByAlias('childContact')`

### 1:N Parent-Child (Combined with `times`)

Using `.times(n)` inside `withChildren` lets you hang multiple children under the same parent.

```apex
SBlueprint.of(Account.class)
    .alias('parentAccount')
    .template(Blueprints.accBasic())
    .withChildren(
        SBlueprint.of(Contact.class)
            .set('LastName', 'Contact-{#}')
            .alias('con_{#}')
            .times(3)
    );
```

Three `Contact`s — `con_1` / `con_2` / `con_3` — are generated, all linked to the same `Account`. Because `{#}` is used both in the alias and the `LastName`, you can later retrieve them individually via `getByAlias('con_2')`.

### Nesting Further to Grandchildren

`withChildren` is **nestable**. Three-tier structures like "Contact under Account, Case under Contact" feel natural to write.

```apex
SBlueprint.of(Account.class)
    .alias('acc')
    .template(Blueprints.accBasic())
    .withChildren(
        SBlueprint.of(Contact.class)
            .set('LastName', 'TestContact')
            .alias('con')
            .withChildren(
                SBlueprint.of(Case.class)
                    .set('Subject', 'TestCase')
                    .alias('case')
            )
    );
```

### Multiple Child Types Under the Same Parent

To hang **children of different SObject types** under the same parent, call `.withChildren(...)` repeatedly.

```apex
SBlueprint.of(Account.class)
    .template(Blueprints.accBasic())
    .alias('acc')
    .withChildren(
        SBlueprint.of(Contact.class)
            .set('LastName', 'TestContact')
            .alias('childContact')
    )
    .withChildren(
        SBlueprint.of(Opportunity.class)
            .set('Name', 'TestOpportunity')
            .set('StageName', 'Prospecting')
            .set('CloseDate', Date.today().addDays(30))
            .alias('childOpp')
    );
```

`Contact` and `Opportunity` are each generated under the same `Account` with the parent Id copied into their respective `AccountId`. You can also use `.times(...)` on the child side, so uneven structures like "3 Contacts and 2 Opportunities under one Account" express naturally.

## Multiplication: Upper-Tier `times` Propagates Downward

A crucial behavior to keep in mind when combining `withChildren` with `.times(n)`: **adding `.times(n)` at an upper tier re-generates the entire child subtree per parent**, so record counts multiply.

### Two Tiers: Parent N × Child M

```apex
SBlueprint.of(Account.class)
    .set('Name', 'Acc-{#}')
    .alias('acc_{#}')
    .times(2)                      // 2 parents
    .withChildren(
        SBlueprint.of(Contact.class)
            .parentIdField('AccountId')
            .set('LastName', 'Con-{#}')
            .alias('con_{#}')
            .times(2)              // 2 children per parent
    );
```

Result: 2 `Account`s and **2 × 2 = 4** `Contact`s.

### Three Tiers and Beyond: Exponential Growth

Adding more tiers compounds the count multiplicatively.

```apex
SBlueprint.of(Account.class)
    .times(2)                          // 2 parents
    .withChildren(
        SBlueprint.of(Contact.class)
            .parentIdField('AccountId')
            .times(2)                  // 2 children per parent → 4 total
            .withChildren(
                SBlueprint.of(Case.class)
                    .parentIdField('ContactId')
                    .times(2)          // 2 grandchildren per child → 8 total
            )
    );
```

Total record count: 2 `Account`s + 4 `Contact`s + **2 × 2 × 2 = 8** `Case`s. Remembering "the leaf count = the product of `.times(...)` along the hierarchy" makes it easy to anticipate counts.

> Be careful: deeper hierarchies make **record counts grow exponentially**. Stacking five tiers with `.times(3)` each generates `3⁵ = 243` records, eating into the DML row governor limit (10,000). "Just a few more" can balloon into a massive count, so be deliberate about each tier's `.times(...)`.

## times + {#}: Bulk Generation with Sequence Numbers

`.times(n)` produces `n` copies of the same blueprint. For cases like "3 Contacts" or "10 Accounts", you can do it in one line without a `for` loop.

The `{#}` placeholder is embedded inside `.set(...)` values or the argument to `.alias(...)`. When expanded by `times(n)`, `{#}` is replaced with `1`, `2`, `3`, ....

```apex
SBlueprint.of(Contact.class)
    .set('LastName', 'Contact-{#}')
    .alias('con_{#}')
    .times(3);
```

What gets generated:

| alias | LastName |
|---|---|
| `con_1` | `Contact-1` |
| `con_2` | `Contact-2` |
| `con_3` | `Contact-3` |

### Alphabetic Sequences: `{A}` / `{a}`

In addition to the numeric `{#}`, **alphabetic sequence** placeholders `{A}` / `{a}` are supported. `{A}` expands to `'A'` / `'B'` / `'C'` ..., and `{a}` expands to `'a'` / `'b'` / `'c'` ....

```apex
SBlueprint.of(Account.class)
    .set('Name', 'Acc-{A}')
    .alias('acc_{a}')
    .times(3);
```

What gets generated:

| alias | Name |
|---|---|
| `acc_a` | `Acc-A` |
| `acc_b` | `Acc-B` |
| `acc_c` | `Acc-C` |

Use these for test data where you want **a human-readable distinction** (e.g. labels appearing in test reports), where letters read more naturally than numbers.

### startAt / interval: Changing the Start and Step

`.set(...)` / `.alias(...)` have extra arguments to specify the **start** and **step** for `{#}`.

```apex
SBlueprint.of(Account.class)
    .set('Name', 'Acc-{#}', 10, 2)   // 10, 12, 14
    .alias('acc_{#}', 10, 2)         // acc_10, acc_12, acc_14
    .times(3);
```

This handles finer-grained needs like "start the sequence from N" or "only even-numbered indices".

## `use` with Offsets: Linking Only Some Bulked Parents to Children

Sometimes you want to link **only some** of the bulked parent records to children. For example, "out of 10 Accounts, only attach Contacts to the latter 5 (6 to 10)".

`use` has 4- and 5-argument overloads that let you specify the **start number** and **step** of the alias being referenced.

**Signature:** `.use(String alias, String fromField, String toField, Integer startAt [, Integer interval])`

```apex
SOrchestrator.start()
    .add(
        SBlueprint.of(Account.class)
            .alias('acc_{#}')
            .template(Blueprints.accBasic())
            .times(10)                              // acc_1 to acc_10
    )
    .add(
        SBlueprint.of(Contact.class)
            .set('LastName', 'Con-{#}')
            .alias('con_{#}', 6)                    // con_6 to con_10
            .use('acc_{#}', 'Id', 'AccountId', 6)   // reference acc_6 to acc_10
            .times(5)
    );
```

Five children — `con_6` through `con_10` — are created, each with `acc_6` through `acc_10` as their parent. This overload shines when you need to express **uneven mappings**, not "every child has the same parent".

## {P0} / {P1} / ...: Referencing Upper-Tier Values

### Why This Placeholder Is Needed

In nestings that use `.times(...)` at each tier, "the true parent" as seen from a leaf record is **a different instance each time it's generated**. For example:

- Parent (Account) `.times(2)`
- Child (Contact) `.times(2)`
- Grandchild (Case) `.times(2)`

In this case, there are 8 grandchildren, 4 children (2 per parent), and 2 parents. From each grandchild's perspective, "its true parent (Contact)" is one specific Contact out of the 4.

However, if you attach a simple `{#}` alias on the Contact side like `.alias('child_{#}')`, "the `child_1` under Acme-1" and "the `child_1` under Acme-2" end up **declaring the same alias twice, which raises a duplicate-alias error and halts execution**.

There is a workaround. By embedding the parent's alias to form a **compound alias** like `.alias('{P0}_child_{#}')`, you get four unique aliases: `__Account_0_1___child_1` / `__Account_0_1___child_2` / `__Account_0_2___child_1` / `__Account_0_2___child_2`, and grandchildren can address them via `.use('__Account_0_1___child_1', ...)`.

But this approach has drawbacks:

- The grandchild's `.use(...)` side has to **mentally assemble the alias string of "its true parent"**
- If you change the hierarchy later, the alias string assembly logic has to be rewritten everywhere
- As a result, the test code reads like an "alias-string puzzle" rather than a "data structure"

The `{P0}` / `{P1}` / `{P2}` ... placeholders avoid this structurally. SOrchestrator hierarchically resolves "the **true parent for me**, at the Nth tier from the top (0-indexed)" — so you don't have to attach aliases, and you don't have to mentally assemble alias strings.

### How to Count: Absolute Depth from the Root

The counting is **not "distance upward from me"** — it's **absolute depth from the root**:

- `{P0}`: the root (outermost parent, tier 0)
- `{P1}`: one level below the root (tier 1)
- `{P2}`: another level below (tier 2)
- And so on, increasing as you go deeper

So when you want to reference "tier 1's value from a fourth-tier record", you specify `{P1}` (not "three steps up from where I am").

### Minimal Example: Using `{P0}` in Two Tiers

Let's start with the simplest two-tier case, referencing `{P0}` (= the root). Here the child Contact's `Description` simply receives the parent Account's `Name`.

```apex
SBlueprint.of(Account.class)             // P0 (root)
    .set('Name', 'Acme')
    .withChildren(
        SBlueprint.of(Contact.class)
            .set('LastName', 'TestContact')
            .use('{P0}', 'Name', 'Description')  // copy root Account's Name into Description
    );
```

The Contact's Description becomes `'Acme'`. `{P0}` points to "the root Account", and you don't need to attach a `.alias(...)` on the parent side.

This is enough to understand the basic pattern of "create one parent and copy its value into the child". More advanced usage — deeper hierarchies with `.times(...)` involved — is covered in the next example.

### Example: A Grandchild Copies Its True Parent's Value

In a three-tier setup (parent / child / grandchild), the grandchild Case's `Subject` receives **the LastName of its own true parent Contact**.

```apex
SBlueprint.of(Account.class)                            // P0 (tier 0 / root)
    .set('Name', 'Acme-{#}')
    .times(2)
    .withChildren(
        SBlueprint.of(Contact.class)                    // P1 (tier 1)
            .parentIdField('AccountId')
            .set('LastName', 'Contact-{#}')
            .times(2)
            .withChildren(
                SBlueprint.of(Case.class)               // P2 (tier 2 = grandchild Case)
                    .parentIdField('ContactId')
                    .use('{P1}', 'LastName', 'Subject') // ← the LastName of its true parent Contact
                    .times(2)
            )
    );
```

The records and the grandchild Case Subjects (= the LastName of each grandchild's true parent) are as follows:

| Account | Contact (true parent) | Case (grandchild) Subject |
|---|---|---|
| `Acme-1` | `Contact-1` (under Acme-1) | `Contact-1` |
| `Acme-1` | `Contact-2` (under Acme-1) | `Contact-2` |
| `Acme-2` | `Contact-1` (under Acme-2) | `Contact-1` |
| `Acme-2` | `Contact-2` (under Acme-2) | `Contact-2` |

Each Contact has 2 grandchildren below it, so there are 8 grandchild Cases — the four combinations above, two each. There are two Contact records with the same `Contact-1` LastName (one under Acme-1, one under Acme-2), but each grandchild receives the value from **the real parent it actually hangs under**.

Points:

- Without attaching any aliases, **"the true parent for me" is hierarchically resolved automatically**
- The counting is the absolute depth from the root (not the distance upward from your current position)
- Let `withChildren`'s automatic processing handle parent Id copying — only use `{Pn}` when you specifically need to copy some other field value

## parentIdField: Disambiguating When the Child Has Multiple Lookups

If a child object has **multiple lookup candidates** (e.g. `Contact` has both `AccountId` and `CustomAccount__c`), `withChildren` alone can't decide "which lookup to put the parent Id into". You disambiguate with `.parentIdField(...)`.

**Signature:** `.parentIdField(String fieldName)`

```apex
SBlueprint.of(Account.class)
    .alias('acc')
    .withChildren(
        SBlueprint.of(Contact.class)
            .parentIdField('AccountId')    // specify which lookup gets the parent Id
            .template(Blueprints.conBasic())
    );
```

Objects without multiple lookups don't need `.parentIdField(...)`. Think of it as "add it when the ambiguity error fires" — a safety net you reach for after the fact.

## sharedWith: Sharing Follows Bulk Generation (v2.0.0+)

`.sharedWith(user, accessLevel)` declares a manual share **as a final state**. You never assemble `Foo__Share` / `AccountShare` rows yourself, and you never think about inserting them after the parent.

**Signature:** `.sharedWith(User user, String accessLevel)` (`accessLevel` is `'Read'` or `'Edit'`)

What matters in the context of this page is that a share declaration **rides the same bulk machinery described above**. Parents multiplied with `times` get the same number of shares.

```apex
SBlueprint.of(Invoice__c.class)
    .set('Name', 'Invoice-{#}')
    .alias('inv_{#}')
    .owner(admin)
    .sharedWith(rep, 'Read')
    .times(5);
// → 5 Invoice__c records, and 5 matching Invoice__Share records
```

A share declared on a nested child behaves the same way — it **multiplies along with the parent's `times`**.

```apex
SBlueprint.of(Account.class)
    .set('Name', 'Acme-{#}')
    .times(2)
    .withChildren(
        SBlueprint.of(Invoice__c.class)
            .owner(admin)
            .sharedWith(rep, 'Read')
            .times(3)          // 6 invoices → 6 shares
    );
```

None of this is special-cased for sharing. Internally `sharedWith` assembles a **sibling blueprint wired to the parent with `use()`**, so `times` propagation and `{Pn}` resolution take exactly the same path as any ordinary child (see [How Dependency Resolution Works](/apex-stem/docs/apex-blueprint-dependency-resolution-deep-dive)).

### A contradictory declaration fails before the DML

Shares are not something you can always write, so declarations that cannot hold raise `ApexBlueprintException` during the analysis stage of `create()`.

| Situation | Why |
|---|---|
| The object's org-wide default is `Public` | Manual share rows don't exist at all (`Foo__Share` is not found) |
| Sharing with the very user given to `owner()` | The owner already has full access, and Salesforce rejects the manual share |
| `accessLevel` is anything other than `'Read'` / `'Edit'` | Bad argument |

None of these reach the DML. Instead of "I inserted it and the share wasn't there", you get a reasoned failure at declaration time.

> 📌 For how to build the persona on the other side of the share, see the [SPersona API reference](/apex-stem/docs/apex-blueprint-api-spersona).

## Example: One Parent + Three Children + One Grandchild

Bringing everything together into a single test:

```apex
@isTest
static void testAccountWithContactsAndCase() {
    SOrchestrator orchestrator = SOrchestrator.start()
        .add(
            SBlueprint.of(Account.class)
                .template(Blueprints.accBasic())
                .set('Name', 'ParentAccount')
                .alias('acc')
                .withChildren(
                    SBlueprint.of(Contact.class)
                        .set('LastName', 'Contact-{#}')
                        .use('{P0}', 'Name', 'Description')  // root Account (P0)'s Name into Description
                        .alias('con_{#}')
                        .times(3)
                )
        )
        .add(
            SBlueprint.of(Case.class)
                .set('Subject', 'TestCase')
                .use('con_2', 'Id', 'ContactId')             // link to the 2nd Contact
                .alias('targetCase')
        );
    orchestrator.create();

    Account parent = (Account) orchestrator.getByAlias('acc');
    Contact con2 = (Contact) orchestrator.getByAlias('con_2');
    Case targetCase = (Case) orchestrator.getByAlias('targetCase');

    Assert.areEqual(3, [SELECT COUNT() FROM Contact WHERE AccountId = :parent.Id]);
    Assert.areEqual('ParentAccount', con2.Description);
    Assert.areEqual(con2.Id, targetCase.ContactId);
}
```

Points:

- The nested `withChildren` visualizes "3 Contacts hang under the Account" directly
- `use('{P0}', 'Name', 'Description')` copies the parent Account's Name into each child Contact's Description
- A `{#}`-bearing alias (`con_{#}`) lets you later retrieve the 2nd Contact as `'con_2'` and link a Case to it
- The test body contains no complex procedure — "the final data structure I want" reads directly from the code

## Example: A Shared Reference Across Trees (Diamond Dependency)

`withChildren` expresses a **tree**, but real integration tests often need a deeply nested child to reference a **shared record that sits outside the tree**. Take a four-tier `Account → Opportunity → Quote → QuoteLineItem` chain where the deepest `QuoteLineItem` references a shared `Product2`: the dependency is no longer a tree but a **diamond** (a DAG).

Declare the parent-child chain with `withChildren`, the cross-tree reference with `use`, and register the shared record with its own `add` so an alias can reach it. The `add` order is free — SOrchestrator's topological sort works out that `Product2` and `Account` must precede `QuoteLineItem`.

```apex
@isTest
static void testQuoteLineItemReferencesSharedProduct() {
    SOrchestrator orchestrator = SOrchestrator.start()
        .add(
            SBlueprint.of(Product2.class)
                .set('Name', 'Widget')
                .alias('product')                        // shared record, outside the tree
        )
        .add(
            SBlueprint.of(Account.class)
                .template(Blueprints.accBasic())
                .withChildren(
                    SBlueprint.of(Opportunity.class)
                        .template(Blueprints.oppBasic())
                        .withChildren(
                            SBlueprint.of(Quote.class)
                                .set('Name', 'Q-2026')
                                .withChildren(
                                    SBlueprint.of(QuoteLineItem.class)
                                        .template(Blueprints.qliBasic())
                                        .set('Quantity', 3)
                                        .use('product', 'Id', 'Product2Id')  // cross-tree reference
                                        .alias('qli')
                                )
                        )
                )
        );
    orchestrator.create();

    Product2 product = (Product2) orchestrator.getByAlias('product');
    QuoteLineItem qli = (QuoteLineItem) orchestrator.getByAlias('qli');
    Assert.areEqual(product.Id, qli.Product2Id);
}
```

Key points:

- The shared record (`Product2`) is not a branch of the tree, so it gets its **own `add` + `alias`** rather than `withChildren`
- The cross-tree reference is one line: `use('product', 'Id', 'Product2Id')`. Combined with the `withChildren` chain, the dependency graph becomes a diamond rather than a tree
- Write `add` calls in whatever order reads best. Insert order is resolved by SOrchestrator's topological sort, so it never concerns you
- Required fields on `QuoteLineItem` (`PricebookEntryId`, `UnitPrice`, and in practice a `PricebookEntry` supplied through its own `add`) belong in the `Blueprints.qliBasic()` template, leaving only the delta under test (`Quantity`) and the reference (`use`) in the test body

## Related Documents

- [Declaring a Single Record with SBlueprint](/apex-stem/docs/apex-blueprint-sblueprint-guide): The five core methods
- [Resolving Dependencies and Executing DML with SOrchestrator](/apex-stem/docs/apex-blueprint-sorchestrator-guide): The execution engine
- [API Reference: SBlueprint](/apex-stem/docs/apex-blueprint-api-sblueprint): Full method signatures
- [Back to the ApexBlueprint Guide](/apex-stem/docs/apex-blueprint-guide)
