ContentPages mit Unterkategorien und Sortierung

Hallo zusammen,

hier eine Funktion, die uns definitiv im Shop gefehlt hat. Eventuell können andere ja auch davon profitieren.
Ich denke es ist soweit selbsterklärend.

Anmerkung:
Das Usage-Example nutzt zusätzlich noch eine andere unserer Plugin-Funktionen namens “FetchField”, welche genutzt werden kann, wenn nur der Wert eines einzelnen Felds aus der DB benötigt wird, ohne gleich ein komplettes Model-Objekt dafür instanzieren zu müssen. Damit wird der Titel der Content-Page in der korrekten Sprache abgerufen. Funktionieren tut das ganze aber auch ohne :wink:

Sobald ich das Caching dort integriert habe, werde ich diese hier vermutlich auch posten.
Aber evtl. wird die hier auch noch umgebaut, wenn sich zeigt, dass es sinnvoll ist, grundsätzlich auch gleich den Titel der Contentpage in aktiver Sprache mit zu fetchen. Ggf. würde ich sie hier dann updaten.

<?php
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use Doctrine\DBAL\Connection;

/**
 * To appear in this list, entries of oxcontents must offer a value for oxposition formatted as follows:
 * <MenuRank>##<EntryRank>##<MainMenuName> where Rank refers to the order within the given main menu name, which itself
 * will group the entries.
 *
 * Type:     function<br>
 * Name:     GetCustomContentList<br>
 * Purpose:  Generates the list of content pages load ids used by the theme<br>
 * Params:   [mandatory]  assign - (str) - Name of the template variable which shall be list be written to.<br>
*
 * @param array $params Received argument array.
 * @param Smarty $smarty Smarty object reference.
 *
 * @return void
 *
 * @throws \Doctrine\DBAL\Driver\Exception
 * @throws \Doctrine\DBAL\Exception
 *
 */
function smarty_function_GetCustomContentList($params, &$smarty) {
  if ( !empty($params['assign']) ) {
    $oDb = ContainerFactory::getInstance()->getContainer()->get(Connection::class);
    $sql = "SELECT OXLOADID,OXPOSITION FROM oxv_oxcontents WHERE OXPOSITION!='' AND OXACTIVE=1 ORDER BY OXPOSITION";
    if ( false === $ds = $oDb->executeQuery($sql)->fetchAllNumeric() ) {
      return;
    }
    $result = [];
    //$oLang = Registry::getLang();
    foreach ( $ds as $row ) {
      // ensure there are the three mandatory sections only (it's just a weak validation but gives better performance)
      if ( count($aPosParts = explode('##', $row[1])) === 3 ) {
        $result[$aPosParts[2]][] = $row[0];
      }
    }
    $smarty->assign($params['assign'], $result);
  } else {
    $msg = '[Smarty-Plugin] getCustomContentList: Missing mandatory param \'assign\'!';
    Registry::getLogger()->error($msg);
  }
}

Folgende Einträge in der Datenbank:


(Die Namen der Überschriten sind die Identifier von Translation-Strings)

Ergeben mit diesem Template:

[{block name='footer_sitemap'}]
  [{* Korrekte View-Names für FetchField-Plugin erzeugen *}]
  [{if $oView->isLanguageLoaded()}]
    [{assign var=sConView value='oxv_oxcontents_'|cat:$oView->getActiveLangAbbr()}]
  [{else}]
    [{assign var=sConView value='oxv_oxcontents'}]
  [{/if}]

  [{GetCustomContentList assign='aContentIds'}]
  <div class="row g-0 d-none d-lg-flex my-5 justify-content-between">
    <ul class="row g-0 list-unstyled justify-content-between">
      [{* Aufbau der CMS-Listen, sortiert nach den CMS-Kategorien *}]
      [{foreach from=$aContentIds key='_ContentCatName' item='_aContentCatIds'}]
        <li class="col-auto">
          <h4 class="row g-0 fw-bold text-uppercase fc_red">[{oxmultilang ident=$_ContentCatName}]</h4>
          <ul class="list-unstyled">
            [{foreach from=$_aContentCatIds item='_ActId'}]
              <li><a href="[{oxgetseourl ident=$_ActId type='oxcontent'}]" class="py-2">[{FetchField table=$sConView oxid=$_ActId field='oxtitle'}]</a></li>
            [{/foreach}]
          </ul>
        </li>
      [{/foreach}]
    </ul>
  </div>
[{/block}]

Dieses Ergebnis:

Beste Grüße
stuck1a

Schön und danke. Aber gibt’s das nicht schon von vt?

Hallo rubbercut,

es ist eine erweitere Variante der [{oxcontent ident=“…” <assign=“…”>}].
Woran ich mich allerdings gestört hatte war, dass die oxcontent direkt ein komplettes Model-Objekt initialisiert, was m.M.n. wirklich nicht nötig ist, wenn es nur darum geht, den Titel und die SEO-URL zu erhalten. Für eine Sitemap (unser use case, siehe Bsp) muss man ergo alle ContentPages durchladen und das direkt auf der Startseite (Sitemap im Footer), wo sowieso schon genug Krempel geladen wird.
Zum anderen wollte wollte ich die Subkategorien der CMS-Pages direkt im DB-Eintrag speichern, damit man bei Änderungen später nicht gleich an die TPLs muss. So können im Backend die “oxposition”-Werte einfach angepasst werden, ohne das gleich ein Programmierer ran muss. Wenn du in das TPL-Snippet schaust, wirst du sicherlich feststellen, dass die Funktion direkt ein entsprechend strukturiertes und sortiertes Array gemäß <MenuRank>##<EntryRank>##<MainMenuName> zurückgibt. Im Bsp. hat aContentIds folgende Struktur:

aContentIds = [
  'INFORMATIONS' => [
    'customerinfo' => 'https://yourdomain.de/Kundeninformationen/',
    'deliveryinfo' => 'https://yourdomain.de/Zahlung-und-Lieferung/',
    // ...
  ],
  'CUSTOMERSERVICE' => [
    'faq' => 'https://yourdomain.de/Haeufig-gestellte-Fragen/',
    'guarantee' => 'https://yourdomain.de/Reklamation-und-Garantie/',
    // ...
  ],
  // ...
];

D.h. sie sind schon sortiert und man kann mit einfachen Mitteln (verschachtelte Schleife) direkt die Sitemap aufbauen, ohne groß rumzuwursteln :wink:
Die URLs kommen in der passenden Sprache (die Kategoriennamen kommen [noch] als Identifier für [{oxmultilang}].

// Edit:
Ich sollte vielleicht anmerken, dass wir einen sehr komplexen Shop umsetzen (unser Grafiker hat ganze Arbeit geleistet lach), daher kämpfe ich um jede Millisekunde.