Skip to content
ms3Favorites
ms3Favorites
Wishlists for MiniShop3 and other resources — browser storage, DB sync
  1. Extras
  2. ms3Favorites
  3. Quick start

Quick start

Step-by-step setup of the wishlist on a MiniShop3 site, similar to MyFavorites: snippets for the button, counter, IDs, and list output without PHP in the template.

Snippet names: ms3FavoritesBtn, ms3FavoritesCounter, ms3FavoritesIds, ms3FavoritesLists.

Fenom examples below assume pdoTools 3.x.

Installation

Requirements

RequirementVersion
MODX Revolution3.0+
PHP8.1+
MiniShop3installed
pdoTools3.0.0+

Via ModStore

  1. Connect ModStore repository
  2. Go to Extras → Installer and click Download Extras
  3. Ensure MiniShop3 and pdoTools are installed
  4. Find ms3Favorites, click Download, then Install
  5. Settings → Clear cache

Package is available at modstore.pro.

After installation

Load lexicon, CSS and JS on the site, add the button on the product card and output the wishlist block. Details below.


Step 1: Lexicon, styles and script

In the template (or shared head/footer), load first the lexicon, then CSS and JS.

fenom
{'ms3fLexiconScript' | snippet}
<link rel="stylesheet" href="{'assets_url' | config}components/ms3favorites/css/favorites.css">
<script src="{'assets_url' | config}components/ms3favorites/js/favorites.js"></script>
modx
[[!ms3fLexiconScript]]
<link rel="stylesheet" href="[[++assets_url]]components/ms3favorites/css/favorites.css">
<script src="[[++assets_url]]components/ms3favorites/js/favorites.js"></script>

Fenom and auto_escape

If the page goes blank after adding these lines, check the MODX log for ms3fLexiconScript errors. With Fenom auto_escape enabled, output the snippet as raw HTML, e.g. {raw ('ms3fLexiconScript' | snippet)} (exact syntax depends on your Fenom version).

Without ms3fLexiconScript, lexicon keys may show instead of translated strings; the JS still works with built-in Russian fallbacks.

Step 2: Add to Wishlist button

Add the button on the product card — snippet ms3FavoritesBtn:

modx
[[!ms3FavoritesBtn? &id=`[[*id]]`]]
fenom
{'!ms3FavoritesBtn' | snippet : ['id' => $_modx->resource.id]}

In the product card chunk (e.g. inside msProducts output):

modx
[[!ms3FavoritesBtn? &id=`[[+id]]`]]
fenom
{'!ms3FavoritesBtn' | snippet : ['id' => $id]}

Reload page after remove (optional):

modx
[[!ms3FavoritesBtn? &id=`[[*id]]` &remove=`1`]]
fenom
{'!ms3FavoritesBtn' | snippet : ['id' => $_modx->resource.id, 'remove' => 1]}

Wishlist-box layout (li.wishlist, box-icon, icon-heart, tooltip) — chunk tplMs3fBtnWishlistBox:

modx
[[!ms3FavoritesBtn? &id=`[[*id]]` &tpl=`tplMs3fBtnWishlistBox`]]
fenom
{'!ms3FavoritesBtn' | snippet : ['id' => $_modx->resource.id, 'tpl' => 'tplMs3fBtnWishlistBox']}

Remove a wrapper by ID prefix (e.g. #product-item-{id}):

modx
<div id="product-item-[[+id]]">
  ...
  [[!ms3FavoritesBtn? &id=`[[+id]]` &remove=`product-item`]]
</div>
fenom
<div id="product-item-{$id}">
  ...
  {'!ms3FavoritesBtn' | snippet : ['id' => $id, 'remove' => 'product-item']}
</div>

Snippet parameters: list, tpl, remove, classes, resource_type, label — see ms3FavoritesBtn.

On click the product is added or removed from the list. A notification is shown automatically (iziToast / MiniShop3 ms3Message / your own ms3fConfig.notify — see Integration).

Step 3: Wishlist counter

Client-side (JS fills the value on load):

html
<a href="/wishlist/">
  <span>Wishlist</span>
  <span data-favorites-count style="display: none;">0</span>
</a>

Limit the counter to one resource_type (e.g. only products): <span data-favorites-count data-resource-type="products"></span>.

Server snippet ms3FavoritesCounter:

modx
[[!ms3FavoritesCounter]]
fenom
{'!ms3FavoritesCounter' | snippet}
modx
<a href="/wishlist/">Wishlist [[!ms3FavoritesCounter]]</a>
fenom
<a href="/wishlist/">Wishlist {'!ms3FavoritesCounter' | snippet}</a>

Total across all lists: &list=all / ['list' => 'all'].

The value is set on load (1–99 or 99+); the element is hidden when zero.

Step 4: Wishlist block

Client-side render (JS):

Use class ms3f__list for flex layout and horizontal scroll on small screens. The container is filled from localStorage/cookie via the connector:

html
<div id="wishlist-list" class="ms3f__list"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
  if (window.ms3Favorites) {
    window.ms3Favorites.render('#wishlist-list');
  }
});
</script>

Optional render options: limit, tpl, emptyTpl, list, resource_type — same names as for the connector.

Server output:

Get favorite IDs into a placeholder — snippet ms3FavoritesIds:

