Appearance
Doofinder Search Integration
Flow ID: IN-10 | Module(s):
ecommercen/doofinder/,src/Feeds/Output/| Complexity: Medium
Business Overview
Doofinder is an external SaaS search platform that powers on-site search for selected Ecommercen clients. Unlike Solr (which indexes data locally), Doofinder operates on a pull-based model: it periodically fetches XML feeds from the shop. Ecommercen exposes three dedicated XML feeds -- products, blog articles, and vendors -- that Doofinder crawls on a configured schedule.
The product feed is enriched with Greek-to-Greeklish transliterations (and reverse), best-seller flags, top-stock indicators, product line names, tag attributes, and full category hierarchies. This enables Doofinder to provide autocomplete, typo-tolerant, and language-flexible search results in the storefront.
API Reference
Feed Endpoints (Public XML)
| Endpoint | Controller | Root Element | Item Element |
|---|---|---|---|
GET /xml/doofinder/products | AdvProductsXml::index() | <products> | <product> |
GET /xml/doofinder/blog | AdvBlogXml::index() | <blog> | <article> |
GET /xml/doofinder/vendors | AdvVendorsXml::index() | <products> | <product> |
All endpoints support language-prefixed routes: /{lang}/xml/doofinder/products (e.g., /en/xml/doofinder/products).
Product Feed Fields
| Field | Source | Description |
|---|---|---|
id / product_id | shop_product.id | Product identifier |
title | shop_product_mui.name | Localized product name |
link | Generated | Full product URL with currency |
image_link | shop_product images | Main product image, resized to w280 |
price / sale_price | Computed | Original and final prices with currency rate applied |
availability | Stock check | in stock or out of stock (respects negative_stock) |
gtin | product_codes | Comma-separated barcodes |
brand | shop_vendor_mui.name | Vendor/brand name |
line | Product lines | Product line name (if any) |
product_code | product_codes | SKU |
title_el_to_en | Computed | Greek-to-Greeklish transliteration of name |
title_en_to_el | Computed | English-to-Greek transliteration of name |
best_seller | Top 500 sales | yes / no |
top_stock | Top 500 stock | yes / no |
attributes | Product tags | Tags joined with / separator |
category | Category hierarchy | Full category path(s) with > separator, %%-delimited |
category_en_to_el | Computed | Greeklish version of category paths |
Vendor Feed Fields
| Field | Source | Description |
|---|---|---|
id | VENDOR_{id} | Prefixed vendor identifier |
title / brand / brand_name | shop_vendor_mui.name | Vendor name in 3 fields |
link | Generated | Vendor page URL |
image_link | shop_vendor.logo | Vendor logo, resized to w280 |
items | Computed | Active product count for vendor |
*_el_to_en / *_en_to_el | Computed | Transliterations for all text fields |
Blog Feed Fields
| Field | Source | Description |
|---|---|---|
id | blog.id | Blog post identifier |
title | blog_mui.title | Localized title |
link | blog_mui.url or slug | Blog post URL |
image | blog.banner_image | Banner image, resized to w280 |
description | blog_mui.small_description | Plain-text excerpt |
title_el_to_en / title_en_to_el | Computed | Transliterations |
Code Flow
Product Feed Generation
HTTP GET /xml/doofinder/products
-> AdvProductsXml::index()
-> getProductData() (via AdvXml base class, applies stock/pricing)
-> parseForXml($products)
-> AdvDoofinderModel::getCategoriesCombo() (cached via pscache)
-> For each product:
-> parseItem($product, $categories)
-> getMainProductImage(), resizeImageOnTheFly()
-> applyCurrencyRate() for price/sale_price
-> convertGreekToGreeklish(), convertEnglishToGreek()
-> Map category IDs to full hierarchy paths
-> OutputDoofinderXml::createXml($data)
-> Feed::output() (sets Content-Type: text/xml, outputs)Data Enrichment (in Model)
AdvDoofinderModel::getProducts()
-> Raw SQL query joining shop_product, product_codes, shop_product_mui, shop_vendor, shop_vendor_mui
-> Subqueries for: categories_ids, variations, product_tags
-> Filters: price > 0, soft_delete = 0, active = 1
-> Post-processing:
-> getTopStockProductsIds(500) -> marks top_stock = yes/no
-> getBestSellersWithLimit(500) -> marks best_seller = yes/no (cached)
-> getProductsLines() -> attaches product line namesCategory Hierarchy Building
AdvDoofinderModel::getCategoriesCombo()
-> Fetches all categories with parent_id, name, published status
-> buildHierarchy(0, categories) recursively
-> Builds "Parent > Child > Grandchild" paths
-> Skips unpublished categories and their descendants
-> Returns [category_id => fullPath] mapArchitecture
ecommercen/doofinder/
controllers/
AdvProductsXml.php # Product feed controller (extends AdvXml)
AdvBlogXml.php # Blog feed controller (extends AdvXml)
AdvVendorsXml.php # Vendor feed controller (extends AdvXml)
models/
AdvDoofinderModel.php # Core model with SQL queries and enrichment logic
application/modules/doofinder/
models/
Doofinder_model.php # Client-overridable model (extends AdvDoofinderModel, empty)
src/Feeds/Output/
OutputDoofinderXml.php # XML output formatter (extends OutputGenericXml)Class Hierarchy
- Controllers:
AdvProductsXml/AdvBlogXml/AdvVendorsXmlall extendAdvXml(the shared feed base controller inecommercen/feeds/core/), which provides stock resolution, currency handling, XML config, and caching. - Model:
AdvDoofinderModelextendsAdv_base_model. Theapplication/modules/doofinder/models/Doofinder_model.phpis an empty extension point for client overrides. - Output:
OutputDoofinderXmlextendsOutputGenericXml, providing the standard<?xml ... ?>envelope with configurable root/item element names.
Key Difference from Solr
| Aspect | Doofinder | Solr |
|---|---|---|
| Type | External SaaS | Internal search engine |
| Data flow | Pull (Doofinder fetches XML) | Push (app indexes to Solr) |
| Setup | Feed URLs configured in Doofinder dashboard | Solr server configured in .env |
| Deployment | No server infrastructure needed | Requires Solr server |
Data Model
Tables Queried
| Table | Role |
|---|---|
shop_product | Core product data (price, stock, active, soft_delete) |
shop_product_mui | Localized product name, slug, description |
shop_vendor / shop_vendor_mui | Vendor/brand information |
product_codes | Product SKU/barcodes |
shop_product_category / shop_product_category_mui | Category hierarchy |
shop_product_category_lp | Product-to-category link table |
shop_product_tags / shop_product_tags_mui | Product tags |
shop_product_product_tags | Product-to-tag link table |
shop_product_tag_categories / shop_product_tag_categories_mui | Tag category metadata |
product_variation_values | Variation data (used for grouping) |
blog / blog_mui | Blog posts |
Configuration
Routes (application/config/routes.php)
php
$route['xml/doofinder/products'] = 'doofinder/productsXml/index';
$route['xml/doofinder/blog'] = 'doofinder/blogXml/index';
$route['xml/doofinder/vendors'] = 'doofinder/vendorsXml/index';
// Language-prefixed variants
$route['(\w{2})/xml/doofinder/products'] = 'doofinder/productsXml/index';
$route['(\w{2})/xml/doofinder/blog'] = 'doofinder/blogXml/index';
$route['(\w{2})/xml/doofinder/vendors'] = 'doofinder/vendorsXml/index';Caching
Category hierarchies are cached via pscache with the L2 default TTL. Best-seller product IDs are also cached at the L2 level. No Doofinder-specific registry or .env keys are required -- the feeds are public XML endpoints.
Client Extension Points
Model Override
Create application/modules/doofinder/models/Doofinder_model.php extending AdvDoofinderModel to customize:
- Product query SQL (override
getProducts()) - Category hierarchy building (override
buildHierarchy()orgetCategoriesCombo()) - Blog/vendor queries (override
getBlogs(),getVendors())
Controller Override
Create controllers in application/modules/doofinder/controllers/ extending the Adv*Xml classes to customize:
- Feed field mapping (override
parseItem()) - Additional data enrichment (override
parseForXml()) - XML configuration (override
getXmlConfig()-- seeAdvBlogXmlfor custom root/item names)
Feed Output Override
The OutputDoofinderXml class can be extended in custom/Feeds/Output/ for structural XML changes.
Business Rules
- Product inclusion: Only products with
price > 0,active = 1,soft_delete = 0are included. - Best sellers: Top 500 products by sales volume are flagged
best_seller = yes. - Top stock: Top 500 products by stock quantity are flagged
top_stock = yes. - Category paths: Only published categories (and their published ancestors) appear in the hierarchy. Unpublished categories cause their entire subtree to be excluded.
- Language support: Feed content is language-aware via
shop_*_muitables. Greek/Greeklish transliterations are always generated regardless of the active language. - Currency: Product prices are converted using the active currency rate (
applyCurrencyRate()). - Blog filtering: Only published blog posts with
blog_date <= todayare included. - Vendor filtering: Only vendors with at least one active, priced product (
HAVING items > 0) are included. Maximum discount across all vendor products is computed.
Related Flows
- CF-04 Search -- Solr-based internal search (alternative to Doofinder)
- IN-01 Feed Generation -- General feed framework shared by all XML feeds
- AD-10 Reporting -- Admin dashboard analytics