Problem bei Modul-Erweitertung

Ich wollte mich mal näher mit der Entwicklung von Zusatzfunktionen beschäftigen, war da aber leider nicht ganz erfolgreich. :mad:

Als Beispiel wollte ich eine Artikel-Liste von “N Zufallsprodukten” und die Artikel-Liste mit “Top N”-Produkten im Template verfügbar machen.

Um systematisch vorzugehen, habe ich zunächst die gewünschte Funktionalität entwickelt, indem ich direkt die betreffenden “core”- und “view”-Module geändert habe. Funktionell habe ich dabei jeweils analog die vorhandene “[B]Top5[/B]”-Liste nachgebildet.

Dies erforderte Änderungen in “[B]core/oxarticlelist.php[/B]”, “[B]views/alist.php[/B]” und “[B]views/oxubase.php[/B]”.

[B] Damit gelang es mir, die gewünschte Funktionalität zu realisieren.[/B]

Dann wollte ich diese [B]direkten [/B]Änderungen in den Shop-Modulen wieder entfernen, und diese in neue zugehörige Klassen-Module verlagern, wie man das halt so machen soll.

Zunächst habe ich im Verzeichnis “[B]modules/articlelist[/B]” ein Programm “[B]myarticlelist.php[/B]” angelegt, und dieses in Admin/Module mit “[B]oxarticlelist => articlelist/myarticlelist[/B]” aktiviert.

Dieses enthält die vorher direkt in “oxarticlelist.php” enthaltenen neuen Methoden, und ist wie folgt aufgebaut:

class myarticlelist extends myarticlelist_parent
{

  public function loadTopNArticles($iArticles)
  {
      //has module?
      $myConfig = $this->getConfig();
      if ( !$myConfig->getConfigParam( 'bl_perfLoadPriceForAddList' ) ) {
          $this->_blLoadPrice = false;
      }
      $sArticleTable = getViewName('oxarticles');
      $sSelect  = "select * from $sArticleTable ";
      $sSelect .= "where ".$this->getBaseObject()->getSqlActiveSnippet()." and $sArticleTable.oxissearch = 1 ";
      //$sSelect .= "and $sArticleTable.oxparentid = '' and $sArticleTable.oxsoldamount>0 ";
      $sSelect .= "and $sArticleTable.oxsoldamount>0 ";
      $sSelect .= "order by $sArticleTable.oxsoldamount desc limit $iArticles";
      $this->selectString($sSelect);
  }

  public function loadRandomArticles($iArticles)
  {
      //has module?
      $myConfig = $this->getConfig();
      if ( !$myConfig->getConfigParam( 'bl_perfLoadPriceForAddList' ) ) {
          $this->_blLoadPrice = false;
      }
      $sArticleTable = getViewName('oxarticles');
      $sSelect  = "select * from $sArticleTable ";
      $sSelect .= "where ".$this->getBaseObject()->getSqlActiveSnippet()." and $sArticleTable.oxissearch = 1 ";
      //$sSelect .= "and $sArticleTable.oxparentid = '' ";
      $sSelect .= "order by rand() limit $iArticles";
      $this->selectString($sSelect);
  }
}

[B]Mit dieser Änderung funktionierte das Ganze weiterhin wie gewünscht![/B]

Dann habe ich im Verzeichnis “[B]modules/articlelist[/B]” ein Programm “[B]myoxubase.php[/B]” angelegt, und dieses in Admin/Module mit “[B]oxubase => articlelist/myoxubase[/B]” aktiviert.

Dieses enthält die vorher direkt in “oxubase.php” enthaltenen neuen Methoden, und ist wie folgt aufgebaut:

class myoxubase extends myoxubase_parent
{

  /**
   * Template variable getter. Returns Top N article list
   *
   * @return array
   */
  public function getTopNArticleList($iArticles=BESTSELLERS_ITEMS)
  {
    if ( true || $this->_aTopNArticleList === null ) {
        $this->_aTopNArticleList = false;
        $myConfig = $this->getConfig();
        if ( $myConfig->getConfigParam( 'bl_perfLoadAktion' ) ) {
            // top 5 articles
            $oArtList = oxNew( 'oxarticlelist' );
            $oArtList->loadTopNArticles($iArticles);
            if ( $oArtList->count() ) {
                $this->_aTopNArticleList = $oArtList;
            }
        }
    }
    return $this->_aTopNArticleList;
  }

