Orchard CMS contentfields vs class properties - orchardcms

When should one use Fields from the CMS and when to use class properties and database fields?
My scenario:
Created a product content part with fields usage (text) and price (double). Then I have created a contenttype product and added the part.
I could also have created a product record in models and added a product table. And a part with driver etc.
At first glance I dont see difference besides option 2 requiring programming.
However I did encounter a problem:
In option 2 I could use repository and create a lot of items programmatically.
In option 1 I could create product content items but was not able to fill in the fields of the product part (no errors, but fields remained empty)
So when to use option 1 and when option 2?
And is my problem with option 1 related to that option?
EDIT: Clarifiation of problem with option 1
I have created a productpart. To the productpart I have added the field price which is a decimal and I have added a field Usage of product which is a text field.
In the code I have the following:
dynamic item = _cm.New("Product");
item.TitlePart.Title = "Mijn dummy product";
item.BodyPart.Text = "Some dummy text for this product";
item.ProductPart.Price.Value = new decimal(20.5);
item.ProductPart.Usage.Value = "Some dummy usage of this product";
_cm.Create(item);
After running the code, the product is created with the correct title and body text, but Usage and Price come up emtpy.
I also tried it with the item.As<> method. But that does not compile to As since I have not created an object with that name.

A difference is that fields can only be queried against with projections but more generally parts and fields are for different usage: parts are meant to be reusable, not fields. Price for example is going to be a characteristic of any product so it should probably be a part property. Usage, I don't know, it sounds fairly specific to what you're doing and may be better done as a field.
As for your problems with option 1, it's hard to say without seeing your code but chances are you're doing it wrong.

Apparently the item has to be created first before accessing the contentpart and fields. At least it worked for me.
Please verify if my analysis is correct. If so I will mark it an answer, as well as the answer of Bertrand.
The code now looks like this:
var item = _cm.New("Product");
item.As<TitlePart>().Title = "Mijn dummy product";
item.As<BodyPart>().Text = "Some dummy text for this product";
//Create first before accessing the contentparts that are dynamic.
_cm.Create(item);
//And then access the product via dynamic object.
dynamic dyn = item;
item.ProductPart.Price.Value = new decimal(20.5);
item.ProductPart.Usage.Value = "Some dummy usage of this product";

Related

E-Commerce Product Custom Field Kentico 10

I am using E-commerce module of Kentico portal and it has two fields for Products pricing : SKUPrice and SKURetailPrice.
I needed one more field to show sell price and I added a new field in Modules application of the portal.(Modules->E-Commerce->Classes->SKU->Fields->New Field)
Now, I need to access this field in my code,but SKUInfo class doesn't show me the newly added field.What I need to do so that the newly added field reflects in my project code ?
I have already build the entire solution multiple times.Any other solutions please.
You can use GetValue and SetValue methods for such fields like this:
SKUInfo sku = ...;
string a = sku.GetValue("field").ToString();
sku.SetValue("field", "value");
JanH has the answer for custom fields you set, also keep in mind though that there is a "SKUCustomData" for other information that you want to store. It takes a Name-Value pair dictionary if memory serves me correctly, and useful if you need to store configuration information or other things that won't be located on the normal SKU table.

In Orchard CMS, how can I get all items of a particular content type based in the values of an attached part's properties

I'm using Orchard 1.7.2.
I have created a new content type called PropertyImage of stereotype Media. I also created a part called PropertyPart and attached that part to my PropertyImage content type. This allows a user to pick a product when uploading a PropertyImage (ie to say 'This image is of this property').
So far so good.
Now what I'd like to do is query for all PropertyImages that have a PropertyPart attached to them where the associated property is x, y, or z.
This is what I have so far:
var images = _orchardServices.ContentManager
.Query<PropertyPart, PropertyRecord>()
.Where(p => p.PropertyId == id)
.ForType(new[] { "PropertyImage" });
This however will only return a collection of PropertyParts, which is not what I want, because I want the whole PropertyImage Content Item. How can I do this?
I should point out that properties come from an external source, and are therefore not content items.
Edit
As soon as I asked this question, I realised I could just append my query with this:
.List().Select(p=>p.ContentItem)
Sometimes it just helps to talk your problem through!
As soon as I asked this question, I realised I could just append my query with this:
.List().Select(p=>p.ContentItem)
Sometimes it just helps to talk your problem through!

