#961 Optional Falang support for product keyword search

Posted in ‘Feature Requests’
This is a public ticket

Everybody will be able to see its contents. Do not include usernames, passwords or any other sensitive information.

J2Commerce

Version
J2Commerce v6

Latest post by Adam Melcher on Monday, 17 August 2026 14:41 UTC

Krzysztof Kuznowicz

Product keyword search in ProductsModel::getListQuery() currently checks only the original Joomla content fields (a.title, a.introtext) together with the product SKU and UPC.

As a result, products cannot be found using titles or descriptions translated by Falang. For example, a product whose original title contains “srebro” but whose English translation contains “silver” cannot be found by searching for “silver”.

Would it be possible to add either:

  1. Optional Falang support controlled through the J2Commerce configuration, or

  2. An event allowing plugins to extend the product keyword search condition?

An extension event would keep J2Commerce completely independent from Falang. However, direct optional support could also be implemented safely and remain disabled by default.

The integration should search only the currently active language, not every available translation.

Suggested behaviour
  • Falang search is disabled by default.

  • Existing J2Commerce search behaviour remains unchanged when disabled.

  • When enabled, the search checks the original product content and the active Falang translation.

  • Only translated title and introtext fields are searched.

  • If Falang is unavailable, J2Commerce falls back to its native search without an error.

  • The same behaviour applies to normal product lists and tag-based product lists.

Suggested implementation

 

1. Add a configuration option

File:

administrator/components/com_j2commerce/config.xml

Add the field to the appropriate product or search configuration fieldset:

<field
    name="enable_falang_search"
    type="radio"
    layout="joomla.form.field.radio.switcher"
    default="0"
    label="COM_J2COMMERCE_CONFIG_FALANG_SEARCH_LABEL"
    description="COM_J2COMMERCE_CONFIG_FALANG_SEARCH_DESC"
>
    <option value="0">JNO</option>
    <option value="1">JYES</option>
</field>
2. Add language strings
COM_J2COMMERCE_CONFIG_FALANG_SEARCH_LABEL="Search Falang translations"
COM_J2COMMERCE_CONFIG_FALANG_SEARCH_DESC="Include product titles and short descriptions translated by Falang in keyword search. Only translations for the active site language are searched."
3. Update both product list models

The change is required in both files:

components/com_j2commerce/src/Model/ProductsModel.php
components/com_j2commerce/src/Model/ProducttagsModel.php

ProductModel.php does not need to be changed because it loads a single product and does not build the product search query.

In both list models, replace the current search block:

// Search filter - searches title, SKU, UPC, and description
$search = $this->getState('filter.search');

if (!empty($search)) {
    $search = '%' . str_replace(' ', '%', trim($search)) . '%';

    $query->where(
        '(' . $db->quoteName('a.title') . ' LIKE :search1'
        . ' OR ' . $db->quoteName('v.sku') . ' LIKE :search2'
        . ' OR ' . $db->quoteName('v.upc') . ' LIKE :search3'
        . ' OR ' . $db->quoteName('a.introtext') . ' LIKE :search4)'
    )
        ->bind(':search1', $search)
        ->bind(':search2', $search)
        ->bind(':search3', $search)
        ->bind(':search4', $search);
}

with:

// Search native product data and optionally the active Falang translation.
$search = trim((string) $this->getState('filter.search'));

