Correct way to change the content type of an existing content item

Topics: Writing modules
Jun 25, 2012 at 5:11 AM

Can anyone confirm with me the correct procedure for changing the content type of an existing content item?

I'm working on a commerce module (with the guidance from Sipke's excellent webshop blog series) and have defined a CustomerPart for storing commerce related information for each customer, and have defined a "Customer" content type that includes the existing Orchard UserPart and the CustomerPart that I've defined.

During checkout, this all works well for a new customer signup, as the Customer content item is created, or for an existing customer, the existing authenticated customer's content item is retrieved.

However, I have the situation where I have a number of "Users" that may exist that are not yet "Customers" (ie. they do not have the CustomerPart and their content item type is User, not Customer).

During checkout, if I fail to retrieve the CustomerPart for the currently authenticated User then I want to change the user into a Customer. Something along the lines of:

var customer = currentUser.ContentItem.As<CustomerPart>();

// If the authenticated used is not yet a customer then firstly generate a customer record
if (customer == null)
    customer = _customerService.MakeCustomer(currentUser);

Now in the customer service's MakeCustomer implementation, I'm attempting the following:

customerPart = new CustomerPart 
    Record = new CustomerPartRecord 
        CreatedAt = _dateTimeService.Now
user.ContentItem.TypeDefinition = _contentDefinitionManager.GetTypeDefinition("Customer");

The problem is, although the customer apart appears to get added to the content item, the type definition of the content item remains as "User". Ie. in the Orchard_Framework_ContentItemRecord table the entry for the User (in my case the admin user with id = 2) still has a ContentType_id of 2 (User) instead of 10 (Customer). I then have problems when attempting to retrieve the content item as a customer later on.

Is there a more correct approach to this problem?


Jun 25, 2012 at 2:01 PM

Gave up on this approach. Just decided to prevent non-customer authenticated users (ie. users without a CustomerPart like admin users) from shopping all together.

Jun 26, 2012 at 2:25 AM
Edited Jun 26, 2012 at 2:25 AM

I have run into a similar situation and in the end decided it would be much easier to simply attach the CustomerPart to the User content type. Allowing all users to purchase stuff increased sales as well ;)

Jun 26, 2012 at 2:50 AM

Yeah that definitely sounds like the simplest approach.

I guess it would be nice if Orchard had (maybe it does?) some concept of Content Type inheritance and the ability to change a Content Item's type (eg. promote it from being a base "User" content type to a "Customer" content type).

Jun 26, 2012 at 3:11 AM

On inheritance:

Short version: no

Long version: it is a deliberate design choice to prefer composition over inheritance. It's the whole point of parts in fact. Inheritance is never going to happen in Orchard :)

As for changing the type of an existing item: it's actually the first time I see that request, which is enough to deny it in my opinion until it becomes a recurrent legitimate scenario.

Jan 2, 2014 at 6:32 PM
Hi Sipke,
In your series on developing modules in Orchard, you used something like:
        var customer = currentUser.ContentItem.As<CustomerPart>();
The above doesnt work and give a compile time error.

I changed the above to something as:
        var customer = ((dynamic)currentUser.ContentItem).CustomerPart;
I am still working on it I am not sure if that works or not.

Jan 3, 2014 at 2:06 AM
I'm pretty sure that statement works, provided that you included the required namespace. As<>() is an extension method.
What is the exact compilation error you're getting?

Your refactored code should work as well. If not, please provide the exact error message you see to help us help you.
Jan 3, 2014 at 6:59 AM
It should work, yes, but what's the point? The first one is easier to read, and will be checked at compile-time, catching errors sooner.
Jan 3, 2014 at 10:22 AM
I completely agree, in this case the first one should be used. But it's useful to know the dynamic syntax as well for cases that work with parts that don't have a class to cast to.
Jan 4, 2014 at 10:48 PM
I was missing the namespaces, it works, Thanks.

Is it a C# thingy that any sub-property fetched on a dynamic object is found? Or something related to Orchard?

Jun 1, 2014 at 12:54 AM
Edited Jun 1, 2014 at 12:55 AM
I have a similar scenario where I have Person content type that I want to be able to attach a UserPart to. There would be scenarios where a Person is not initially a User but would be promoted to one which works fine by just attaching the UserPart to my Person type. My problem is that I have existing Users that would need to be converted to Person items. Having a way to change a User to a Person would be helpful to me.