New ContentItem from code, when is it saved?

I can't get my head around this.
If I have a ContentType called Contacts. The ContentType has two fields attached to it.
FirstName (textfield) and LastName (textfield).
If I want to create a new contentitem of this type then I can write code like this.
dynamic contact = _services.ContentManager.New("Contacts");
contact.Contacts.FirstName.Value = "John";
contact.Contacts.LastName.Value = "Doe";
_services.ContentManager.Create(contact, VersionOptions.Published);
This does not work. The Contentitem gets created but the fields are empty.
However, if I write it like this it works. Why is that? Must I set the fields values after ContentManager.Create is called?
dynamic contact = _services.ContentManager.New("Contacts");
_services.ContentManager.Create(contact, VersionOptions.Published);
contact.Contacts.FirstName.Value = "John";
contact.Contacts.LastName.Value = "Doe";
What you observed is indeed the intended behaviour and is by design. I've come across this before too and also created an issue about it. As you can see there it was closed by Sebastien, the lead developer stating that this is by design but unfortunately not explaining why.
FYI the standard workflow for managing content items is the following:
Instantiate item.
Create it.
Update its values.
If the update happened through the model binder then check if the ModelState is valid. If not, cancel the transaction and return.
If everything's OK and the content type is set to be draftable, publish the item.
You can see an explained example of this in the Orchard Training Demo module (ContentsAdminController.PersonListDashboardPost()).

Orchard - Query Custom Fields in Content Item

I have a Content Type "News" with custom field "NewsDate" (DateTimeField) and "Active" (BooleanField)
Now I'm need to get 3 active atimes order desc by NewsDate
Get all news, make them toList() and from there manipulate the data is not a solution.
P.S. I need to do something like:
var items = contentManager
.Query(Entities[PageType.Press])
.OrderByDescending<CommonPartRecord, DateTime?>(record => record.PublishedUtc)
.Slice(0, 3);
but instead of PublishedUTC use my custom field "NewsDate" and add Active == true, However it is not possible due to Orchard architecture of storing custom data in a separate field as XML data.
UPDATED:
In a nutshell I want to generate from code behind the following Query:
DECLARE #temp as TABLE(id int, xmldata xml)
INSERT #temp VALUES(1,'<Data><News><NewsDate>07/14/2011 11:42:00</NewsDate><Link Title="" DisplayText="" Link="www.example.com" OpenInNewTab="True">www.example.com</Link></News></Data>')
INSERT #temp VALUES(2,'<Data><News><NewsDate>07/11/2011 12:11:00</NewsDate><Link Title="" DisplayText="" Link="www.example.com" OpenInNewTab="True">www.example.com</Link></News></Data>')
INSERT #temp VALUES(3,'<Data><News><NewsDate>02/21/2012 16:56:00</NewsDate><Link Title="" DisplayText="" Link="www.example.com" OpenInNewTab="True">www.example.com</Link><NewsLink></NewsLink></News></Data>')
SELECT
TOP 3 [id],
[xmldata].value('(Data/News/NewsDate)[1]', 'datetime') as NewsDate
FROM #temp
ORDER BY NewsDate DESC
P.S. I looked through the code for DynamicContentQueryTests, however all the examples uses the Part, and in my case Fields are just in the ContentItem:
E.g. News content type contains NewsDate field (datetime field) and some parts as well
Any suggestions are greatly appreciated.
Querying fields is possible since 1.4 through Projector and underlying index tables and new APIs on Content Manager. Your simplest bet actually may be to create a projection.
To get the values of fields that have been attached directly to a Content Item, you need to first look for the Content Part with the same name as the item, which is created by Orchard. So in your case in the Parts list for each News Content Item you'll find a part called "NewsPart", and inside this the Fields property will have your NewsDate and Active fields.
However, like you say Orchard serializes the field values into XML for storage to prevent it having to change the database structure every time you add/remove a field to/from a content type. For this reason, querying and ordering by fields is not recommended because all the serialized data needs to be de-serialized for each Content Item. If you want to be able to do this, the best way is to make your own Content Part with a Content Part Record and use your own table for storage, then you can do this:
contentManager.Query<NewsPart, NewsPartRecord>()...
...and query/sort on whatever values you like.
Bertrand is correct. Check this link : http://www.davidhayden.me/blog/projector-module-in-orchard-cms. You can implement your requirements easily with Projection Module. The link will tell you the steps to do that. If you don't want a widget, you can create a page with your query too. Once the module is enabled, you will see the option to create a Projection Page in the left hand side navigation.
EDIT :
Can't you simply parse the XML? You can query your content type 'News'. Something like -
var contentItems = contentManager.Query<ContentPart>("News").Join<TitlePartRecord>().Join<AutoroutePartRecord>().Join<CommonPartRecord>().List();
Once you have that, you can access the XML data by :
foreach (var item in contentItems)
{
var contentItem = (ContentItem)item.ContentItem;
ContentItemVersionRecord contentItemRecord = contentItem.VersionRecord;
string data = contentItemRecord.Data;
//Call some function here to parse 'data' and store the object in the list.
}
'data' has the information in XML format that you need.
You can parse it and then store the objects in your list that you can order by your custom field.

