Tip of the Day - Extend Liferay Models With Custom Fields

The Expando API is a great tool to add custom data to the default models Liferay offers. We've used it regularly in our projects. Although the concept might overwhelm you at first, there’s no need to be scared. Let me show you around...

Liferay’s models offer you a basic interface to use. For instance, when interacting with a User class, you can easily get the name, email address or the screen name of the user. These attributes are delivered as is, with no easy way to extend the model itself. Luckily Liferay exposes the Expando service with which you can add and edit custom fields related to Liferay's very own models.

You can add the expando fields through a graphical user interface, which is quite simple and easy to use. In Liferay 6.2 this is done in the Control Panel under the Custom Fields menu. After you’ve created the fields, you can edit the values while editing the model itself. This happens, surprisingly, under the tab called "Custom Fields".

 

Custom fields can be added manually in control panel

Custom fields can be added manually

The interesting part, at least for me, is how to add custom fields programmatically. Here’s a little 101 on the subject.

First of all, you need the ExpandoTable entity, so we’ll add it, or if added already return the previously added table.

private ExpandoTable getTable(long companyId, Class<? extends ClassedModel> model) throws SystemException, PortalException {
    ExpandoTable table;
    try {
        table = ExpandoTableLocalServiceUtil.addDefaultTable(companyId, model.getName());
    } catch (DuplicateTableNameException dtne) {
        table = ExpandoTableLocalServiceUtil.getDefaultTable(companyId, model.getName());
    }
    return table;
}

The second step is adding the ExpandoColumn to the previously fetched table. Note that the model passed as a parameter to the method is not really needed - it is used just for logging purposes.

private void addExpandoColumn(long companyId, Class<? extends ClassedModel> model, ExpandoTable table, String columnName)
        throws SystemException, PortalException {
    
    ExpandoColumn column;
    try {
        column = addColumn(table, columnName);
        _log.info("Custom field " + columnName + " added for model " + model.getName());
    } catch (DuplicateColumnNameException dcne) {
        column = getColumn(table, columnName);
        _log.debug("Custom field " + columnName + " already exists for model " + model.getName());
    }
    updateTypeSettings(column);
}
    
private ExpandoColumn addColumn(ExpandoTable table, String columnName)
        throws PortalException, SystemException {
    final ExpandoColumn column = ExpandoColumnLocalServiceUtil.addColumn(table.getTableId(), columnName, ExpandoColumnConstants.STRING);
    return column;
}

In the end we’ll update the ExpandoColumn’s TypeSettings to define some further properties the column should have. You can check what Liferay is doing with the TypeSettings by editing the properties on the GUI and checking the results from the database, table called ExpandoColumn. This should give you a good idea of what's possible with the settings.

private void updateTypeSettings(final ExpandoColumn column)
        throws PortalException, SystemException {
    UnicodeProperties properties = column.getTypeSettingsProperties();
    // update the properties here
    ExpandoColumnLocalServiceUtil.updateTypeSettings(column.getColumnId(), properties.toString());
}

As I said in the beginning, there is no need to be scared. Just experiment with your code and you’ll be the master of Expando in no time!