modx
[[!ms3FavoritesIds? &toPlaceholder=`favorites_ids`]]
[[!+favorites_ids:is=`-0`:then=`
  <p>[[%ms3favorites_empty]]</p>
`:else=`
  [[!ms3Favorites?
    &ids=`[[+favorites_ids]]`
    &tpl=`tplFavoritesItem`
    &emptyTpl=`tplFavoritesEmpty`
  ]]
`]]
fenom
{'!ms3FavoritesIds' | snippet : ['toPlaceholder' => 'favorites_ids']}
{set $idsStr = $_modx->getPlaceholder('favorites_ids')}
{if $idsStr == '-0'}
  <p>{$_modx->lexicon('ms3favorites_empty')}</p>
{else}
  {'!ms3Favorites' | snippet : [
    'ids' => $idsStr,
    'tpl' => 'tplFavoritesItem',
    'emptyTpl' => 'tplFavoritesEmpty'
  ]}
{/if}

List of named lists (tabs / menu)

Like MyFavorites.lists: snippet ms3FavoritesLists outputs the user’s lists with counts. Links use System setting ms3favorites.list_page (default wishlist/).

modx
[[!ms3FavoritesLists? &tplWrapper=`tplMs3fListsWrapper`]]
fenom
{'!ms3FavoritesLists' | snippet : ['tplWrapper' => 'tplMs3fListsWrapper']}

Without tplWrapper you get only rows from tplMs3fListsRow.

Custom paginated favorites (pdoPage + msProducts)

Use this on a separate resource if you need server-side pagination — not the default /wishlist/ output from ms3FavoritesPage (that page uses JS render() only). Flow: IDs → empty check → paged output and “Clear list”:

modx
[[!ms3FavoritesIds? &toPlaceholder=`myf.ids`]]
[[!+myf.ids:is=`-0`:then=`
  <p>[[%ms3favorites_empty]]</p>
`:else=`
  [[!pdoPage?
    &element=`msProducts`
    &parents=`0`
    &limit=`12`
    &resources=`[[!+myf.ids]]`
    &sortby=`FIELD(msProduct.id, [[!+myf.ids]])`
  ]]
  <button type="button" class="btn btn-primary" data-favorites-clear>[[%ms3favorites_clear_list]]</button>
  [[!+page.nav]]
`]]
fenom
{'!ms3FavoritesIds' | snippet : ['toPlaceholder' => 'myf.ids']}
{set $idsStr = $_modx->getPlaceholder('myf.ids')}
{if $idsStr == '-0'}
  <p>{$_modx->lexicon('ms3favorites_empty')}</p>
{else}
  {'pdoPage' | snippet : [
    'element' => 'msProducts',
    'parents' => 0,
    'limit' => 12,
    'resources' => $idsStr,
    'sortby' => 'FIELD(msProduct.id, ' ~ $idsStr ~ ')'
  ]}
  <button type="button" class="btn btn-primary" data-favorites-clear>{$_modx->lexicon('ms3favorites_clear_list')}</button>
  {$_modx->getPlaceholder('page.nav')}
{/if}

If ms3favorites_clear_list is missing in lexicon, use plain text or add the key.

Catalog: pdoPage + msProducts, counter and row button

Not the favorites page: a normal catalog with pagination, favorites button per row (list default), counter on top. Package chunk tplCatalogRowMs3f; full notes and AJAX — Integration.

modx
<p>Favorites [[!ms3FavoritesCounter? &list=`default` &resource_type=`products`]]</p>
[[!pdoPage?
  &element=`msProducts`
  &parents=`0`
  &limit=`10`
  &tpl=`tplCatalogRowMs3f`
  &totalVar=`page.total`
  &pageNavVar=`page.nav`
]]
<nav class="pagination">[[!+page.nav]]</nav>
fenom
<p>Favorites {'!ms3FavoritesCounter' | snippet : ['list' => 'default', 'resource_type' => 'products']}</p>
{'!pdoPage' | snippet : [
  'element' => 'msProducts',
  'parents' => 0,
  'limit' => 10,
  'tpl' => 'tplCatalogRowMs3f',
  'totalVar' => 'page.total',
  'pageNavVar' => 'page.nav'
]}
<nav class="pagination">{$_modx->getPlaceholder('page.nav')}</nav>

Step 5: /wishlist/ page

Create a resource with alias wishlist, a template that loads ms3fLexiconScript, favorites.css and favorites.js. In the content:

modx
[[!ms3FavoritesPage]]
fenom
{'!ms3FavoritesPage' | snippet}

Pagination: ms3FavoritesPage has no embedded pdoPage — cards on /wishlist/ are always filled by favorites.js. For a separate paginated favorites view, use ms3FavoritesIds → pdoPage → ms3Favorites (or msProducts) — see Integration.

Extended toolbar (Catalog / Clear list / Share): pass extendedToolbar or use chunk alias tplFavoritesPageDemo — see ms3FavoritesPage.

Styling (short)

Override CSS variables: --ms3f-button-active, --ms3f-bg, --ms3f-border, --ms3f-color, etc.

css
:root {
  --ms3f-button-active: #e74c3c;
  --ms3f-bg: #fff;
  --ms3f-color: #333;
}

Guest DB cleanup (cron)

To purge expired guest rows by guest_ttl_days, schedule:

bash
php /path/to/site/core/components/ms3favorites/cli/cleanup_guests.php

The CLI script resolves MODX from config.core.php (walks up from cli/). If guest_ttl_days = 0, TTL cleanup is skipped.

Next steps