Werte über Checkbox in DB eintragen


#1

Hallo Community,

ich bin neu im Bereich der Modulentwicklung und habe ein (für Euch) vermutlich kleines Problem.

Leider konnte ich dazu bisher keinen Thread finden, so dass ich jetzt einfach mal einen neuen aufmache…

Ich habe das Formular der Benutzergruppen (Tab: “Stamm”) im Backend um eine Checkbox erweitert.

Das dazugehörige Feld in der Datenbank (Tabelle: “oxgroups”), welches den entsprechenden Wert dieser Checkbox beinhaltet existiert ebenfalls bereits in der Datenbank. Ich habe es identisch zum Feld “OXACTIVE” angelegt (also gleicher Datentyp etc.), welches ebenfalls eine Checkbox ist.

Wenn man das Formular nun aufruft, wird auch der korrekte Wert aus der Datenbank ausgelesen und angezeigt. Standard ist “0”, also ist die Checkbox zunächst nicht gecheckt.

Setze ich nun das Häkchen und klicke auf “Speichern” wird der Eintrag in der Datenbank aktualisiert (auf “1”) und die Checkbox ist ab sofort immer gecheckt, wenn ich die Gruppe im Backend wieder anwähle.

Jedoch ist es ab diesem Punkt nicht mehr möglich, den Haken wieder zu entfernen und dies zu speichern. Der Eintrag in der DB bleibt nach dem ersten setzen des Häkchens immer auf 1 und lässt sich über das Frontend nicht mehr entfernen. Nur manuell über die Datenbank.

Kennt jemand dieses Phänomen? Was mache ich falsch?

Hier ist der Code, den ich im Block “admin_usergroup_main_form” ergänzt habe:

<input class="edittext" type="checkbox" name="editval[oxgroups__pdk_mynewfield]" value='1' [{if $edit->oxgroups__pdk_mynewfield->value == 1}]checked[{/if}] [{ $readonly }]>

Ich wäre für Hilfe sehr dankbar. Bin im Moment sehr ratlos…


#2

<input class="edittext" type="hidden" name="editval[oxgroups__pdk_mynewfield]" value='0'>

Eine “ungecheckte” checkbox gibt nicht 0, sondern keinen wert zurück und wird entsprechend nicht in der DB eingetragen.Lösung: Oberhalb des Input ein hidden mit gleichem Namen eintragen oder im Controller (Save) eine extra Abfrage, also etwas in der Art.:

if (!isset($aParams['oxgroups__pdk_mynewfield'])) {
            $aParams['oxgroups__pdk_mynewfield'] = 0;
        }

#3

hey klasse, jetzt erscheint mir das auch logisch!

ich habe gerade mal den hidden input eingefügt, und es funktioniert.

werde mir morgen aber nochmal den controller vornehmen und versuchen es dort sauber abzufangen. hoffe das bekomme ich hin.

vielen dank für die schnelle und kompetente hilfe! :grinning:


#4

Ho!

Der Weg mit dem Hidden Input ist schon der richtige. Lasse am besten den Browser den “Standard” senden und nutze PHP nur um die Standards zu erweitern. Es ist leichter dem Formular zu vertrauen dass es weiß was es sendet, als jeden Fall explizit in PHP zu prüfen. In einigen Fällen wird es sogar gewünscht sein, dass dieses Feld nicht berücksichtigt wird - was machst du dann? :slight_smile:

Im Controller empfehle ich die Prüfung als gute Übung, aber nicht als Lösung.

Just my two cents.


#5

Guten Morgen, und vielen Dank für Rückmeldung.

Ich habe jetzt eine Weile für meine “Übung” gebraucht, aber nun läuft es endlich auch mit dem Controller!

Ich würde mich aber freuen, wenn von Euch nochmal ein Experte darüber schaut und mir Feedback gibt. Denn auch wenn es jetzt funktioniert, ich bin bei der Entwicklung in Oxid noch sehr neu (und oft noch sehr überfordert).

Deshalb würde es mich interessieren, ob ich es aus Eurer Sicht auch sauber gelöst habe und nicht irgendwelche Bugs im Nachhinein auftauchen, die mir derzeit nicht auffallen, weil ich etwas übersehen habe…

