Documentation

Adding a New Product Data Source for the Product Publisher Widget

This tutorial will show you how to add a new product data source by implementing the CPDataSource interface.

Product data sources provide unique ways to search for products that are related. Liferay Commerce provides several product data sources out-of-the-box, including ones that search by product relations and by categories.

Out-of-the-box product data sources

Deploy an Example

In this section, we will get an example product data source up and running on your instance of Liferay Commerce.

Start a new Liferay instance by running

docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.22-ga22

Sign in to Liferay at http://localhost:8080. Use the email address test@liferay.com and the password test. When prompted, change the password to learn.

Then, follow these steps:

  1. Download and unzip the Acme Commerce Product Data Source.

    curl https://learn.liferay.com/commerce/latest/en/developer-guide/content/liferay-m5x7.zip -O
    
    unzip liferay-m5x7.zip
    
  2. Build and deploy the example.

    ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    

    Note

    This command is the same as copying the deployed jars to /opt/liferay/osgi/modules on the Docker container.

  3. Confirm the deployment in the Liferay Docker container console.

    STARTED com.acme.m5x7.impl_1.0.0
    
  4. Verify that the example product data source was added. Open your browser to https://localhost:8080 and navigate to a page with a Product Publisher widget. Click Configuration for the Product Publisher, then select Data Source under the Product Selection section. The new product data source (“Products Ending in the Same Word”) will be present under the Data Source dropdown below.

New product data source

Congratulations, you’ve successfully built and deployed a new product data source that implements CPDataSource.

Next, let’s dive deeper to learn more.

Walk Through the Example

In this section, we will review the example we deployed. First, we will annotate the class for OSGi registration. Second, we will review the CPDataSource interface. And third, we will complete our implementation of CPDataSource.

Annotate Your Class for OSGi Registration

@Component(
    property = "commerce.product.data.source.name=m5x7",
    service = CPDataSource.class
)
public class M5X7CPDataSource implements CPDataSource {

The product data source name must be a unique value so that Liferay Commerce can distinguish the new data from existing data sources.

Review the CPDataSource Interface

Implement the following methods:

public String getLabel(Locale locale);

This method returns a text label that describes how product data source will search for related products. See the implementation in M5X7CPDataSource.java for a reference in retrieving the label with a language key.

public String getName();

This returns the name of the product data source.

public CPDataSourceResult getResult(
        HttpServletRequest httpServletRequest, int start, int end)
    throws Exception;

This will be where we add the business logic to perform the search for related products. The HttpServletRequest contains a reference to a particular product which the results should be related to in some way.

The method will return a CPDataSourceResult, which contains a list of the search results; see the implementation at CPDataSourceResult.java.

Complete the Product Data Source

The product data source is comprised of logic to perform a search for related products. Do the following:

Add the Search Logic to getResult

@Override
public CPDataSourceResult getResult(
        HttpServletRequest httpServletRequest, int start, int end)
    throws Exception {

    CPCatalogEntry cpCatalogEntry =
        (CPCatalogEntry)httpServletRequest.getAttribute(
            CPWebKeys.CP_CATALOG_ENTRY);

    if (cpCatalogEntry == null) {
        return new CPDataSourceResult(new ArrayList<>(), 0);
    }

    return _cpDefinitionHelper.search(
        _portal.getScopeGroupId(httpServletRequest),
        new SearchContext() {
            {
                setAttributes(
                    HashMapBuilder.<String, Serializable>put(
                        Field.STATUS, WorkflowConstants.STATUS_APPROVED
                    ).put(
                        "excludedCPDefinitionId",
                        cpCatalogEntry.getCPDefinitionId()
                    ).build());
                setCompanyId(_portal.getCompanyId(httpServletRequest));
                setKeywords(
                    StringPool.STAR + _getLastWordOfName(cpCatalogEntry));
            }
        },
        new CPQuery(), start, end);
}

We use a CPDefinitionHelper to perform the search. The CPDefinitionHelper combines logic specific to product definitions with BaseIndexer’s search functionality; see BaseIndexer.java for more information.

Add the product definition’s ID as the value for the "excludedCPDefinitionId" attribute to the SearchContext. This will omit the original product from the results. In our example, we also specify the last word of the product name to search for. For details, see the search logic implementation in the example M5X7CPDataSource.java file’s _getLastWordOfName method.

Add the Language Key to Language.properties

Add the language key and its value to a Language.properties file within our module:

products-ending-in-the-same-word=Products Ending in the Same Word

See Localizing Your Application for more information.

Conclusion

Congratulations! You now know the basics for implementing the CPDataSource interface, and have added a new product data source to Liferay Commerce.