if ($search !== '') {
    $searchPattern = '%' . str_replace(' ', '%', $search) . '%';

    $conditions = [
        $db->quoteName('a.title') . ' LIKE :search1',
        $db->quoteName('v.sku') . ' LIKE :search2',
        $db->quoteName('v.upc') . ' LIKE :search3',
        $db->quoteName('a.introtext') . ' LIKE :search4',
    ];

    $params = $this->getState('params');
    $falangEnabled = \is_object($params)
        && method_exists($params, 'get')
        && (bool) $params->get('enable_falang_search', 0);

    if ($falangEnabled && $this->isFalangAvailable()) {
        $languageTag = Factory::getApplication()
            ->getLanguage()
            ->getTag();

        $falangQuery = $db->getQuery(true)
            ->select('1')
            ->from($db->quoteName('#__falang_content', 'fc'))
            ->join(
                'INNER',
                $db->quoteName('#__languages', 'fl')
                    . ' ON ' . $db->quoteName('fl.lang_id')
                    . ' = ' . $db->quoteName('fc.language_id')
            )
            ->where(
                $db->quoteName('fc.reference_id')
                    . ' = ' . $db->quoteName('a.id')
            )
            ->where(
                $db->quoteName('fc.reference_table')
                    . ' = ' . $db->quote('content')
            )
            ->where(
                $db->quoteName('fc.reference_field')
                    . ' IN ('
                    . $db->quote('title')
                    . ', '
                    . $db->quote('introtext')
                    . ')'
            )
            ->where($db->quoteName('fc.published') . ' = 1')
            ->where(
                $db->quoteName('fl.lang_code')
                    . ' = :falangLanguage'
            )
            ->where(
                $db->quoteName('fc.value')
                    . ' LIKE :falangSearch'
            );

        $conditions[] = 'EXISTS (' . $falangQuery . ')';

        // The Falang subquery is embedded in the parent query as SQL,
        // therefore its placeholders are bound on the parent query.
        $query
            ->bind(
                ':falangLanguage',
                $languageTag,
                ParameterType::STRING
            )
            ->bind(
                ':falangSearch',
                $searchPattern,
                ParameterType::STRING
            );
    }

    $query
        ->where('(' . implode(' OR ', $conditions) . ')')
        ->bind(':search1', $searchPattern, ParameterType::STRING)
        ->bind(':search2', $searchPattern, ParameterType::STRING)
        ->bind(':search3', $searchPattern, ParameterType::STRING)
        ->bind(':search4', $searchPattern, ParameterType::STRING);
}

Both models already import Factory and ParameterType, so no additional imports should be required.

4. Check whether Falang is available

Add the following method near the end of both list model classes:

/**
 * Check whether the Falang content table is available.
 *
 * The result is cached for the duration of the request because
 * getListQuery() can be called more than once when filters are built.
 */
private function isFalangAvailable(): bool
{
    static $available;

    if ($available !== null) {
        return $available;
    }

    $db        = $this->getDatabase();
    $tableName = $db->replacePrefix('#__falang_content');

    $available = \in_array(
        $tableName,
        $db->getTableList(),
        true
    );

    return $available;
}

This prevents SQL errors if Falang is removed while the configuration option remains enabled.

5. Include the option in the model cache key

In getStoreId() in both models, add the Falang setting before:

return parent::getStoreId($id);

Suggested code:

$params = $this->getState('params');

$falangSearchEnabled = \is_object($params)
    && method_exists($params, 'get')
    ? (int) $params->get('enable_falang_search', 0)
    : 0;

$id .= ':' . $falangSearchEnabled;

This ensures that cached model results cannot be shared between requests using different Falang search settings.

Possible alternative

Instead of implementing Falang-specific SQL in both models, J2Commerce could dispatch an event while building the search condition. A Falang integration plugin could then append the translated-content condition and required query bindings.

That would avoid a direct Falang dependency and could later support other translation extensions. If direct support is preferred, the configuration-controlled implementation above keeps the feature optional and preserves the current search behaviour for stores that do not use Falang.

 

best regards
KK

Krzysztof Kuznowicz

My store on J2Commerce is almost ready, and I think it will be up and running at full capacity starting in September after the migration from Virtuemart ;-)

KK

Adam Melcher

Krzysztof,

I think the best method would be to add event hooks to allow 3rd party developers to update the placeholder text and hook into search results for translation plugins or other elements such as including search results that could extend to 3rd party attributes linked to products. I will look into this.

 

Adam

Growing Together

Help us get back to #1 in the Joomla Extension Directory by leaving us a 5-Star review here.

Stay Updated

Subscribe for free and be the first to know about the latest features, updates, and new additions.