Hier der Code meiner überladenen Controller-Klasse:

<?php

namespace pdk\mynewmodule\Controller\Admin;


class UserGroupMain extends UserGroupMain_parent
{

  public function save()
  {
    $aParams = \OxidEsales\Eshop\Core\Registry::getConfig()->getRequestParameter("editval");

    // my checkbox handling
    if (!isset($aParams['oxgroups__pdk_mynewfield'])) {
        $aParams['oxgroups__pdk_mynewfield'] = 0;
    }

    $oGroup = oxNew(\OxidEsales\Eshop\Application\Model\Groups::class);
    $oGroup->assign($aParams);
    $oGroup->save();

    parent::save();

  }

}

In der save-Funktion, welche ich hier überschreibe passieren so einige Dinge, die ich noch genauer nachvollziehen muss. Ich denke aber, auf dem sicheren Weg zu sein, wenn ich nach der Anpassung die ich vornehme, die Ursprungsfunktion noch einmal aufrufe.

Was meint Ihr? Kann ich das so lassen?


#6

Ho!

Sieht gut aus, würde ich auch so machen.


#7

Super, vielen Dank!!!


#8

moment mal. Hat das ganz sicher funktioniert?
Soweit ich es sehe, dürfte dein Code bestenfalls für bestehende Gruppen funktionieren, aber nicht für neue, weil du dich darauf verlässt, dass die oxID in den $aParams auftaucht…

In der ursprünglichen save Funktion gibts eine Stelle:

        if ($soxId != "-1") {
            $oGroup->load($soxId);
        } else {
            $aParams['oxgroups__oxid'] = null;
        }

wenn eine neue Gruppe über admin erstellt wird, ist $oxid gleich “-1”, daran erkennt der Shop es und erstellt neue Gruppe mit einer (später) generierten oxID.
Sollte es aber keine neue Gruppe sein, sondern eine bestehende, so ist deren oxID in $oxid hinterlegt und über $oGroup->load($oxid) werden die Daten aus der DB in das Objekt geladen, bevor er später aktualisiert und gespeichert wird.
Diese Stelle fehlt dir und solltest du eine neue Gruppe übers Admin anlegen, wird er vermutlich zwei Gruppen erstellen, sofern MySQL keine Fehlermeldungen wegen leeren Feldern schmeißt.

Und der zweite Punkt ist: du musst immer überlegen, wann dein eigener Code ausgeführt werden soll: vor oder nach dem ursprünglichen Code?

Wenn du z.B. irgendwas prüfen musst, bevor eine Gruppe angelegt wird, dann platziert man parent::save(); am Ende, wie bei dir.
will man aber mit den Daten arbeiten, die gerade im Formular angegeben wurden, so müssen diese natürlich als erstes verarbeitet werden und der eigene Code danach ausgeführt. Man muss ja schließlich zuerst einkaufen gehen, bevor man das Essen aus dem Kühlschrank holen kann ;).
Und weil du ja deine Daten in eine Gruppe einfügen willst, solltest du parent::save() vor deinem Code einfügen und nicht danach.


#11

Hallo Vanilla,

da sagst Du etwas Wahres: Wenn ich eine neue Gruppe anlege, werden gleich zwei erzeugt!

Genau das ist die Angst die ich hatte, wenn ich nicht zu 100% weiß, was die Funktion da tut, die ich überladen will. Verzeiht meine Unwissenheit, aber Oxid ist noch sehr viel Neuland für mich.

Aber jetzt zum Thema, denn nach Deiner Antwort, und einigen Tests von mir, tun sich jetzt neue Fragen für mich auf…

Ich habe meine Funktion nun wie folgt ungebaut:

  public function save()
  {

    parent::save();

    $aParams = \OxidEsales\Eshop\Core\Registry::getConfig()->getRequestParameter("editval");

    // my checkbox handling
    if (!isset($aParams['oxgroups__pdk_mynewfield'])) {
        $aParams['oxgroups__pdk_mynewfield'] = 0;
    }

    $oGroup = oxNew(\OxidEsales\Eshop\Application\Model\Groups::class);
    if ($soxId != "-1") {
        $oGroup->load($soxId);
    } else {
        $aParams['oxgroups__oxid'] = null;
    }

    $oGroup->assign($aParams);
    $oGroup->save();

  }