   /**
   * Template variable getter. Returns Top N article list
   *
   * @return array
   */
  public function getRandomArticleList($iArticles=CAROUSEL_PRODUCTS)
  {
    if ( true || $this->_aRandomArticleList === null ) {
        $this->_aRandomArticleList = false;
        $myConfig = $this->getConfig();
        if ( $myConfig->getConfigParam( 'bl_perfLoadAktion' ) ) {
            // random articles
            $oArtList = oxNew( 'oxarticlelist' );
            $oArtList->loadRandomArticles($iArticles);
            if ( $oArtList->count() ) {
                $this->_aRandomArticleList = $oArtList;
            }
        }
    }
    return $this->_aRandomArticleList;
  }
}

Wenn jetzt aber in “[B]oxubase.php[/B]” der Code zum Laden der Listen in “[B]_loadActions[/B]”

protected function _loadActions()
    {
        $this->_aViewData['articlebargainlist'] = $this->getBargainArticleList();
        $this->_aViewData['aTop5Articles']      = $this->getTop5ArticleList();

        $this->_aViewData['aTopNArticlesList']      = $this->getTopNArticleList();
        $this->_aViewData['aRandomArticlesList']    = $this->getRandomArticleList();
    }

ausgeführt werden soll, dann meldet OXID-Shop, dass die Routine “getTopNArticleList()” [B]nicht definiert [/B]sei!

Mit dem Debugger (PhpEd) habe ich verifiziert, dass “[B]modules/articlelist/myoxubase.php[/B]” geladen wurde.

Ebenso, dass die Klasse “myoxubase” mit ihren Methoden definiert ist.

Warum “getTopNArticleList()” nicht definiert ist verstehe ich jetzt nicht mehr so recht, da ja durch die Modul-Klasse “myoxubase” diese Methode definiert sein sollte…

Was mache ich falsch?

Any idea, anybody?

Vielen Dank für jede Hilfe…

ich hab immer mal wieder das phänomen, das oxid bei manchen klassen eine erweiterung einfach nicht übernimmt. Das ist mir auch mal bei der oxview passiert, der ich neue methoden gegeben hab.

Entweder ist das ein “Feature” oder ein Bug, wenn das jetzt schon anderen leuten passiert als mir.

[QUOTE=csimon;9195]ich hab immer mal wieder das phänomen, das oxid bei manchen klassen eine erweiterung einfach nicht übernimmt. Das ist mir auch mal bei der oxview passiert, der ich neue methoden gegeben hab.

Entweder ist das ein “Feature” oder ein Bug, wenn das jetzt schon anderen leuten passiert als mir.[/QUOTE]
Die “[B]Core[/B]”-Child-Klassen werden anscheinend sauber behandelt, nur bei den “[B]View[/B]”-Child-Klassen gibt es dieses blöde Problem, dass deren Methoden nicht bekannt sind…

Ich habe den Ablauf der Klassenerzeugung in OXID für die Anweisung “oxubase => articlelist/myoxubase” im Debugger nachvollzogen, und das sieht eigentlich perfekt aus…

Wenn die Klasse “oxubase” in “oxutilsview.php” erzeugt wird, dann wird zuerst die Klasse “myoxubase_parent” erzeugt

class myoxubase_parent extends oxubase {}

Danach wird dann die Datei “modules/articlelist/myoxubase.php” aufgerufen, womit dann

class myoxubase extends myoxubase_parent

definiert wird.

[B]myoxubase [/B]beerbt also die Klasse [B]myoxubase_parent[/B], welche Ihrerseits die Klasse [B]oxubase[/B] beerbt hat.

Der Konstruktor von [B]myoxubase[/B] wird auch aufgerufen.

So weit, so gut.

Aber:

Diese Initialisierung findet erst im Rahmen des Ablaufs des “views” [B]Start[/B] statt, und diese Klasse beerbt nur “[B]oxubase[/B]”!!! (class Start extends oxUBase)

Hier wird also [B]nicht [/B]statt “[B]oxubase[/B]” die Klasse “[B]myoxubase[/B]” verwendet, was erklärt, warum die Methoden der Klasse “[B]myoxubase[/B]” nicht bekannt sind, sondern nur die der Original-Klasse “[B]oxubase[/B]”!!!

Start müsste daher auch “[B]myoxubase[/B]” beerben (falls eine solche Re-Definition existiert), und nicht statisch “[B]oxubase[/B]”…

So, jetzt ist mir zumindest klar, [B]warum [/B]das [B]nicht[/B] funktioniert… (Was ja auch schon mal nicht schlecht ist.)

Jetzt muss ich “nur” noch herausfinden, wie das funktioniert…

Hallo,

wenn ich mich recht entsinne, ist die oxubase ein Sonderfall. Schau Dir mal diesen Thread an:
http://www.oxid-esales.com/forum/showthread.php?t=906

Gruß

[QUOTE=Marco Steinhäuser;9212]Hallo,

wenn ich mich recht entsinne, ist die oxubase ein Sonderfall. Schau Dir mal diesen Thread an:
http://www.oxid-esales.com/forum/showthread.php?t=906

