Accessing Content Type Records from Controller

Topics: Customizing Orchard, Writing modules
Sep 4, 2011 at 11:45 PM

Hi all,

Thanks for all the helpful responses on my past posts, you've all been a great help.

So this is what I want to acheive. We have a google map that we've built that takes JSON to plot of few markers on the map. Currently the JSON is hard coded but I want to make this content manageable.

So my plan is to create a custom content type and then create a module that has a route that then accessing these records from the database and then returns them as JSON.

So as I don't know much about the data access in Orchard Project, how can I go about directly accessing the database to pull out any records I need? Do i just need to create a repository and access that way? Is there ways to write dynamic queries like LINQ-to-SQL for example?

Sorry for all of the questions, any examples would be great. Even the smallest help would be greatly appreciated.

Thanks,

Kevin

 

Sep 5, 2011 at 12:13 AM

You can use linq on the repositories, and you can access the repositories via constructor injection. (Put an IRepository<> in the constructor of your controller).

Or if you needed a more complex query you can get content items back using IContentManager.Query().

Developer
Sep 5, 2011 at 12:13 AM
Edited Sep 5, 2011 at 12:14 AM

If you need to access records directly, use IRepository<YourDesiredRecord>. It's a low-level mean of database access and you can do any queries Linq-to-NHibernate allows you to.

But if you are creating your own content type (with some custom content part on it, I suppose) you'd rather stick to IContentManager and it's Query method. It's a better way to go in this case. Querying the IRepository will get you only the raw database records, but content manager will return the full content items (with all parts it has).

Both approaches have pros and cons, though.

EDIT: @Carlbm - what a timing:D

Sep 5, 2011 at 4:59 AM

Hi Guys,

Thanks for the input, do you have any URL examples that I can take a look at. Can't find many samples around. If you want to post some that would be great.

Few questions, I just want to access a custom content type I will be creating so I gather this won't have it's own class name? i.e. if I use IRepository.

Trying to source more examples of the net now.

Thanks,

Kevin

Developer
Sep 5, 2011 at 5:11 AM

There is a lot of examples in almost every module - just search the code. IRepository is used directly eg. somewhere in Orchard.Roles. IContentManager is used everywhere:) Eg. in Orchard.Core/Contents/Controllers/ItemController.cs

If that's the case - don't bother using IRepository<>. It's for directly accessing records. If you need to query for your content types - use IContentManager.Query method instead. Content types don't have class names, but can be queried by name - there's an overload to IContentManager.Query which allows you to filter content items by given name (eg. Page, User, HtmlWidget, MyFancyNewType and so on).

Coordinator
Sep 5, 2011 at 5:19 AM

I thought I'd mention this module that seems to do something extremely close: http://orchardproject.net/gallery/List/Modules/Orchard.Module.NogginBox.BingMapList

Sep 5, 2011 at 5:32 AM

Brilliant thanks guys, I will take a look and post some code that I find that completes my task, for other orchard project n00bs like me. Thanks again.

Sep 5, 2011 at 5:57 AM

    Ok an update on this, but I'm still stuck. I've managed to hook up the IContentManager correctly and am loading the records. However I've got custom fields on the content types and the data seems to be coming back as XML in the ContentItemRecord class. i.e. <Data><Name>name here</Name></Data>

My question is is there an easy way to access this data? i.e. Select(x=>x.Name) etc etc.

Thanks for help in advance, I checked out the BingMaps code but couldn't see anythign that could help me with this.

public class HomeController : Controller
    {
        private readonly IContentManager _contentManager;

        public HomeController(IContentManager contentManager)
        {
            _contentManager = contentManager;
        }
       
        public JsonResult Index()
        {
            WhatsAroundViewModel whatsAroundVieWModel = new WhatsAroundViewModel();

            // Load the WhatsAround content items
            IEnumerable<ContentItem> whatsAroundContentItems = _contentManager.Query().ForType("WhatsAround").List();

            foreach (ContentItem contentItem in whatsAroundContentItems)
            {
                ContentItemRecord contentItemRecord = contentItem.Record;
                if (contentItem == null)
                    continue;

                WhatsAroundItem item = new WhatsAroundItem();

                // TODO Set values, how??

                whatsAroundVieWModel.Items.Add(item);
            }

            // Allow JSON GET as this is insecure data
            return base.Json(whatsAroundVieWModel, JsonRequestBehavior.AllowGet);
        }
    }

p.s. I'll promise I'll stop asking questions when i figure it all out, actually I'll start answering questions.

Sep 6, 2011 at 10:23 PM

If you're getting the data you need to interrogate as XML I would say your best bet is to parse the XML in a helper method/class and return a friendlier object or list of objects.

I THINK you would need to know what the Type is at compile time in order to be able to call properties on the results so that you can cast the list of ContentItems to that Type.

Also there is definitely a more efficient way of doing what you're trying to do. I'm not claiming to be a c#/.NET expert, and i'm sure even what I suggest could be improved but it's a start:

 

WhatsAroundViewModel whatsAroundViewModel = new WhatsAroundViewModel
{
    Items = (from contentItem in _contentManager.Query().ForType(YOUR_DYNAMIC_TYPE_STRING).List()
                  select new WhatsAroundItem(contentItem.YOUR_XML_DATA_STRING)
};

return base.Json(whatsAroundViewModel, JsonRequestBehavior.AllowGet);

 

As a note, i'm not sure how you are getting your xml data from the ContentItem, but you would get that xml data and then you could pass it into the constructor of the "WhatsAroundItem", and then parse the xml inside of the constructor to load the values you desire.

I hope this helps and that i'm not WAY off on what you're trying to achieve.

 

-Austin

Sep 7, 2011 at 4:21 AM

Hi Austin,

Yes I could see the XML in the Data property of the ContentItemRecord, I was wondering whether or not there was a specific way to access these fields, but I decided to use an XmlReader. I passed the Data string into my constructor. See below:

Thanks to all for your help. :)

public WhatsAroundItem(string contentRecordDataXml)
        {
            XmlReader xmlReader = System.Xml.XmlReader.Create(new System.IO.StringReader(contentRecordDataXml));

            string currentNodeName = string.Empty;

            while (xmlReader.Read())
            {
                if (xmlReader.NodeType == XmlNodeType.Element)               
                    currentNodeName = xmlReader.Name;                   
               
                if (xmlReader.NodeType == XmlNodeType.Text && !String.IsNullOrEmpty(xmlReader.Value))
                {
                    switch (currentNodeName)
                    {
                        case "Name":
                            this.Name = xmlReader.Value;
                            break;

                        case "Latitude":                           
                            this.Latitude = Convert.ToDouble(xmlReader.Value);           
                            break;

                        case "Longitude":
                            this.Longitude = Convert.ToDouble(xmlReader.Value);
                            break;

                        case "Image":
                            this.ImageUrl = xmlReader.Value.Replace("~","");
                            break;

                        default:
                            break;
                    }
                }
            }
        }