How to create a lookup column that targets a Doc Lib and uses the 'Name' of the document?

How do you create a lookup column to a Document Library that uses the 'Name' of the document as the lookup value?
I found a blog post that recommends adding another custom field like "FileName" and then using a item reciever to populate the custom field with the value from the Name field but that seems cheesy.
Link to the blog in case people are interested:
http://blogs.msdn.com/pranab/archive/2008/01/08/sharepoint-2007-moss-wss-issue-with-lookup-column-to-doc-lib-name-field.aspx
I've got a bunch of custom document content types that I dont want to clutter with a work around that should really work anyway.
I created a one step workflow to set the title from the name, fired on modify and created. Seems to work and took seconds to create.
One way you can do this (although not the easiest way) is by creating a custom field type that extends the SPFieldLookup class. SharePoint's field editor for Lookup fields purposefully hides any columns types that aren't supported by Lookup fields, but you can create a field editor for your custom field type that shows them.
However, I have created a Lookup column that points to a Name column in a Document Library before, and it probably doesn't work like you'd expect. While the value stored in the lookup column is valid, it doesn't show up right in List view or on the View Properties form.
The solution you posted may actually be the best way to handle this. Lookup fields require some kludges if you want to handle more complex scenarios, but that's because they're not meant to provide the same functionality as a foreign key relationship in a database.
Coding in any form always scares me. So Here's what I did: I simply renamed the Stupid "Title" Field to something else, say "Keywords", since you cant do anything with that field: cant even make it mandatory.
Then I created another Single line field called "Title" and used this field for the Lookups
Well there is a simple solution to that and in might work in some case.
In the nutshell if you make the Title field Mandatory, this will force the user to enter a title. In that manner we can use title field as a lookup field.
Now How to do that?
One you are done create a document library go to the library setting. Select Advance Setting and Select Yes for the option "Allow management of content types?".
Then go back to the Library setting and Under content types select the "Document" Content type. THen Select Title Column and then Select "Required (Must contain information)" and say OK.
Now try uploading a document to this document library. You will see Title field in the form.
Hope this helps
Cheers
Vaqar
You have to add the field as XML with the ShowField as 'FileLeafRef'
var XmlFieldDefinition = "<Field DisplayName='myLookupColumn' Type='LookupMulti' StaticName='myLookupColumn' Name='myLookupColumn' Required='FALSE' List='THE LOOKUP ID HERE' WebId='THE WEB ID HERE' UnlimitedLengthInDocumentLibrary='TRUE' Mult='TRUE' Sortable='FALSE' ShowField='FileLeafRef' />"
Field fld = fieldCollection.AddFieldAsXml(XmlFieldDefinition, true, AddFieldOptions.DefaultValue);
ClientContext.Load(fld);
ClientContext.ExecuteQuery();

Resources