Form-Request, um Artikel von Merkzettel zu entfernen

Guten Morgen zusammen,

für unser eigenes Theme bin ich derzeit mit dem Merkzettel im User-Bereich beschäftigt.
Da wir sehr unterschiedliche Designs für diverse Artikel-Listen haben, habe ich die lists aufgeteilt und bin gerade an der Umsetzung des Merkzettels (noticelist).

Für den Form-Request zur Entfernung vorhandener Artikel habe ich etwas in den Azure-Templates gespickt, die ja leider recht abstrakt sind, da ein generisches Listen-Widget genutzt wird.
Leider funktioniert mein nachfolgender Request noch nicht und ich kann den Fehler nicht ausfindig machen.

<form action="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:'cl=account&section=noticelist'}]" method="post">
  <ul class="list-unstyled">
    [{$oViewConf->getHiddenSid()}]
    <input type="hidden" name="cl" value="basket">
    <input type="hidden" name="fnc" value="tonoticelist">
    <input type="hidden" name="CustomError" value="basket">
    [{foreach from=$aNoticelistItems key='iBasketIndex' item='oBasketItem'}]
      [{assign var='oArticle' value='OxidEsales\Eshop\Application\Model\Article'|@oxNew}]
      [{assign var='blLoaded' value=$oArticle->load($oBasketItem->oxuserbasketitems__oxartid->value)}]
      [{if $blLoaded}]
        <li class="row noticelist-item mb-3 list-unstyled">
          [{* ...gekürzt zur besseren Übersicht... *}]
          <div class="d-flex mt-2 flex-shrink-1" style="margin-left:var(--noticelist-image-width)">
            [{* Artikel von Merkzettel entfernen *}]
            <div class="col-auto">
              [{block name='userarea_noticelist_item_remove'}]
                <input type="hidden" name="aproducts[[{$iBasketIndex}]][aid]" value="[{$oBasketItem->oxuserbasketitems__oxartid->value}]">
                <input type="hidden" name="aproducts[[{$iBasketIndex}]][basketitemid]" value="[{$iBasketIndex}]">
                <input type="hidden" name="aproducts[[{$iBasketIndex}]][override]" value="1">
                <input type="hidden" name="aproducts[[{$iBasketIndex}]][am]" value="0">
                <button class="btn fc_red" type="submit" name="updateBtn" title="[{oxmultilang ident='REMOVE'}]">
                  <i class="kcs-glyphs kcs-trash bg_red fc_white"></i>
                </button>
                <span class="align-middle text-uppercase fst-italic small">[{oxmultilang ident='REMOVE'}]</span>
              [{/block}]
            </div>
          </div>
        </li>
      [{/if}]
    [{/foreach}]
  </ul>
</form>

Was mich auch etwas irritiert, ist, dass ich oArticle manuell via oxNew erzeugen muss, da

[{assign var='oArticle' value=oBasketItem->getArticle()}]

fehlschlägt, während es im regulären Basket (Warenkorb) funktioniert. Die Funktion an sich ist für oBasketItem jedoch existent. Das aber nur am Rande.

Erkennt jemand, wo das Problem im Request liegt?

Vorab herzlichen Dank und beste Grüße

// Edit:
Das mit dem Artikel-Objekt hat sich schonmal geklärt. Ich hatte nicht erwartet, dass ich nocheinmal die BasketItem-ID als Parameter mitgeben muss, aber die Doku ist da eindeutig. Folgendes funktioniert:

$oBasketItem->getArticle($oBasketItem->getId())

Das primäre Problem mit dem Request konnte ich dennoch noch nicht auflösen.
Da ich nach dem Request trotz meiner abweichenden Target-Angabe zum Warenkorb weitergeleitet werden, vermute ich, es fehlt eine Angabe zur korrekten Remove-Funktion, daher vermute ich, dass der Hase bei “fnc=tonoticelist” begraben liegt.

getNoticeProductList in Verbindung mit der removeFunktion (tonoticelist) liefert eigentlich alles, was man braucht.

Woher kommt denn

[quote=“stuck1a, post:1, topic:98656”]
$aNoticelistItems
[/quote] ?