Gruß[/QUOTE]
Das Problem ist also, dass ich die “oxubase”-Klasse nicht über Module erweitern kann, weil diese an alle “views” vererbt wird, bevor der Mechanismus der Einbeziehung von Child-Klassen greift…

Diese Erkenntnis führt aber auch schon direkt zur Problemlösung!

Ich muss also dafür sorgen, dass die “oxubase”-Klasse meine Erweiterungen erhält, bevor sie vererbt wird.

Meine Lösung (die von den OXID-Entwicklern sicher sehr viel eleganter und flexiber gestaltet werden kann) sieht wie folgt aus:

Die Datei “views/oxubase.php” wird umbenannt in “views/oxubase_std.php”.

In Datei “views/oxubase_std.php” wird

class oxUBase extends oxView

ersetzt durch

class oxUBase_std extends oxView

Die Datei “views/oxubase.php” besteht jetzt erst einmal nur aus

class oxUBase extends oxUBase_std
{
   //Hier werden alle Methoden definiert, die "oxubase" (also alle "views") zusätzlich haben sollen.
}

Hier werden dann alle Eigenschaften und Methoden untergebracht, die ich brauche.

In der “index.php” muss ich jetzt nach

require_once getShopBasePath() . ‘core/adodblite/adodb.inc.php’;
einfügen

// Including standard oxubase_std
require_once getShopBasePath() . ‘views/oxubase_std.php’;

dann habe ich erreicht, dass alle “views” meine neuen “oxubase”-Methoden erben, und froh und zufrieden sind.

Funktioniert prima!

Eine generelle Lösung, die natürlich im OXID-Core vorgesehen werden muss, sollte natürlich intelligenter sein, und statt der starren, modifizierten “oxubase” an dieser Stelle schon prüfen, ob es für “oxubase” Erweiterungen gibt (Admin=>Module), und dann diese vererben.

Mit einer solchen Änderung würde das OXID-Entwickler-Leben ,m,E sehr erleichtert, da ja offenbar nicht nur ich das Problem habe, die “Views” erweitern zu wollen/müssen, was bisher ja nur durch direkten Eingriff in den “oxubase.php”-Code funktioniert.

Was wiederum das Konzept der OXID-Anpassung nur über Zusatzmodule doch empfindlich stört

So jetzt melde ich mich mal zu Wort :slight_smile:
Also oxUBase kann nicht mit dem OXID Modulkonzept erweitert werden, ebensowenig andere Klassen von denen Vererbt wird (oxView, oxBase, etc.) Dies ist in meinen Augen kein Bug, da i.d.R. nicht notwendig.
@avenger: Dein Problem lässt sich viel eleganter lösen ohne die o.g. Klassen zu überschreiben. Eleganter heisst hier in diesem Falld das du keine OXID Klassen anfassen musst und du damit updatefähig bleibst. Schau dir bitte mal die Komponentenklassen an. Diese fangen alle mit “oxcmp_” and und liegen im “views” Ordner. Diese Komponeten werden bei jedem Aufruf mitgeladen und stehen dir somit “überall” zur Verfügung (Stichwort: Decorator). Siehe dazu auch der Link von Marco.

Ichb hoffe ich konnte helfen.

Grüße
Mathias

Hallo, Matthias.

Danke für Deinen Hinweis, werde ich mal untersuchen…

Aber, ein Problem bleibt m.E. jedenfalls auch dann bestehen:

Ich muss den “oxubase”-Quellcode ändern, um meine neue Komponente dort für die Aktivierung einzutragen, wie in http://www.oxid-esales.com/forum/showthread.php?t=1199#post7040 beschrieben.

So dass ich wieder nicht update-sicher bin…

Das könnte man m.E. aber z.B. durch eine sehr einfache Änderung im OXID-Code lösen…

Man erlaube im Verzeichnis “views” eigene User-Komponenten mit Namen wie “oxcmp_user_xxxxxxx.php” (“xxxxxxx” beliebige Zeichenfolge).

Und statt in “oxubase/function init” die im Array “_aUserComponentNames” benannten User-Komponenten zu aktivieren

// as the objects are cached by dispatcher we have to watch out, that we don’t add these components twice
if ( !$this->_blCommonAdded ) {
$this->aComponentNames = array_merge( $this->[B][/B]aComponentNames, $this->_aUserComponentNames );
$this->_blCommonAdded = true;
}
wird der Komponenten-Array “_aComponentNames” um die in “views” vorhandenen “oxcmp_user_xxxxxxx”-Komponenten erweitert (wenn es solche gibt).

(Evtl. wäre es übersichtlicher (und effizienter) User-Komponenten im Unterverzeichnis “user_components” von Verzeichnis “views” abzulegen.)