Leider produziert sie noch immer doppelte Datensätze. Ursache ist anscheinend der Aufruf von “$oGroup->save();”, welcher sowohl in meinem Code, als auch in der ursprünglichen Funktion aufgerufen wird und somit jeweils ein Datensatz erzeugt wird.

Lasse ich den Aufruf von “$oGroup->save();” jedoch in meinem Code weg, bin ich wieder bei meinem ursprünglichen Problem, dass sich Gruppen zwar mit den richtigen Werten erzeugen lassen, es im Nachhinein jedoch nicht möglich ist, den gesetzten Haken wieder zu entfernen.

Der Aufruf von “$oGroup->save();” in meinem Code ist also nötig, um den angepassten Parameter auch zu speichern. Jedoch nur, wenn es sich um die Änderung einer Gruppe handelt, nicht wenn sie erstmalig angelegt wird.

Die Lösung sieht jetzt so bei mir aus:

  public function save()
  {
    //Aktuelle oxID sichern
    $soxId = $this->getEditObjectId();

    //Ursprungsfunktion ausführen: Datenbankeintrag u. OxId erzeugen
    parent::save();

    if ($soxId != "-1") {

      // Parameter Laden
      $aParams = \OxidEsales\Eshop\Core\Registry::getConfig()->getRequestParameter("editval");

      // checkbox "is organisation" handling
      if (!isset($aParams['oxgroups__pdk_mynewfield'])) {
          $aParams['oxgroups__pdk_mynewfield'] = 0;
      }

      $oGroup = oxNew(\OxidEsales\Eshop\Application\Model\Groups::class);
      $oGroup->load($soxId);
      $oGroup->assign($aParams);
      $oGroup->save();
    }
  }

Ich sichere mir im ersten Schritt die OxID (die ggf. -1 ist). Danach rufe ich erst die Ursprungsfunktion auf (welche dafür sorgt, dass die Gruppe eine Oxid bekommt, also nicht mehr -1 ist).

Anschließend prüfe ich, ob $soxId vor dem Aufruf der Ursprungsfunktion eine -1 war (also eine neu angelegte Gruppe). Falls ja, führe ich quasi ein Update durch, indem ich jetzt (und NUR jetzt) meine Code-Ergänzung durchführe.

Eigentlich müsste es jetzt einen Fehler beim Anlegen einer neuen Gruppe geben. Nämlich genau dann, wenn ich beim Anlegen der Gruppe meine neue Checkbox nicht markiert habe. Theoretisch stimmt das, es wird hier nämlich kein Parameter für meine neue Checkbox übergeben. Da aber das entsprechende Feld in der Datenbank als DEFAULT-Value den Wert “0” hat, passt das.

Puh… aber mal ehrlich. Ist das jetzt wirklich eine Lösung so (ich meine es funktioniert ja). Aber ich hätte gedacht es geht etwas einfacher, bzw. sauberer…


#12

Entferne parent::save(); und führe stattdessen $this->resetContentCache(); aus.


#13

Habe ich gemacht, jetzt funktioniert weder das Anlegen neuer Gruppen, noch das Ändern von bestehenden Gruppen.

Was tut denn die Funktion resetContentCache() ?


#14

In parent::save wird das schon abgefragt, und gegebenenfalls eine neue Gruppe erstellt, und am Ende wird die neu erzeugte oxid übernommen (mit setEditObjectId).
Du musst also parent::save() einfach noch vor $soxId = $this->getEditObjectId(); ausführen, dann kannst du sicher sein dass der Eintrag schon existiert und du kannst dir die Abrage auf -1 sparen.


#15

Ok, ich habe diese umständliche Abfrage auf -1 nun entfernt.

Mein Problem war die ganze Zeit, dass ich die durch parent::save() bereits erzeugte OXID nicht in den Parametern mit abgelegt habe:

$aParams['oxgroups__oxid'] = $soxId;

Und somit wurde immer wieder ein Datenbank-Element mit der ID -1 erzeugt.

Was ebenfalls noch gefehlt hat, waren am Ende die Aufrufe der Funktionen:

$oGroup->setLanguage(0);
$oGroup->setLanguage($this->_iEditLang);

Es war mit meinem Modul bisher nicht mehr möglich, den Gruppennamen in mehreren Sprachen in der Datenbank abzulegen.

Meine finale Funktion sieht jetzt so aus:

  public function save()
  {
    parent::save();

    $soxId = $this->getEditObjectId();

    $aParams = \OxidEsales\Eshop\Core\Registry::getConfig()->getRequestParameter("editval");

    if (!isset($aParams['oxgroups__pdk_mynewfield'])) {
      $aParams['oxgroups__pdk_mynewfield'] = 0;
    }

    $aParams['oxgroups__oxid'] = $soxId;

    $oGroup = oxNew(\OxidEsales\Eshop\Application\Model\Groups::class);

    $oGroup->load($soxId);
    $oGroup->setLanguage(0);
    $oGroup->setLanguage($this->_iEditLang);
    $oGroup->assign($aParams);
    $oGroup->save();
  }

Grundsätzlich funktioniert sie jetzt so auch und sieht sauberer aus.

Aber wenn man es mal vergleicht, habe ich doch die ursprüngliche Funktion die ich überladen möchte fast komplett nachgebaut. Und das obwohl diese zu Beginn meines Codes einmal komplett ausgeführt wird!

Und das alles nur, um diese kleine Prüfung hier zu ergänzen:

if (!isset($aParams['oxgroups__pdk_mynewfield'])) {
  $aParams['oxgroups__pdk_mynewfield'] = 0;
}

Ist ein solcher “Aufwand” normal? Mir kommt das ehrlich gesagt schon arg umständlich vor…


#16

So wie du es jetzt hast holst du dir alle Parameter aus dem Formular, assignst sie und speicherst. Besser wäre es du würdest nur den einen Parameter neu schreiben den du ändern willst, die anderen sind ja schon gespeichert. Also da wo du die 0 zuweist und beim assign z.B. “$aMyParams” verwenden. Dann musst du auch die Oxid nicht nochmal setzen weil du sie nicht änderst.

Ja weil du dich nicht in die Mitte der Methode einklinken kannst musst du das Objekt nochmal neu laden, ändern und wieder speichern.

Man könnte jede Methode in ihre Aufgaben zerlegen, also eine Methode für Daten empfangen, eine für Daten ändern und eine für Speichern, dann müsstest du nur “Ändern” überschreiben, Oxid hat das teilweise schon so geändert. Du könntest auch direkt $_POST manipulieren dann wäre es weniger Code aber irgendwie unsauber.


#17

sofern es nur beim Speichern der Checkbox bleibt bzw einem beliebigen anderen Feld, und sonst nichts gemacht, berechnet, validiert werden muss, dann muss man die save() Funktion nicht erweitern.

Man muss lediglich das eigene Feld nach dem selben Muster wie vorhandene Felder benennen ..name="editval[oxgroups__feldname]... und dann wird es zusammen mit anderen Feldern erfasst und über ...->assign($aParams); im Objekt gespeichert. Daher hat man bei Checkboxen auch immer ein hidden Feld mit value 0 und dann eins mit einem positiven value, das man anklicken kann.


#18

Ich denke ich werde es letztendlich auch einfach über das Template lösen und einfach ein Hidden-Feld einbauen.

Aber als Übung war es doch schon mal ganz schön zu sehen, wie genau man einen Controller überlädt und vor allem, auf was man dabei alles achten muss. Das hat mich jetzt schon ein ganzes Stück weiter gebracht.

Ich danke Euch auf jeden Fall für die sehr gute Unterstützung hier im Forum, ich bin wirklich sehr begeistert!


#19

Nachtrag: Diese wird in parent::save (also class AdminController) aufgerufen.


#20

hey,

ja dort wird sie auch deklariert:

public function resetContentCache($blForceReset = null)
{
    $blDeleteCacheOnLogout = $this->getConfig()->getConfigParam('blClearCacheOnLogout');
    if (!$blDeleteCacheOnLogout || $blForceReset) {
        \OxidEsales\Eshop\Core\Registry::getUtils()->oxResetFileCache();
    }
}

aber, entschuldige die dumme frage: was für einen cache löscht sie denn?