Sorry, ganz vergessen :slight_smile:

Hier die Deklarationen:

[{assign var='oNoticelist' value=$oxcmp_user->getBasket('noticelist')}]
[{assign var='aNoticelistItems' value=$oNoticelist->getItems()}]

“getNoticeProductList” sagt mir jetzt gerade nichts. Womöglich ist das das Problem :smiley:
Werde gleich mal die Doku bzw. den Core-Code checken.

// Okay, verstehe. Da bin ich mal wieder in die alte Falle der Controller-Scopes gerannt.
Das Problem ist, dass wir für das User-Backend einen OnePager angedacht haben, die Noticelist usw also als Aufklappmenü’s usw umsetzen möchten. Daher ist oView vom Typ AccountController, d.h. ich habe keinen Zugriff auf die Methoden von AccountNoticeListController. Das Problem hatte ich schon an einigen Stellen zuvor. Bisher habe ich mir da mit Mini-Modulen und/oder Plugin-Funktionen beholfen, womit ich aber nicht so ganz glücklich bin.

Auch wenn es mit dem MVC-Pattern bricht:
Gibt es da eine direktere Möglichkeit, auf die Funktionen eines “templatefremden” Controllers zuzugreifen? Dauernd Mini-Module zu erzeugen, ist ja performancetechnisch auch nicht unbedingt ideal. Hatte schon versucht Controller-Objekte in solchen Fällen via …|@oxNew zu nutzen, aber da fehlt dann logischerweise ein wesentlicher Teil der Initialisierung.

Allerdings… wenn ich mir die getNoticeProductList() so anschaue:

    public function getNoticeProductList()
    {
        if ($this->_aNoticeProductList === null) {
            if ($oUser = $this->getUser()) {
                $this->_aNoticeProductList = $oUser->getBasket('noticelist')->getArticles();
            }
        }

        return $this->_aNoticeProductList;
    }

…macht die aber letztlich auch nichts anderes, als ich im Template.

Genau. Das Form zum Entfernen schaut aber anders aus, schau dir das mal im Frontend auf der Merkzettelseite an. Da wird direkt aid und am usw. übergeben, also ein einzelner Artikel, ohne aproducts[[{$iBasketIndex}]]:

Ich bin mir nicht so sicher ob der Array “aproducts” von der Methode tonoticelist überhaupt ausgewertet wird.

Dann fällt mir noch auf dass bei dir im action-Parameter cl=account steht und im Form cl=basket, das ist widersprüchlich.

Du hast Recht, aproducts wird nicht ausgewertet.
UtilsComponent->tonoticelist() nutzt seine Funktion _toList() zur Auswertung:

    protected function _toList($sListType, $sProductId, $dAmount, $aSel) // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
    {
        // only if user is logged in
        if ($oUser = $this->getUser()) {
            $sProductId = ($sProductId) ? $sProductId : Registry::getConfig()->getRequestParameter('itmid');
            $sProductId = ($sProductId) ? $sProductId : Registry::getConfig()->getRequestParameter('aid');
            $dAmount = isset($dAmount) ? $dAmount : Registry::getConfig()->getRequestParameter('am');
            $aSel = $aSel ? $aSel : Registry::getConfig()->getRequestParameter('sel');

            // processing amounts
            $dAmount = str_replace(',', '.', $dAmount);
            if (!$this->getConfig()->getConfigParam('blAllowUnevenAmounts')) {
                $dAmount = round((string) $dAmount);
            }

            $oBasket = $oUser->getBasket($sListType);
            $oBasket->addItemToBasket($sProductId, abs($dAmount), $aSel, ($dAmount == 0));

            // recalculate basket count
            $oBasket->getItemCount(true);
        }
    }

Da bin ich wohl etwas durcheinander geraten bei diesen ganzen generischen Listen.
Demnach werden als Request-Parameter “itmid”, “aid”, “am” und ggf. “sel” benötigt/ausgewertet.
Danke für die Richtigstellung, ich denke, damit sollte ich das Ganze zum laufen bringen.
Ansonsten mache ich mich noch einmal bemerkbar :grin:

