Hook processFieldSQL und seine Funktion

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • Erdpferd
    Contao-Nutzer
    • 28.01.2011
    • 113

    #1

    Hook processFieldSQL und seine Funktion

    Hallo,

    ich habe den Catalog mal nach Hooks durchforstet und in Zeile 2032 den processFieldSQL Hook gefunden. Laut Kommentar können mit seiner Hilfe die SQl-Daten beeinflusst werden. Leider werde ich nicht so wirklich schlau aus dem Code:

    PHP-Code:
        protected function processFieldSQL($arrVisible)
        {
            $arrConverted = array();
    
            // iterate all catalog fields
            $objFields = $this->Database->prepare("SELECT * FROM tl_catalog_fields f WHERE f.pid=(SELECT c.id FROM tl_catalog_types c WHERE c.tableName=?)")
                                       ->execute($this->strTable);
    
            $arrFields = array();
            if ($objFields->numRows)
            {
                while ($objFields->next())
                {
                    $row = $objFields->row();            
                    $arrFields[$row['colName']] = $row;
                }
    
                foreach ($arrVisible as $id=>$field)
                {
                    if (array_key_exists($field, $arrFields))
                    {
                        switch ($arrFields[$field]['type'])
                        {
                            case 'calc':
                                // set query value to forumla
                                $value = '('.$arrFields[$field]['calcValue'].') AS '.$field; //.'_calc';
                                $arrConverted[$id] = $value;
                                break;
    
                            default:
                                $arrConverted[$id] = $field;
                        }
    
                    }
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    // HOOK: allow third party extension developers to prepare the SQL data
                    if(is_array($GLOBALS['TL_HOOKS']['processFieldSQL']) && count($GLOBALS['TL_HOOKS']['processFieldSQL']))
                    {
                        foreach($GLOBALS['TL_HOOKS']['processFieldSQL'] as $callback)
                        {
                            $this->import($callback[0]);
                            $this->$callback[0]->$callback[1]($this->catalog, $id, $field, $arrFields, $arrConverted, $this->strTable);
                        }
                    }    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                }    
            }
    
            return $arrConverted;
        } 
    
    Kann ich mit Hilfe des Hooks die SQL-Query beeinflussen und wenn ja, wie? Ich würde gerne eine individuelle Sortierreihenfolge integrieren. Ist das möglich?
    Wäre super wenn mir jemande helfen kann, da ich bereits die Zeile

    PHP-Code:
    $this->$callback[0]->$callback[1]($this->catalog, $id, $field, $arrFields, $arrConverted, $this->strTable); 
    
    in ihrer Wirkung nicht verstehe.

    Viele Grüße
  • the_scrat
    Contao-Urgestein
    • 24.02.2010
    • 2055

    #2
    Hallo Erdpferd,

    die SQL-Query kannst du damit nicht beeinflussen, die ist zum Zeitpunkt des Hooks bereits vollständig duchlaufen.
    Mit dem Hook kannst du eine eigene Methode registrieren, die die Parameter
    $this
    ->catalog, $id, $field, $arrFields, $arrConverted, $this->strTable

    übergibt. Die Sortierreihenfolge kannst du allerdings im Modul auch selbst bestimmen. Dazu gibt es im Modul einen Bereich unter Feld-Filter namens "Anordnen nach" dort kannst du die Spalten reinschreiben nach denen sortiert werden soll.

    Hoffe ich konnte dir damit weiterhelfen.
    Programmers don't comment their code. It was hard to write, it should be hard to understand...

    Kommentar

    • Erdpferd
      Contao-Nutzer
      • 28.01.2011
      • 113

      #3
      Danke für die schnelle Antwort.

      Leider habe ich das Problem, dass ich nicht nach Spalten sortieren will, sondern nach einer Sortierung, die sich durch Formulareingaben des Nutzers ergeben. Eine normale Sortierung nach ASC oder DESC hilft mit dabei nicht weiter. Deshalb meine Hoffnung in die SQL-Query eingreifen zu können.

      Wenn ich $this->catalog bekomme, habe ich dann den ganzen oder nur die Einträge, die auf der jeweiligen paginierten Seite stehen? Für die Sortierung brauche ich nämlich alle Einträge, die dann so sortiert auf die einzelnen Seiten verteilt werden können.

      Kommentar

      • the_scrat
        Contao-Urgestein
        • 24.02.2010
        • 2055

        #4
        Ich kann mir leider nur sehr schlecht vorstellen, was du machen möchtest. Aber selbst wenn du in die SQL eingreifen "könntest", würde dir das ja nix nützen, da der Catalog ja erstmal mit dem Ergebnis das da zurückommt weiterarbeitet.

        Evtl. ist eine Lösung, wenn du das direkt im Template löst. Dort hast du alle Informationen und Felder. Lass diese doch erst in ein Array laufen und dann ausgeben, wie du das meinst. Das kannst du ja dort auch über einen GET oder POST Parameter laufen machen. Vielleicht ist das die einfachste und schnellste und ggf. beste Methode.
        Programmers don't comment their code. It was hard to write, it should be hard to understand...

        Kommentar

        • Erdpferd
          Contao-Nutzer
          • 28.01.2011
          • 113

          #5
          Bekomme ich im Template nicht auch wieder nur eine Seite, wenn die Paginierung aktiv ist. Die meisten Probleme ergeben sich leider, weil eine Paginierung gesetzt ist und zudem ein Endless-Scroll die einzelnen Seiten nachladen soll. Das Script habe ich hier gefunden:



          Ich muss also irgendwie an alle Catalogeinträge kommen, um diese individuell zu sortieren. Die einzelnen Seiten zu sortieren hilft leider nicht.

          Ziel der ganzen Sache ist eine Umkreissuche. Die einzelnen Einträge werden mit einer Entfernung zu einer eingegebenen Adresse versehen und sollen dann nach Entfernung sortiert werden. Dafür muss aber die gesamte Liste sortiert werden, genau wie sie gefiltert werden soll, wenn die Entfernung zu groß ist. Durch die Paginierung habe ich aber leider nie alle Einträge gleichzeitig im Template. Deshalb mein Ansatz die SQL-Query zu verändern, so dass ich eine eigene Sortierung und Filterung unterbringen kann.

          Kommentar

          • psren
            Contao-Urgestein
            • 07.04.2010
            • 2733

            #6
            Falls du wirklich "nur" die Sortierung anhand einer Nutzereingabe ändern willst kannst du das im Modul unter "Sortierreihenfolge" machen.
            Dort kannst du Post oder Get Daten via Inputvar einsetzen.

            Edit: Okay eine Umkreissuche also :-)

            Das hatte ich einmal auf meinen Fall zugeschnitten mit eigenem FE-Modul realisiert. Mhh... bin gespannt auf deine Lösung :-)
            Zuletzt geändert von psren; 02.05.2012, 20:22.

            Wenn ich euch helfen konnte könnt ihr euch gerne mal
            meine Amazon Wunschliste anschauen. Dankeschön.

            Kommentar

            • psren
              Contao-Urgestein
              • 07.04.2010
              • 2733

              #7
              Zitat von the_scrat Beitrag anzeigen
              die SQL-Query kannst du damit nicht beeinflussen, die ist zum Zeitpunkt des Hooks bereits vollständig duchlaufen.
              Ohne es getestet zu haben sehe ich das anders. Es ist nur das Query abgefeuert worden, dass die Felder aus der Feld-Tabelle holt. Der eigentlich relevante Query kommt erst später.

              PHP-Code:
              protected function fetchCatalogItems(array $arrFields, $strWhere ='',
                                                                                              array $arrParams =array(),
                                                                                              $strOrder ='', $intLimit =0,
                                                                                              $intOffset =0, array $arrJoins=array())
                  {
                      $table = $this->objCatalogType->tableName;
                    $arrFields = $this->processFieldSQL($arrFields, $this->catalog, $table);
                    
                    // prepend columns to minimize the possibility of collisions when using JOINs
                    foreach ($this->systemColumns as $sysField)
                    {
                      $arrFields[] = sprintf('%s.%s AS %s',
                          $table, $sysField, $sysField);
                    }
                  
                    if($this->objCatalogType->aliasField)
                      $arrFields[] = $this->objCatalogType->aliasField;
                    
                    $strJoins = '';
                    if($arrJoins)
                      $strJoins = str_replace('{{table}}', $table, implode(' ', $arrJoins));
                    
                    $strOrder = strlen($strOrder) ? " ORDER BY " . $strOrder : "";
                    $strWhereOrder = ($strWhere?" AND " . $strWhere:'') . $strOrder;
                    
                    // pid
                    $params = array($this->objCatalogType->id);
                    $params = array_merge($params, $arrParams);
                          
                    // Run Query
                    $objCatalogStmt = $this->Database->prepare(sprintf('SELECT %1$s,
                        "%2$s" AS catalog_name, %3$u AS parentJumpTo
                        FROM %4$s %5$s WHERE pid=? %6$s',
                        implode(',', $arrFields),
                            $this->objCatalogType->name,
                            $this->objCatalogType->jumpTo,
                        $table,
                        $strJoins,
                        $strWhereOrder));
                    
                    // Limit result
                    if ($intLimit > 0)
                      $objCatalogStmt->limit($intLimit, $intOffset);
                    
                    return $objCatalogStmt->execute($params);
                  } 
              
              Ja, da wird erst der eigentliche Query ausgeführt, logischerweise nach dem Hook. Also suchst du schonmal an der richtigen Stelle.

              Allerdings habe ich hier grade keine Installation zum rumprobieren :-)

              Wenn ich euch helfen konnte könnt ihr euch gerne mal
              meine Amazon Wunschliste anschauen. Dankeschön.

              Kommentar

              • Erdpferd
                Contao-Nutzer
                • 28.01.2011
                • 113

                #8
                Danke für die Antworten.

                Das ist doch mal eine Richtung in der ich weiterarbeiten kann. Vielen Dank. Ich werde berichten.

                Kommentar

                • psren
                  Contao-Urgestein
                  • 07.04.2010
                  • 2733

                  #9
                  Wenn du nochmals Hilfe brauchst melde dich einfach :-)

                  Schön wäre es wenn am Ende ein wiederverwendbares Modul herauskommen würde :-) Du hast mich erst auf den Hook gebracht, den hatte ich beim letzten mal übersehen... leider...

                  Wenn ich euch helfen konnte könnt ihr euch gerne mal
                  meine Amazon Wunschliste anschauen. Dankeschön.

                  Kommentar

                  • psren
                    Contao-Urgestein
                    • 07.04.2010
                    • 2733

                    #10
                    Zitat von the_scrat Beitrag anzeigen
                    Evtl. ist eine Lösung, wenn du das direkt im Template löst. Dort hast du alle Informationen und Felder. Lass diese doch erst in ein Array laufen und dann ausgeben, wie du das meinst.
                    Das ist leider keine Lösung, da viel zu viele Daten aus der Datenbank kommen. Bei vielen Einträgen ist das ein riesen Array den man sortieren müsste. Die Datenbank löst das irgendwie performanter.

                    Hier gibt es ein paar Interessante Infos. Wobei ich mit Haversine als Stored Procedure ~17.000 Locations relativ performant sortiere ;-)

                    Wenn ich euch helfen konnte könnt ihr euch gerne mal
                    meine Amazon Wunschliste anschauen. Dankeschön.

                    Kommentar

                    • the_scrat
                      Contao-Urgestein
                      • 24.02.2010
                      • 2055

                      #11
                      Öhm, ja, schon klar, aber als ich das geschrieben habe, lagen nur wenige Informationen vor. Hätte ja auch sein können, dass 20 oder 50 Datensätze verarbeitet werden sollen, dann wäre die Templatelösung absolut akzeptabel.
                      Dass es sich um eine Umkreissuche handelt, wurde erst 1 Stunde danach klar, somit fällt logischerweise so eine Lösung weg ;-)
                      Programmers don't comment their code. It was hard to write, it should be hard to understand...

                      Kommentar

                      • psren
                        Contao-Urgestein
                        • 07.04.2010
                        • 2733

                        #12
                        Zitat von the_scrat Beitrag anzeigen
                        Öhm, ja, schon klar, aber als ich das geschrieben habe, lagen nur wenige Informationen vor. Hätte ja auch sein können, dass 20 oder 50 Datensätze verarbeitet werden sollen, dann wäre die Templatelösung absolut akzeptabel.
                        Dass es sich um eine Umkreissuche handelt, wurde erst 1 Stunde danach klar, somit fällt logischerweise so eine Lösung weg ;-)
                        Klar :-) Ich wollte es nur nocheinmal für spätere Leser verdeutlichen wieso jetzt "so umsändlich" :-D

                        Wenn ich euch helfen konnte könnt ihr euch gerne mal
                        meine Amazon Wunschliste anschauen. Dankeschön.

                        Kommentar

                        • Erdpferd
                          Contao-Nutzer
                          • 28.01.2011
                          • 113

                          #13
                          Es ist schon eine ganze Weile vergangen aber ich wollte mal meinen letzten Stand präsentieren. Mit dem processFieldSQL-Hook lassen sich die SQL-Abrufe durch den Catalog nicht beeinflussen. So musste ich für die Umkreissuche dem Catalog u.a. einen eigenen Hook implementieren, der die Variable $strWhere übergibt, bevor sie dem Datenbankabruf zugeführt wird. Auf die gleiche Weise war für die Sortierung (in meinem Fall nach Entfernungen) ein neuer Hook nötig (für die Variable $strOrder).

                          Da das ganze nicht Updatesicher ist, wird da momentan wohl eher kein eigenes Modul draus. Die zusätzliche Filterung für die Entfernungen habe ich über ein kleines Formular gelöst, dessen Werte im Hook ausgelesen und verarbeitet werden, da der Catalog auch hier keine Lösung ermöglichte. Zumindest keine, die mir eingefallen wäre.

                          Viele Grüße

                          Kommentar

                          • Tim G
                            Contao-Urgestein
                            • 13.02.2010
                            • 2212

                            #14
                            Hi Leute,
                            ja, der processFieldSQL Hook erwartet leider keinen Rückgabewert.

                            Die Idee von psren mit inputvar eine POST bzw. GET Variable zu setzen, ist in meinen Augen die eleganteste.
                            Du kannst all deine Berechnungen etc. für die nötige Distanz z.B. im parseFrontendTemplate Hook machen, danach die POST Variable FORM_SUBMIT (oder wie dein Form auch identifiziert wird) löschen, damit die Berechnung nur einmal ausgeführt wird und am Ende einfach die neue POST Variable setzen auf die dann inputvar greift, die im Katalog-Liste Modul per Inserttag schlummert.

                            Damit kannst du natürlich jedes Feld in den Modul-Einstellungen befeuern. Z.B. auch das Bedingungsfeld.

                            Sollte klappen.

                            Grüße,
                            Tim
                            ps. oder generatePage HOOK, alles was früher gefeuert wird als das Modul selbst.
                            http://www.tim-gatzky.de ˙ auch schon wieder 2 Jahre alt - wie die Zeit vergeht... muss mal umbauen.

                            Kommentar

                            • Erdpferd
                              Contao-Nutzer
                              • 28.01.2011
                              • 113

                              #15
                              Aber wenn ich es richtig verstanden habe, dann kann ich bei einem mehrseitigen Katalog immer nur eine Seite sortieren. Der Hook wird ja folgendermaßen beschrieben:

                              Der parseFrontendTemplate-Hook wird bei der Aufbereitung eines Frontend-Templates ausgeführt. Er übergibt Inhalt und Name des Templates als Argument und erwartet den geänderten Template-Inhalt als Rückgabewert.

                              Im Modul selbst wird das Template ja aber bereits auf die benötigten Einträge der Seite reduziert, so dass ich immer nur eine Seite sortiere. Der Katalog soll aber mehrseitig sein und via Ajax die Seiten im Endlesspage-Style nachgeladen werden. Dafür habe ich das Script aus diesem Post verwendet:



                              Folgendes Vorgehen habe ich aber auch nicht ganz verstanden:

                              ...danach die POST Variable FORM_SUBMIT (oder wie dein Form auch identifiziert wird) löschen, damit die Berechnung nur einmal ausgeführt wird und am Ende einfach die neue POST Variable setzen auf die dann inputvar greift, die im Katalog-Liste Modul per Inserttag schlummert.

                              Damit kannst du natürlich jedes Feld in den Modul-Einstellungen befeuern. Z.B. auch das Bedingungsfeld.
                              Was genau sollte ich denn in die POST Variable schreiben? Alle Entfernungen für die Einträge? Ich glaube ich steh grad auf der Leitung. Sorry.

                              Kommentar

                              Lädt...