Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a virtual node in Umbraco

Situation

I have created a new section in Umbraco 7.3 where I can manage a catalogue (create, edit, and delete products). When I create a product, I store all its information in a table in the Umbraco database. Please note that I do not create a node in the tree, I just work directly with the database.

Question

For example, when I create the "Product A" in that catalogue, I automatically generate and store a custom URL "/products/product-A" in a table in the database. However, that URL does not exist as I do not have a physical node:

Page not found

No umbraco document matches the url '/products/product-A'.

As I do not have a physical node for it, how can I "create" a virtual node and assign a template for that URL?

like image 302
Felipe Cruz Avatar asked Feb 26 '16 08:02

Felipe Cruz


2 Answers

I could figure this out at the end. Thank you for your answers!

1. Create a class for our virtual page

First thing is to create a class i.e ProductVirtualPage which inherits from PublishedContentWrapped. This last class provides an abstract base class for IPubslihedContent implementations that wrap and extender another IPublishedContent. We will use the property Product to get the Product from the Razor view and render.

public class ProductVirtualPage : PublishedContentWrapped
{   
  private readonly Product _product;

  public ProductVirtualPage(IPublishedContent content, Product product ) : base(content)
  {
     if (product.Name == null) throw new ArgumentNullException("productName");
     _product = product;
  }

   public Product Product
   {
       get
       {
           return _product;
       }
   }
 }

2. Create our handler based on UmbracoVirtualNodeRouteHandler

In the tree we need to create a reference node, which in my case it has the id 3286. We will pass it to the ProductVirtualPage(nodeReference, product) method when the FindContent() method is called.

public class ProductVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
{
    private ProductService _productService;

    protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
    {
        // Get the real Umbraco IPublishedContent instance as a reference point
        var nodeReference = new UmbracoHelper(umbracoContext).TypedContent(3286);

        //Get the product Id from the URL (http://example.com/Products/product/57)
        var productId = umbracoContext.HttpContext.Request.Url.Segments[3];

        // Create an instance for the ProductService
        _productService = new ProductService(new ProductRepository(), new SiteRepository());

        // Get the product from the database
        var product = _productService.GetById(Convert.ToInt32(productId));

        // Get the virtual product
        return  new ProductVirtualPage(nodeReference, product);
    }
}

3. Register our custom route

We need to add our custom route using MapUmbracoRoute and bind it to the ApplicationStarted method that provides ApplicationEventHandler.

public class ContentServiceEventsController : ApplicationEventHandler
{

    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication,
        ApplicationContext applicationContext)
    {
        // Add a route for the Products
        RouteTable.Routes.MapUmbracoRoute(
            "ProductPage",
            "Products/Product/{id}/",
            new
            {
                controller = "Product",
                action = "GetProduct"
            },
            new ProductVirtualNodeRouteHandler());
    }
}

4. Create the controller

The controller will just return the View, passing our RenderModel model.

public class ProductController : RenderMvcController
{
    public ActionResult GetProduct(RenderModel model, int id)
    {
        return View("Product", model);
    }
}

5. The view

@{
  Layout = "~/Views/Shared/_MasterLayout.cshtml";
  var productModel = Model.Content.Product;
  var product = productModel as Product; 
}
<pre>
  Product Info
  ---------------

  Id: @product.Id
  Name: @product.Name
</pre>
like image 182
Felipe Cruz Avatar answered Sep 28 '22 11:09

Felipe Cruz


It sounds like you don't need your product urls to be handled by Umbraco.

You need to add a custom route into your RouteConfig.cs file e.g.

routes.MapRoute(
  name: "Products",
  url: "Products/{id}",
  defaults: new { controller = "Products", action = "Product", id = UrlParameter.Optional }
);

and then create a ProductsController

public class ProductsController : Controller
{
    public ActionResult Product(int id)
    {
        // Retrieve product from database
        // Return view
    }

If you need access to the UmbracoContext, there is a good example here: http://shazwazza.com/post/Custom-MVC-routing-in-Umbraco

like image 43
Electric Sheep Avatar answered Sep 28 '22 12:09

Electric Sheep