Dann fällt mir noch auf dass bei dir im action-Parameter cl=account steht und im Form cl=basket, das ist widersprüchlich.

Stimmt, das habe ich übersehen. Sind noch Trial and Error Überbleibsel, danke für den Hinweis.

Okay, etwas hakt es doch noch.

Der gekürzte Request sieht nun wie folgt aus:

<form action="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:'cl=account&section=noticelist'}]" method="post">
  <input type="hidden" name="cl" value="account">
  <input type="hidden" name="fnc" value="tonoticelist">
  <input type="hidden" name="aid" value="[{$oBasketItem->oxuserbasketitems__oxartid->value}]">
  <input type="hidden" name="itmid" value="[{$oArticle->getItemKey()}]">
  <input type="hidden" name="am" value="0">
  <button class="btn fc_red" type="submit" title="[{oxmultilang ident='REMOVE'}]">
    <i class="kcs-glyphs kcs-trash bg_red fc_white"></i>
  </button>

Das wird zu folgendem Code kompiliert:

...
<input type="hidden" name="aid" value="20151">
<input type="hidden" name="itmid" value="c843d785521c7810dba90b7e2c254398">
<input type="hidden" name="am" value="0">
...

Was mit dem entsprechenden Datenbankeintrag in oxuserbasketitems übereinstimmt:

Jedoch wird der Artikel nach wie vor nicht entfernt.
Ändere ich den Parameter itmid wie folgt auf den BasketIndex ab, wird ein Artikel entfernt, jedoch stets der letzte (=jüngst hinzugefügte) Artikel:

<input type="hidden" name="itmid" value="[{$sBasketItem}]">

Zum Vergleich hier nochmal der Request aus dem Azure-Template (widget/product/listitem_line.tpl):

<form action="[{$oViewConf->getSelfActionLink()}]" method="post" id="remove_[{$removeFunction}][{$iIndex}]">
  <div>
    [{$oViewConf->getHiddenSid()}]
    <input type="hidden" name="cl" value="[{$oViewConf->getTopActiveClassName()}]">
    <input type="hidden" name="fnc" value="[{$removeFunction}]">
    <input type="hidden" name="aid" value="[{$product->oxarticles__oxid->value}]">
    <input type="hidden" name="am" value="0">
    <input type="hidden" name="itmid" value="[{$product->getItemKey()}]">
    [{if $recommid}]
      <input type="hidden" name="recommid" value="[{$recommid}]">
    [{/if}]
  </div>
</form>

Probiere es statt 0 mit remove

Könnte mir vorstellen dass es daran liegt:

$oBasketItem->getArticle($oBasketItem->getId())

Weil die itmid ist normal nicht die OXID. Mit dem Aufruf wird sie aber neu gesetzt auf die OXID, und das bekommst du mit getItemKey() dann wieder zurück. Hab aber jetzt auch keine Idee wie man den Artikel holen soll ohne vorher die echte itmid zu kennen. Müsstest vielleicht mal in userbasket schauen wie das genau läuft mit dem itemkey.

Probiere es statt 0 mit remove

Für die virtuellen Warenkörbe gibt es soweit ich weiß leider keine separate remove-Funktion, da werden Artikel über am=0 entfernt. Zumindest habe ich noch keine andere remove-Funktion entdeckt.

Weil die itmid ist normal nicht die OXID. Mit dem Aufruf wird sie aber neu gesetzt auf die OXID, und das bekommst du mit getItemKey() dann wieder zurück. Hab aber jetzt auch keine Idee wie man den Artikel holen soll ohne vorher die echte itmid zu kennen. Müsstest vielleicht mal in userbasket schauen wie das genau läuft mit dem itemkey.

So etwas in der Art habe ich mir eben auch gedacht, denn ich habe gerade den Remove-Vorgang im Debugger verfolgt und tatsächlich wurde das richtige Objekt aus dem virtuellen Warenkorb entfernt. Sobald das Template dann aber ausgegeben wird, fehlt das falsche Objekt - ebenfalls in der Datenbank.
Werde jetzt mal mit dem Debugger nachschauen, was danach noch so passiert oder nicht passiert.

