# Use model builders

> Canvas Sitecore integration allows users to define a model builder for component.

Source: https://docs.uniform.app/docs/integrations/content/sitecore/use-model-builders

Canvas Sitecore integration allows to define a model builder for component.
It can be helpful when item fields isn't enough and you need to prepare data from back-end side.

## Model builders

Implement `Uniform.ModelBuilders.IModelBuilder, Uniform.ComponentModel` interface

```csharp
public interface IModelBuilder
{
  [CanBeNull]
  object BuildModel([NotNull] Item datasourceItem);
  bool CanBuildModel([NotNull] Item datasourceItem);
}
```

`datasourceItem` - current datasource item (can be the same as a page item)  
`<result>` - json-serializable object

Register model builder via Sitecore config

```xml
<configuration xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
      <uniform>
        <siteConfigurations>
          <siteConfiguration name="MySite">
            <componentModel>
              <componentModelBuilderService ref="uniform/services/componentModelBuilderService">
                <modelBuilders hint="raw:RegisterModelBuilder">
                  <builder componentId="<CANVAS_COMPONENT_ID>" parameterName="<CANVAS_PARAMETER_NAME>" type="Namespace.ModelBuilderType, Assembly" />
              </modelBuilders>
            </componentModelBuilderService>
          </componentModel>
        </siteConfiguration>
      </siteConfigurations>
    </uniform>
  </sitecore>
</configuration>
```

`@uniformdev/canvas-sitecore` package contains a few helpers to work with model builders.

- `getModelOrItem` function

  ```js
  const model = await getModelOrItem({
    id: itemId,
    componentType: component.type,
    parameterName,
    config,
    isPreview,
    // disable fallback to item result when model is missing 
    modelOnly,
  });
  ```
- `getPageItemId` function

  ```js
  const pageId = getPageItemId({ composition });
  ```
- create an enhancer from `createItemEnhancer` function

  ```js
  const modelEnhancer = createItemEnhancer({
    pageId,
    isPreview,
    config,
    allowMultipleItems,
    // disable fallback to item result when model is missing 
    modelOnly,
    logger,
  });
  ```

## Implement model builder

1. In Visual Studio, create a new project

   - **Project template**: Class Library (.NET Framework)
   - **Framework**: 4.6.2+
2. Add the Sitecore NuGet repository as a package source in your solution.
3. Add the Uniform NuGet repository as a package source in your solution.

   > **Info:**
   >
   > Contact our [support team](mailto:support@uniform.dev) for a link to this repository.
4. Add a reference to the following packages:

   - Sitecore.Kernel
   - Uniform.ComponentModel

Implement Sample Rendering model and model builder

```csharp
using System.Collections.Generic;
using Sitecore.Data.Items;
using Uniform.ModelBuilders;
namespace MySite.ModelBuilders
{
  public class SampleRenderingModel
  {
    public string Title { get; set; }
    public string Text { get; set; }
  }
  public class SampleRenderingModelBuilder : IModelBuilder
  {
    public object BuildModel(Item datasourceItem)
    {
      return new SampleRenderingModel()
      {
        Title = datasourceItem["Title"],
        // Rich Text fields needs to be rendered
        Text = ModelBuilderHelper.RenderField(datasourceItem, "Text")
      };
    }
    public bool CanBuildModel(Item datasourceItem)
    {
      return datasourceItem.Paths.IsContentItem;
    }
  }
}
```

Register Sample Rendering model builder

Create `App_Config\Include\zzz_MySite\MySite.Uniform.Canvas.config` config with the following content

```xml
<configuration xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <uniform>
      <siteConfigurations>
        <siteConfiguration name="MySite">
          <componentModel>
            <componentModelBuilderService ref="uniform/services/componentModelBuilderService">
              <modelBuilders hint="raw:RegisterModelBuilder">
                <sampleRendering componentId="sampleRendering" parameterName="datasource" type="MySite.ModelBuilders.SampleRenderingModelBuilder, MySite" />
              </modelBuilders>
            </componentModelBuilderService>
          </componentModel>
        </siteConfiguration>
      </siteConfigurations>
    </uniform>
  </sitecore>
</configuration>
```

Update `enhanceComposition` function

