related items based on category

May 20, 2015 at 10:37 AM
Edited May 20, 2015 at 11:52 AM
Hi

With all the new features what would be the best way to set this up

I have a few contentypes one of them being a blog, event etc

the blog has categories ( e_Category )

Now when I am on a blog detail page ( filterbyid ) , what would be the best way to accomplish the following

I want a list with all the related blogs ( so blogs that has the same category , and also a list of related events, so all the events with the same category

To make it more fun/complex a blog or event item can have multiple categories


so if the blog item has category a and category 2


related blog
  • all blog items with either category 1 or 2
related events
  • all event items with either category 1 or 2
I have used this before
var blogOfCat = CreateSource<RelationshipFilter>(App.Data["Blog"]);
        blogOfCat.Relationship = "e_Categories";
        blogOfCat.Filter = "[QueryString:cat]";
So instead of the querystring is it easy to setup so that it can take one or more categories?

Any sample code?

thanks

Armand
Coordinator
May 26, 2015 at 5:41 AM
@Armand

So I believe you're attempting 2 things
  1. Use a parameter from another entity (instead of from URL)
  2. Use multiple parameters - so you want to be able to find all items with relationships to EITHER 1, 2, 3, or many of these, right?
Doing #1 is simple for a single-value, just do
blogOfCat.Filter = Content.Category.EntityId;
For multiple values the RelationshipFilter doesn't do it though. So for multiple values you'll have to use LINQ, sorry :(.

I don't know the exact code, but it will be something along the lines of
var blogOfMultipleCats = from b in AsDynamic(App.Data["Blog"])
     where b.Any(item=> item.e_Categories.Contains(Content.Category))
     select b;
this is just approximate code. You'll probably have to fiddle with it a bit till it works - please post the result you worked out :)
Coordinator
Jun 8, 2015 at 7:42 AM
Did it work? any feedback?
May 24, 2016 at 9:19 PM
Hi, I had the exact some question and have some feedback on your last answer.

For 1., it seems like Filter will only work with strings, is that correct? I cannot get EntityId to work successfully here. I always have to use a field that's a string.

For 2: This brings me back to where I was in another thread: http://sexycontent.codeplex.com/discussions/546602#post1473925

When attempting to do this, I still get a lambda expression error. For context, here is my entire template file. It's based on the FAQ example app, so that's why there's some qsofCat mixed in with things like Events. Semantically, I guess it should be more like eventsofCat, but that's beside the point. Any ideas on how to get this to work properly?
@using ToSic.Eav.DataSources
@using ToSic.SexyContent
@functions{
    // FOR MULTIPLE CATEGORIES
    // Prepare the data
    public override void CustomizeData()
    {

    var qsOfMultipleCats = from q in AsDynamic(App.Data["event"])
        where q.Any(item=> item.categories.Contains(Content.Category))
        select q;     

        var sorted = CreateSource<ValueSort>(qsOfMultipleCats);
        sorted.Attributes = "EntityTitle";
        Data.In.Add("event", sorted["Default"]);
    }
   
}

<h2 class="sc-element">@Content.Title @Content.Toolbar</h2>
<p>@Html.Raw(Content.Introduction)</p>

    @foreach (var cont in AsDynamic(Data["event"]))
    {
    <div class="entry">
             @cont.Toolbar
             <h2>@cont.EntityTitle</h2>

        
        @if(cont.categories != null)
             {
                 <p><strong>Categories: </strong>
                    @{ int counter=0; }
                    @foreach(var item in cont.categories){
                        counter++;
                        @item.name
                        if(counter < cont.categories.Count) {@Html.Raw(", ")}
                 }
                </p>
                
            } else {<text>no categories</text>}
            
       </div>
    }
The below works just fine, BTW:
@using ToSic.Eav.DataSources
@using ToSic.SexyContent
@functions{

    // Prepare the data
    public override void CustomizeData()
    {
        var qsOfCat = CreateSource<RelationshipFilter>(App.Data["event"]);
        qsOfCat.Relationship = "categories";
        qsOfCat.Filter = Content.Category.Count > 0 ? Content.Category[0].Name : "";  // manually set in content type
        var sorted = CreateSource<ValueSort>(qsOfCat);
        sorted.Attributes = "EntityTitle";
        Data.In.Add("event", sorted["Default"]);
    }
}

<h2 class="sc-element">@Content.Title</h2>
<p>@Html.Raw(Content.Introduction)</p>

@foreach (var cont in AsDynamic(Data["event"]))
{
    <div class="entry">
             <h2>@cont.EntityTitle</h2>

        @if(cont.categories != null)
             {
                 <p><strong>Categories: </strong>
                    @{ int counter=0; }
                    @foreach(var item in cont.categories){
                        counter++;
                        @item.name
                        if(counter < cont.categories.Count) {@Html.Raw(", ")}
                 }
                </p>
                
            } else {<text>no categories</text>}

    </div>
}
Coordinator
May 25, 2016 at 6:19 AM
Your question regarding does-the-filter-datasource-require-the-entity-title - I believe currently this is the case. The filter component could use some extra love though - care to contribute :)?

regarding your error I would need more details - like what line causes the trouble.
May 26, 2016 at 11:29 PM
Here is the full error message I'm getting:

Error: System.Web.HttpCompileException (0x80004005): q:\dev-dnn.calarts.edu\Portals\0\2sxc\scsmith-calendar_All events of preselected multiple categories.cshtml(14): error CS1977: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type at System.Web.Compilation.AssemblyBuilder.Compile() at System.Web.Compilation.BuildProvidersCompiler.PerformBuild() at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath) at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(VirtualPath virtualPath, HttpContext context, Boolean allowCrossApp, Boolean throwIfNotFound) at System.Web.Compilation.BuildManager.GetCompiledType(VirtualPath virtualPath) at ToSic.SexyContent.Engines.RazorEngine.CreateWebPageInstance() in C:\Projects\2SexyContent\Web\DesktopModules\ToSIC_SexyContent\2Sexy Content Razor\RazorEngine.cs:line 81 at ToSic.SexyContent.Engines.RazorEngine.InitWebpage() in C:\Projects\2SexyContent\Web\DesktopModules\ToSIC_SexyContent\2Sexy Content Razor\RazorEngine.cs:line 103 at ToSic.SexyContent.Engines.RazorEngine.Init() in C:\Projects\2SexyContent\Web\DesktopModules\ToSIC_SexyContent\2Sexy Content Razor\RazorEngine.cs:line 27 at ToSic.SexyContent.Engines.EngineBase.Init(Template template, App app, ModuleInfo hostingModule, IDataSource dataSource, InstancePurposes instancePurposes, SxcInstance sexy) in C:\Projects\2SexyContent\Web\DesktopModules\ToSIC_SexyContent\SexyContent\Engines\EngineBase.cs:line 60 at ToSic.SexyContent.SxcInstance.GetRenderingEngine(InstancePurposes renderingPurpose) in C:\Projects\2SexyContent\Web\DesktopModules\ToSIC_SexyContent\SexyContent\SxcInstance.cs:line 244 at ToSic.SexyContent.SxcInstance.Render() in C:\Projects\2SexyContent\Web\DesktopModules\ToSIC_SexyContent\SexyContent\SxcInstance.cs:line 206

