Custom ContentPart via code - ContentField vs. Database Field

Topics: Customizing Orchard, Writing modules
Jun 21, 2014 at 12:44 AM
I've been tinkering with Orchard for a while now but am finally diving in head first into some more advanced stuff. I'm creating a few custom Modules for various reasons, and intent to create custom ContentParts in the Migration for the module.

The question I've been grappling with is, when is it appropriate (best?) to attach a ContentField and when is it appropriate to use a PartRecord?

For example, in the Orchard documentation there's an example where a MapPart is created. The migration is as follows:
// Creating table MapRecord
SchemaBuilder.CreateTable("MapRecord", table => table
                .ContentPartRecord()
                .Column("Latitude", DbType.Double)
                .Column("Longitude", DbType.Double)
            );

ContentDefinitionManager.AlterPartDefinition(
                typeof(MapPart).Name, cfg => cfg.Attachable()
            );
Now, of course, you have to have the appropriate PartRecord defined, Drivers defined, Handlers defined, and so on.

What are the benefits of this strategy versus just creating the same MapPart this way (this code isn't tested so I apologize if there are errors - just here for discussion):
ContentDefinitionManager.AlterPartDefinition("MapPart",
    b => b.Attachable()
    .WithField("Latitude", f => f
        .OfType("NumericField").WithDisplayName("Latitude")
        .WithSetting("NumericFieldSettings.Hint", "Latitude (ex: 31.25, 31.00, 3.00)")
        .WithSetting("NumericFieldSettings.Required", "False")
        .WithSetting("NumericFieldSettings.Scale", "2")
        .WithSetting("NumericFieldSettings.Minimum", "-90")
        .WithSetting("NumericFieldSettings.Maximum", "90"))
    .WithField("Longitude", f => f
        .OfType("NumericField").WithDisplayName("Longitude")
        .WithSetting("NumericFieldSettings.Hint", "Longitude (ex: 31.25, 31.00, 3.00)")
        .WithSetting("NumericFieldSettings.Required", "False")
        .WithSetting("NumericFieldSettings.Scale", "2")
        .WithSetting("NumericFieldSettings.Minimum", "-90")
        .WithSetting("NumericFieldSettings.Maximum", "90"))
);
The flexibility Orchard provides is great, but it makes the minefield of learning it a bit more difficult to navigate.

Thanks in advance!
Developer
Jun 21, 2014 at 10:34 PM
See this for the difference between fields and parts generally: http://orcharddojo.net/orchard-resources/Library/Wiki/ContentField

To make matters more complicated with 1.8 it's easy to store data from parts in the same way as fields do, in infoset.

In this particular case I'd use a content part, but all properties stored in infoset.
Jun 23, 2014 at 2:39 AM
Okay, so say I choose to go in the direction of defining my part with an infoset. My part, when using fields, contained an ImageField (actually, two of them) and a Text Field of flavor HTML.

I assume I would store the HTML field as a string and then, in the editor, use some HTML editor the same way Orchard does. Is that correct?

What direction should I go in to deal with the images? Do I store the URL as a string and then, in the editor, use the Orchard ImageField uploader?
Jun 23, 2014 at 2:53 AM
Or, it just occurred to me, is the path of least resistance to create two parts, one of which attached fields and the other of which uses Infoset? That way, I can have my TextField with flavor HTML and my Image fields in their own part, and the more concrete fields (datetime, strings, URLs, etc.) in their own part stored as an infoset?
Developer
Jun 23, 2014 at 1:15 PM
If you need to attach more than one "things" of the same type to a content type, possibly with different configuration, then you need a field, not a part. My suggestion was for your MapPart.

So your other example with ImageField and TextField sounds like a good candidate for fields. It depends on how what these fields/parts should do and how you want to use them (see again the general distinction between the two).
Jun 23, 2014 at 1:32 PM
Edited Jun 23, 2014 at 1:33 PM
After doing more reading, my head is spinning.

I want to create a Content Type. Let's say that it's for a Seminar.

The Seminar has (this is a partial listing):
  • Title
  • Date
  • Seminar Image
  • Seminar Type
  • Description
  • Presenter Name
  • Presenter Bio
  • Presenter Image
If I were to give these fields a type, so to speak, it would be:
  • Title (Title part)
  • Date (datetime)
  • Seminar Image (Media)
  • Seminar Type (string)
  • Description (Body part)
  • Presenter Name (string)
  • Presenter Bio (html)
  • Presenter Image (Media)
I want to be able to query and sort on the Date of the seminar and the Seminar Type. I want to create all of this in code in a custom module. I want to control the layout of the editor but I don't want to code it all by hand (let's just talk about storage first - just keep this in mind).

To make this work the best way without having to build editors for everything (media picker, HTML, etc.), I'm in a situation where I am best served by storing the data three different ways:

SeminarMedia Custom Content Part:
  • Seminar Image (Media field)
  • Presenter Bio (text field flavor HTML)
  • Presenter Image (Media field)
SeminarPart Custom Content Part Containing a parts record and infoset:

Part Record (for the queryable fields):
  • Date (datetime)
  • Seminar Type (string)
Infoset for:
  • Presenter Name
  • ...and other non-queryable fields
Then I'd have a custom Content Type with:
Title Part
Body Part
EventMedia Part
Event Part

Would that work?

That doesn't even tackle the issue of being able to control the editor layout where I might want fields from different parts interspersed. Every day I wrap my head around things a bit better, but I feel like my scenario isn't uncommon but the solution sure seems convoluted.
Developer
Jun 23, 2014 at 7:03 PM
This seems a good solution, yes.

In the end you can control the (editor) layout of everything by using shape overrides, regardless of where the part or field comes from.