```js
import { EnhancerBuilder, enhance } from "@uniformdev/canvas";
import { createItemEnhancer } from '@uniformdev/canvas-sitecore';
export async function enhanceComposition({ composition, pageId, config, context, isPreview }) {
  const modelEnhancer = createItemEnhancer({
    pageId,
    config,
    isPreview,
  });
  const enhancers = new EnhancerBuilder()
    .component('sampleRendering', b => b.data('item', modelEnhancer))
  await enhance({ composition, enhancers, context });
}
```

## Item model builder

`Uniform.ModelBuilders.ItemFieldsModelBuilder, Uniform.ComponentModel` allows to build simple models without custom code.

```xml
<configuration xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <uniform>
    <siteConfigurations>
        <siteConfiguration name="MySite">
          <componentModel>
            <componentModelBuilderService ref="uniform/services/componentModelBuilderService">
              <modelBuilders hint="raw:RegisterModelBuilder">
                <primaryHero componentId="primaryHero" parameterName="datasource" type="Uniform.ModelBuilders.ItemFieldsModelBuilder, Uniform.ComponentModel">
                  <!-- camel-cased by default -->
                  <UseCamelCase>false</UseCamelCase>
                  <Templates hint="list:AddTemplate">
                    <PrimaryHero>{9E922288-97F6-4368-9231-72043A99CBA9}</PrimaryHero>
                  </Templates>
                  <Fields hint="raw:AddField">
                    <!-- use `title` as property key -->
                    <field prop="title">HeroTitle</field>

                    <!-- use field raw value (item[field]) -->
                    <field raw="true">RawValueField</field>

                    <!-- 
                        mark field as optional
                        can be helpful when templates have different set of fields
                    -->
                    <field optional="true">OptionalField</field>

                    <!-- ignore field expanders (standard FieldRenderer will be used) -->
                    <field expander="false">HeroDesc</field>
                  </Fields>
                </primaryHero>
              </modelBuilders>
            </componentModelBuilderService>
          </componentModel>
        </siteConfiguration>
      </siteConfigurations>
    </uniform>
  </sitecore>
</configuration>
```

## Define model dependencies

Model builders may have implicit Sitecore item dependencies.
In order to support incremental cache purge for Data Types, we need to define such kind of dependencies explicitly.

> **Info:**
>
> You can skip this step if you don't intend to utilize Data Types.

> **Warning:**
>
> This functionality requires latest `v7.2-patch-2` version of the Uniform connector (Sitecore package named `Uniform Canvas v7.2.230517-1 Upgrade Package for UFS v7.2`) which needs to be installed on top of previously installed `v7.2` or `v7.2-patch-1` (Sitecore package named `Uniform Canvas v7.2.23XXXX for Sitecore 8.2.0-10.3.0`). If you use .NET SDK please make sure to update your dependencies as per [packages.config](https://github.com/uniformdev/sitecore-canvas-dotnet-sdk/blob/main/Uniform.SDK/packages.config)

Use `Uniform.Models.ModelBuilderResult, Uniform.ComponentModel` as model builder result

```csharp
using System.Collections.Generic;
using Sitecore.Data.Items;
using Uniform.ModelBuilders;
using Uniform.Models;
namespace MySite.ModelBuilders
{
  public class SampleRenderingModel
  {
    public string Title { get; set; }
    public string GlobalTitle { get; set; }
  }
  public class SampleRenderingModelBuilder : IModelBuilder
  {
    public object BuildModel(Item datasourceItem)
    {
      var dependencies = new List<ID>();
      var globalSettings = datasourceItem.Database.GetItem("/sitecore/content/GlobalSettings");
      if (globalSettings != null)
      {
        dependencies.Add(globalSettings.ID);
      }
      var model = new SampleRenderingModel()
      {
        Title = datasourceItem["Title"],
        GlobalTitle = globalSettings != null ? globalSettings["Title"] : ""
      };
      // you don't need to add `datasourceItem` to the dependency list
      return new ModelBuilderResult(model, dependencies);
    }
    public bool CanBuildModel(Item datasourceItem)
    {
      return datasourceItem.Paths.IsContentItem;
    }
  }
}
```