Dann braucht man den “oxubase”-Quellcode tatsächlich nicht mehr zu ändern, um eigene Komponenten zu verwenden, sondern kopiert die einfach in das entsprechende Verzeichnis…

So wie es jetzt ist habe ich aber auch mit der Komponenten-Lösung mein Update-Problem, da der “oxubase”-Code geändert werden muss.

Ist zwar eine geringfügige Änderung, aber halt eine Änderung, die beim nächsten “oxubase”-Update verloren geht…

[QUOTE=avenger;9304]Hallo, Matthias.

Danke für Deinen Hinweis, werde ich mal untersuchen…[/QUOTE]
So, habe das jetzt untersucht…

[B]Und funktioniert!!![/B]

Wenn OXID jetzt noch eine Lösung schafft, die es erübrigt, die eigene Komponente im Feld “[B]_aUserComponentNames[/B]” im Code von “[B]oxubase”[/B] zwecks Aktivierung eintragen zu müssen (wie es z.B. mein Vorschlag unter http://www.oxid-esales.com/forum/showthread.php?p=9304#post9304 erreichen würde), dann kann man sich auch in diesem OXID-Bereich frei entfalten, ohne das nächste Update fürchten zu müssen. (Andere/bessere Lösungen mit dem gleichen Ergebnis sind natürlich ebenfalls willkommen!)

Eine technische Frage habe ich aber noch, vielleicht kann mir die ja jemand beantworten…

Meine Lösung besteht darin, dass ich meine notwendigen Funktionen in einer Klasse “cmp_pt_utils” untergebracht habe.

Die wird auch wunderbar eingebunden, da ich in “oxubase” folgende Definition eingebracht habe:

    protected $_aUserComponentNames = array(
                                    'oxcmp_pt_utils'      => 1
    );

Was mir bis jetzt aber noch nicht so recht klar ist: wie bringe ich diese Komponente (“politisch korrekt”!) in Smarty hinein???

Ich habe das jetzt zwar erst mal gelöst, indem ich in dem PHP-Modul, das ich sowieso innerhalb des Templates verwende, folgende Definition eingebracht habe:

$this->_tpl_vars['cmp_pt_utils']=new oxcmp_pt_utils;

Aber da gibt es sicher einen [B]eleganteren [/B]und [B]systematischeren [/B]Weg!?

Kenn den jemand?

Was mir bis jetzt aber noch nicht so recht klar ist: wie bringe ich diese Komponente (“politisch korrekt”!) in Smarty hinein???

entsprechenden view anpassen und das innerhalb der render() methode in $this->_aViewData[‘variablenname’] = $deinObjekt reinschreiben.

[QUOTE=csimon;9416]entsprechenden view anpassen und das innerhalb der render() methode in $this->_aViewData[‘variablenname’] = $deinObjekt reinschreiben.[/QUOTE]
Ok, danke.

Werde ich mal ausprobieren…

kurze Bemerkung in einer Komponente funktioniert:
$this->_aViewData[] … nicht
du musst:
$this->_oParent->addTplParam( ‘NAME’, WERT );
machen.

Grüße
Mathias

[QUOTE=MaFi;9954]kurze Bemerkung in einer Komponente funktioniert:
$this->_aViewData[] … nicht
du musst:
$this->_oParent->addTplParam( ‘NAME’, WERT );
machen.

Grüße
Mathias[/QUOTE]
Gut zu wissen, ich habe nämlich eine Komponente anzubinden…

[QUOTE=csimon;9416]entsprechenden view anpassen und das innerhalb der render() methode in $this->_aViewData[‘variablenname’] = $deinObjekt reinschreiben.[/QUOTE]
Was ist nun " $deinObjekt"?

$this???

das was du im template benutzen willst, einen string, ein objekt, eine zahl, was auch immer.

Gibt es denn hier mittlerweile ein funktionierendes, updatefähiges Workaround? Ich suche nach genau so einer Lösung und bin damit echt überfragt.

Lösung wurde hier doch schon angesprochen:
http://www.oxid-esales.com/forum/showthread.php?t=1571&highlight=oxubase+überschreiben#post9288

@avenger: Dein Problem lässt sich viel eleganter lösen ohne die o.g. Klassen zu überschreiben. Eleganter heisst hier in diesem Falld das du keine OXID Klassen anfassen musst und du damit updatefähig bleibst. Schau dir bitte mal die Komponentenklassen an. Diese fangen alle mit “oxcmp_” and und liegen im “views” Ordner. Diese Komponeten werden bei jedem Aufruf mitgeladen und stehen dir somit “überall” zur Verfügung (Stichwort: Decorator). Siehe dazu auch der Link von Marco.