And here is line 14 of _All events of preselected multiple categories.cshtml:
where q.Any(item=> item.categories.Contains(Content.Category))
Here is my full template code again for reference:
@using ToSic.Eav.DataSources
@using ToSic.SexyContent
@functions{
    // FOR MULTIPLE CATEGORIES
        
    // requires category setting in content item
    // want to make this work for selecting multiple categories 
    
    // Prepare the data - get all categories through the pipeline
    public override void CustomizeData()
    {
    var qsOfMultipleCats = from q in AsDynamic(App.Data["event"])
         where q.Any(item=> item.categories.Contains(Content.Category))
         select q;     
                                 
        var sorted = CreateSource<ValueSort>(qsOfMultipleCats);
        sorted.Attributes = "EntityTitle";
        Data.In.Add("event", sorted["Default"]);
    }
    
}

<h2 class="sc-element">@Content.Title @Content.Toolbar</h2>
<p>@Html.Raw(Content.Introduction)</p>
<div>
     @if(Content.Category != null)
       {
           <p><strong>Selected Category: </strong>
              @* counter to count iterations *@
              @{ int counter=0; }
              @foreach(var item in Content.Category){
                  counter++;
                  @item.name
                  if(counter < Content.Category.Count) {@Html.Raw(", ")}
           }
          </p>
          
      } else {<text>No category selected</text>}
</div>

    @foreach (var cont in AsDynamic(Data["event"]))
    {
    <div class="entry">
             @cont.Toolbar
             <h2>@cont.EntityTitle</h2>

        
        @if(cont.categories != null)
             {
                 <p><strong>Categories: </strong>
                    @* counter to count iterations *@
                    @{ int counter=0; }
                    @foreach(var item in cont.categories){
                        counter++;
                        @item.name
                        if(counter < cont.categories.Count) {@Html.Raw(", ")}
                 }
                </p>
                
            } else {<text>no categories</text>}
            
       </div>
    }