Add field to content type through code

Topics: Customizing Orchard, Writing modules
Jun 20, 2012 at 5:25 PM

Is it possible to add a field to a content type through code?

Developer
Jun 20, 2012 at 8:14 PM
Edited Jun 20, 2012 at 8:15 PM

Fields are never attached to content types directly: they are always attached to parts. Even though it looks like you can actually add fields to a content type via the admin, what really happens is that the fields are attached to a content part that is implicitly created by Orchard. This implicitly created part has the same name as the content type in question.

So yes, you can use code to add fields to content type: just create a part specific to your content type, and add the field to that part.

Jun 21, 2012 at 4:06 PM

Thanks @sfmskywalker for the explanation, that helps me conceptually understand what is going on. I'm writing a module that I would like to add an enumeration field to the page and blog content types.

I don't suppose you could illustrate doing this in code? I know how to write and define a custom part, it is the ContentDefinitionManager.AlterPartDefinition call I'm not quite clear on.

Developer
Jun 22, 2012 at 1:15 PM
Edited Jun 22, 2012 at 1:17 PM

Of course, just try the following:

using Orchard.ContentManagement.MetaData;
using Orchard.Data.Migration;

namespace MyCustomModule
{
    public class Migrations : DataMigrationImpl
    {
        public int Create() {

            // Define the "Page" content part (this is the same part that would have been implicitly created by the admin when you added a field to the "Page" content type).
            ContentDefinitionManager.AlterPartDefinition("Page", cfg => cfg
                
                // Add an enumeration field called "Category" to the part and specify some settings. To see what settings are available, consult the EnumerationFieldSettings class.
                .WithField("Category", field => field
                    .OfType("EnumerationField")
                    .WithSetting("EnumerationFieldSettings.Options", "Professional\r\nPersonal\r\nScience\r\nReligion\r\nSports")
                    .WithSetting("EnumerationFieldSettings.ListMode", "Radiobutton")
                )
            );

            // Attach the "Page" part to the "Page" content type.
            ContentDefinitionManager.AlterTypeDefinition("Page", cfg => cfg
                .WithPart("Page")
            );

            return 1;
        }
    }
}

The code should speak for itself, but this is what's happening:

  1. When the module is enabled, a "Page" part is either created or modified (depending on whether the "Page" part already exists or not).
  2. A field called "Category" is attached to the "Page" part. In this sample, we're attaching an EnumerationField and specify some settings that are specific to this field type. 
  3. We modify the "Page" content type by attaching the "Page" part to it. If the "Page" part was already attached to this type, nothing special happens. Otherwise, the part is simply attached to the type.
Jun 22, 2012 at 7:22 PM
Edited Jun 22, 2012 at 7:26 PM

@sfmskywalker- Perfect! Thank you so much, this was exactly what I needed to see.

Also, this explains why when I access fields from the view its Model.ContentItem.Page.FieldName. Fields are part of a dynamically added part, named the same as the content type. The pieces are coming together!

Dec 16, 2013 at 3:52 PM
Just wondering if anything would also have to be done at the View, Model, or Driver level in a case like this. I'm trying to make it so a new field gets added to one of our custom parts when a module is enabled. It is just a boolean field that should dipslay a checkbox in the Editor for the custom part, and (ideally) display a text string if the checkbox is checked when the custom part is displayed.
Coordinator
Dec 16, 2013 at 8:32 PM
No. Just follow the instructions above.