sollte mit $ sein oder

Danke für den Hinweis. Ja, da habe ich mich vertippt. War nicht kopiert. Fehlgeschlagen war das ganze allerdings, weil der nötige Parameter gefehlt hat.

Wenn du statt getItems getArticles verwendest kannst du dir das getArticle auch sparen und der irmkey wird nicht verändert. In getArticles kannst du auch sehen dass der itmKey nicht die OXID ist, sondern der Array-Key von getItems:

        foreach ($aItems as $sId => $oItem) {
            $oArticle = $oItem->getArticle($sId);

Habe das mal angepasst. Das hat dazu geführt, das oBasketItem vom Typ Article ist, so wie es vermutlich auch sein sollte. Das ist schonmal gut :slight_smile:
Leider besteht nach wie vor das Problem, das der falsche Artikel verschwindet.

Der Übersicht halber hier der Code in seiner aktuellen Form:

[{assign var='oNoticelist' value=$oxcmp_user->getBasket('noticelist')}]
[{assign var='aNoticelistItems' value=$oNoticelist->getArticles()}]
<form action="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:'cl=account&section=noticelist'}]" method="post">
  <ul id="basket_list" class="cart_item-list list-unstyled">
    [{$oViewConf->getHiddenSid()}]
    <input type="hidden" name="cl" value="account">
    <input type="hidden" name="fnc" value="tonoticelist">
    [{foreach from=$aNoticelistItems key='sBasketIndex' item='oBasketItem'}]
      [{assign var='oAttributes' value=$oBasketItem->getAttributesDisplayableInBasket()}]
      [{assign var='oCat' value=$oBasketItem->getCategory()}]
      <li class="row noticelist-item mb-3 list-unstyled">
    
        [{* ... *}]

        <input type="hidden" name="aid" value="[{$oBasketItem->oxarticles__oxid->value}]">
        <input type="hidden" name="itmid" value="[{$oBasketItem->getItemKey()}]">
        [{*
        Folgendes ergibt den gleichen key (demzufolge mit selben Resultat)
        <input type="hidden" name="itmid" value="[{$sBasketIndex}]">
        *}]
        <input type="hidden" name="am" value="0">
        <button class="btn btn-primary" type="submit" title="[{oxmultilang ident='REMOVE'}]">
          <i class="kcs-glyphs kcs-trash"></i>
        </button>
      </li>
    [{/foreach}]
  </ul>
</form>

Du musst die Form-Tags innerhalb von foreach setzen, also für jeden Artikel ein eigenes Form.

Oh man, mist! Natürlich, das war es :slight_smile:
Manchmal sieht man wohl wirklich den Wald vor lauter Bäumen nicht mehr.
Das war mal eine schwere Geburt, immer diese verflixten Request’s.

Einmal mehr vielen herzlichen Dank euch allen für eure kompetente Hilfe, speziell dir, leofonic.

Für’s Archiv hier noch einmal der vollständige, funktionsfähige Source:

[{assign var='oNoticelist' value=$oxcmp_user->getBasket('noticelist')}]
[{assign var='aNoticelistItems' value=$oNoticelist->getArticles()}]
  <ul id="basket_list" class="cart_item-list list-unstyled">
    [{foreach from=$aNoticelistItems item='oBasketItem'}]
      <li class="row noticelist-item mb-3 list-unstyled">

        [{* ... *}]

        <form action="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:'cl=account&section=noticelist'}]" method="post">
          [{$oViewConf->getHiddenSid()}]
          <input type="hidden" name="cl" value="account">
          <input type="hidden" name="fnc" value="tonoticelist">
          <input type="hidden" name="aid" value="[{$oBasketItem->oxarticles__oxid->value}]">
          <input type="hidden" name="itmid" value="[{$oBasketItem->getItemKey()}]">
          <input type="hidden" name="am" value="0">
          <button class="btn btn-primary" type="submit" title="[{oxmultilang ident='REMOVE'}]">
            <i class="kcs-glyphs kcs-trash">[{oxmultilang ident='REMOVE'}]</i>
          </button>
        </form>
      </li>
    [{/foreach}]
  </ul>
2 Likes