MB: funktionalität in Konstruktoren ähnlicher Klassen besser machen???

moin,

ich möchte "schematisch" eine Klasse von meinem wachsenden 5 MB großen Projekt vorstellen, die bei Instanziierung ein SQL-Schnipsel bereit stellt.

class Between extends AbstractConstruct implements FilterInterface
{

  public function __construct (
    ColumnInterface $value,
    ExpressionInterface $minimum,
    ExpressionInterface $maximum,
    bool $negation = false,
    bool $safe = false
  ) {
    // receive
    $this->receiveContext ( $value->sendContext() );
    $this->_addValues( $value->getValues() );
        
    // Built
    $this->_addString( $negation ? 'NOT ' : '' );
        
    if ( $safe ) {
      $this->_addString( "{$value->getString()} BETWEEN ? AND ?" );
      $this->_addValue( $minimum->getString () );
      $this->_addValue( $maximum->getString () );
    } else {
      $this->_addString(
        "{$value->getString()} BETWEEN {$maximum->getString ()} AND {$minimum->getString ()}"
      );
    }
      
    $this->_addValues( $minimum->getValues() );
    $this->_addValues( $maximum->getValues() );
  }
}

Der Aufbau, der vorgestellte Klasse, ist bei jeder anderen Klasse, dieser Klassengruppe, die eine SQL-Schnippsel bereit stellt, die gleiche. Es sind noch Validatoren im Spiel die, vor der Abarbeitung des Methoden Rumpfes, eine Prüfung der Parameter unterziehen - hier in diesem Fall, ist es nicht notwendig da die Parameter eindeutig sind.

Kann man generalisierende "Hilfsmethode" bereit stellen die den Code etwas organisieter machen? Ich komme da einfach nicht weiter, wie ich den Code besser organisieren kann 😕. Kann man überhaupt noch etwas dran drehen oder ist das Ende, so ziehmlich erreicht?

Ich meine, wenn ich sei einer Woche den Code nich mehr zur Gesicht bekommen habe, muss ich mich in den Code erst wieder reinfinden, trotz PHPDoc an jeder Ecke, der garnich kommentiert werden brauchen (ich habe diese Code Docs weggelassen).

lgmb

akzeptierte Antworten

  1. Hallo MB,

    hast Du den Code kopiert oder abgetippt? Denn bei BETWEEN in der non-safe Variante sind min und max vertauscht. Und vor allem fehlt hier der Kontextwechsel, diese Werte müssen escaped werden. Dein toString kann das nicht machen, weil dann die toString Aufrufe im safe-Teil falsch wären.

    Du könntest ggf. die häufigen toString Aufrufe dadurch ersetzen, dass Du den entsprechenden Objekten eine magic method __toString verpasst. Die wird dann implizit aufgerufen, wenn das Objekt wie ein String verwendet wird.

    $this->formatSqlFragment($safe, "@1 BETWEEN @2 AND @3", $value, $minimum, $maximum)

    So ein Aufruf müsste dann die @x Marker durch Parameter ersetzen, entsprechend schauen, ob das ein ColumnInterface oder ein ExpressionInterface ist und dann je nach Stand von $safe unterschiedlich generieren. Ob's dadurch klarer wird oder wirrer, weiß ich nicht recht 😉. Aber das könnte man dann an vielen Stellen nutzen und es würde viele manuelle Formatieraufrufe kapseln.

    Es darf nur nicht zu viel werden. Irgendwann soll's ja auch mal ein Ergebnis bringen und die Datenbank befragen, und dafür darf nicht eine Sekunde mit abstraktem Hantieren vergehen.

    Ich glaube, man kann auch sowas schreiben: WHERE 17 BETWEEN col1 AND col2 oder WHERE col2 BETWEEN 1 AND col3 - probier's mal aus und lies es nach. Das könnte Dir deinen BETWEEN-Builder ordentlich verhageln, weil nämlich dann jede der 3 Positionen wahlweise eine Spaltenangabe oder eine Expression sein kann.

    Rolf

    --
    sumpsi - posui - clusi
    1. moin,

      hast Du den Code kopiert oder abgetippt? Denn bei BETWEEN in der non-safe Variante sind min und max vertauscht.

      Ups danke!

      Und vor allem fehlt hier der Kontextwechsel, diese Werte müssen escaped werden.

      Welher Kontext Wechsel? Was muss escapet werden?

      Dein toString kann das nicht machen, weil dann die toString Aufrufe im safe-Teil falsch wären. […]

      ich hab kein __toString()-Methode. Ich sammel alles und dann concatenate ich. Ein konkretes Dummy Beispiel.

      $subQuery = new QueryBuilder( new InitDBContextPOPO ( $schema, [
          'tableA'    => 'tA',
          'tableB'    => 'tB',
          'tableC'    => 'tC'
      ] ) );
      
      $subQuery
        ->generalSelect ( $allColumns )
        ->generalFunction ( $cases )
        ->source ( $equiJoin )
        ->generalCondition ( new LogicContainer ( [
          $between, $in
        ], LogicConstant::_OR ), LogicConstant::_NONE )
        ->generalOrder ( $sort );
      
      /* Init Supra Query */
      
      $supraQuery
        /* n Constructs */
        ->generalCondition (
          new Comparison (
            EquotationConstant::_EQUAL, 
            $everything,
            new SubQuery (
              $subQuery->getResult ( )
            )
          ), LogicConstant::_NONE )
        )
        /* n Constructs */
        
      var_dump ( $supraQuery->getResult() );
      

      Die wird dann implizit aufgerufen, wenn das Objekt wie ein String verwendet wird.

      $this->formatSqlFragment($safe, "@1 BETWEEN @2 AND @3", $value, $minimum, $maximum)

      Das wäre ne Idee 😀. Bedankt.

      […] Aber das könnte man dann an vielen Stellen nutzen und es würde viele manuelle Formatieraufrufe kapseln.

      was meinst du mit Manuelle Formatierung?

      Es darf nur nicht zu viel werden. Irgendwann soll's ja auch mal ein Ergebnis bringen und die Datenbank befragen, und dafür darf nicht eine Sekunde mit abstraktem Hantieren vergehen.

      Ja das drohgt mir

      Ich glaube, man kann auch sowas schreiben: WHERE 17 BETWEEN col1 AND col2 oder WHERE col2 BETWEEN 1 AND col3 - probier's mal aus und lies es nach.

      Wo und was nachlesen?

      Das könnte Dir deinen BETWEEN-Builder ordentlich verhageln, weil nämlich dann jede der 3 Positionen wahlweise eine Spaltenangabe oder eine Expression sein kann.

      es können auch Werte sein numerischer Art und keine Spaltenangaben, daher ExpressionInterface. Aber ich muss ja noch mal genauer nach hacken, was das Prädikat BETWEEN eigentlich für Parameter animmt und was nicht. Dann kann ich das Interface anpassen.

      lgmb

      1. Hallo MB,

        Kontextwechsel

        noch nie gehört? Wenn ich Daten aus einer Variablen in ein SQL Statement einsetze, statt PREPARE und Parametermarker zu verwenden, muss ich sie maskieren.

        Dieser Comic von Randall Munroe bringt es göttlich auf den Punkt. „sanitize your database inputs“ - das ist der Kontextwechsel. Du bringst einen String-Wert in ein SQL Statement ein, und musst ihn darum so maskieren, dass SQL Steuerzeichen im String das Statement nicht stören. Dafür hat mysqli die real_escape_string Funktion, und PDO hat quote.

        Bei Spaltennamen muss man auch aufpassen, Rolf B ist ein völlig korrekter Spaltenname. Aber er grillst Dir dein SQL, wenn Du ihn nicht in Backticks einschließt (MYSQL). MS SQL Server verwendet eckige Klammern oder Spec-gemäß doppelte Anführungszeichen (sic!).

        new InitDBContextPOPO

        Das ist kein POPO. Plain Old * Objects gehören thematisch zum fachlichen Modell. Dein Objekt würde, glaube ich, lieber QueryContext heißen. Dein Tables-Array ist - scheint mir - falschrum gebaut. Die Aliasnamen müssen üblicherweise eindeutig sein in einer Query, Tablenamen nicht. Wenn Du einen self join machen musst, geht das bei Dir schief. Es sollte 'tA' => 'tableA' heißen.

        ->generalCondition(..., LogicConstant::_NONE)

        Warum eigentlich _NONE und nicht NONE?

        Abgesehen von der allgemeinen Geschwätzigkeit deines Konzepts - hier würde ein Defaultparameter helfen. Ich nehme an, dass generalCondition auch eine Version hat, in der Du ein Array von LogicContainern als 1. Parameter übergibst und in diesem Fall ergibt die LogicConstant einen Sinn. Wenn aber nur ein einziger Container übergeben wird, ist NONE ein sinnvoller Default.

        Was auch helfen kann, ist ein Set von Helper-Funktionen, die das Erzeugen von Interfaces und Containern kapseln. Dann verschwindet eine Menge Technomantie aus einem Query-Build. Und durch schöne Variablennamen liest sich der Build der Query gleich wie ein Buch.

           $isMiddleAged = new BetweenClause($ageColumn, 30, 59);
           $hasLargeSize = new InClause($sizeColumn, [ 'L', 'XL', 'XXL' ]);
        
           $logic = $isMiddleAged -> or( $hasLargeSize );
        

        oder kürzer

           $logic = new BetweenClause(...)
                    ->or(new InClause(...));
        

        Es gibt sicherlich eine abstrakte Superklasse für Logikelemente, mit Ableitungen für Basiskonstrukte (Subselect, Between, In, Vergleich) und Logikoperationen (AND, OR).

        In der sähe die or-Methode ganz einfach so aus:

           public function or($otherLogic) {
              return new LogicContainer( $this, $otherLogic, LogicConstant::_OR);
           }
        

        Und diese Methoden heißen or und and - nicht etwa addOrCondition oder so. Man nennt sowas ein Fluid Interface - es sieht aus wie flüssige Sprache. Die Container sollten übrigens rein binär sein. Ein OR aus mit N Operanden lässt sich immer auf (N-1) ORs mit 2 Operanden zurückführen, und das macht die Programmierung vieeeel einfacher.

        Warum heißen die Methoden des Querybuilders eigenlich generalXXXX? Gibt es auch specialXXXX Varianten? Eigentlich sollte ein Builder doch der Select-Struktur folgen.

        $builder->select( $columnExpressions )
                ->from( $source )
                ->where( $whereLogic )
                ->groupBy( $groups )
                ->having( $havingLogic )
                ->orderBy( $columnExpressions )
        

        Warum da neue Namen erfinden? Das $builder Objekt sollte übrigens nur die Methoden select, insert, update, delete und union kennen. Diese erzeugen spezifische Builder für die Befehle (die natürlich alle Subklassen eines abstrakten Builders sind).

        Vielleicht kann $builder auch Helper enthalten, die ColumnInterfaces oder LiteralInterfaces erzeugen. Und diese Interfaces können ihrerseits Builder-Helper enthalten:

        $fooIsBar = $builder->column("foo")
                    ->equals($builder->string("o'hara"))
        
        $everything = $builder->column("everything");
        $isInSubquery = $everything->equals($subQuery)
        

        könnte dann ein ExpressionInterface für einen Test auf Gleichheit erzeugen, und $fooIsBar->getSql() liefert den String \foo` = 'o'hara'` - oh je, das rafft Markdown nicht - . Das Backslash in o'hara stammt aus dem Kontextwechsel, das ' Zeichen darf nicht unmaskiert in eine Query hinein.

        Das liest sich jedenfalls besser als deine Comparison Konstruktion. Wichtig ist da auch eine API Konsistenz. Es ist merkwürdig, wenn ein LogicContainer die LogicConstant am Ende hat und eine Comparison die „EquotiationConstant“ (das Wort gibt's nicht) vorn. Diese Constant möchte vermutlich lieber ComparisonOperator heißen…

        was meinst Du mit manuelle Formatierung

        Ich meinte die von Dir überall verstreuten Aufrufe von toString oder auch getResult. Sowas sollten die Builder-Komponenten bei Bedarf selbst tun.

        Wo und was nachlesen

        Syntax von BETWEEN. Im MYSQL Handbuch. Findet sich im Internet. Lohnende Lektüre 😂

        Jedenfalls ist so ein fluid interface aus meiner Sicht die eleganteste Lösung. Bestimmt besser als mein Schnaps mit formatSqlFragment von gestern.

        Ich habe jetzt viel durcheinander geschrieben, es geht nicht alles stringent in eine Konzeptrichtung. Such Dir was 'raus 😉

        Rolf

        --
        sumpsi - posui - clusi
        1. moin,

          Kontextwechsel

          noch nie gehört?

          klar und angewendet. Du verwirrst mich. Den Cartoon der Seite den du als Link verwiesen hast, checke ich nich.

          Bei Spaltennamen muss man auch aufpassen, Rolf B ist ein völlig korrekter Spaltenname. Aber er grillst Dir dein SQL, wenn Du ihn nicht in Backticks einschließt (MYSQL).

          Ich verwende immer Backticks.

          new InitDBContextPOPO

          Das ist kein POPO. Plain Old * Objects gehören thematisch zum fachlichen Modell.

          POPO Klassen haben doch außer dem __construct() keine Methoden und sind readOnly zugänglich. So habe ich es verstanden und das ist hier gegeben

          Dein Objekt würde, glaube ich, lieber QueryContext heißen.

          Wenn dann SQLContext da ich die Initialisierung mit allen DQL, DML, DDL, DCL mache.

          Dein Tables-Array ist - scheint mir - falschrum gebaut. Die Aliasnamen müssen üblicherweise eindeutig sein in einer Query, Tablenamen nicht. Wenn Du einen self join machen musst, geht das bei Dir schief. Es sollte 'tA' => 'tableA' heißen.

          nur n Beispiel. Mein Validator erkennt das.

          ->generalCondition(..., LogicConstant::_NONE)

          Warum eigentlich _NONE und nicht NONE?

          AND und OR sind Keywords und kann ich nicht nehmen also _AND und _OR. NONE hingegen nicht. Aber es sind nicht ordenlich aus wenn ich bei NONE kein unterstrich mache.

          […] hier würde ein Defaultparameter helfen

          is auch drin

          Was auch helfen kann, ist ein Set von Helper-Funktionen, die das Erzeugen von Interfaces und Containern kapseln.

          Ok hast du ne Lektüre parat?

          Dann verschwindet eine Menge Technomantie aus einem Query-Build. Und durch schöne Variablennamen liest sich der Build der Query gleich wie ein Buch.

          Ich verschaffe euch mal ein UML Diagramm meines Projektes. Habe ich sowieso vor.

          Man nennt sowas ein Fluid Interface - es sieht aus wie flüssige Sprache.

          Ok Lektüre?

          Die Container sollten übrigens rein binär sein. Ein OR aus mit N Operanden lässt sich immer auf (N-1) ORs mit 2 Operanden zurückführen, und das macht die Programmierung vieeeel einfacher.

          War auch nur Beispiel. Ich habs hardgecoded um zu zeigen was die BETWEEN Klasse tun soll.

          Warum heißen die Methoden des Querybuilders eigenlich generalXXXX? […] Warum da neue Namen erfinden?

          Aggregatoren muss GroupBy haben sonst beißen die sich. daher habe ich das getrennt von ->select() in generalSelect() für CASE usw. und ->groupSelect() für Aggregatoren wie MAX().

          Das $builder Objekt sollte übrigens nur die Methoden select, insert, update, delete und union kennen.

          bin noch an Coden

          Vielleicht kann $builder auch Helper enthalten, die ColumnInterfaces oder LiteralInterfaces erzeugen.Und diese Interfaces können ihrerseits Builder-Helper enthalten:

          Wie sähe das wirklich konkret aus??? Hast du n SQL Builder zum Donload oder ist es SecretCode? Dann habe ich nicht gefragt 😉.

          Das liest sich jedenfalls besser als deine Comparison Konstruktion.

          ok

          Wichtig ist da auch eine API Konsistenz.

          Lektüre und Referenzen hast du?

          Es ist merkwürdig, wenn ein LogicContainer die LogicConstant am Ende hat und eine Comparison die „EquotiationConstant“ (das Wort gibt's nicht) vorn. Diese Constant möchte vermutlich lieber ComparisonOperator heißen…

          Konkretes Beispiel:

          class EquotationConstant {
            const
              EQUAL = '=',
              NOT_EQUAL = '<>'
              GREATER_THAN = '>'
              LESSER_THAN = '<';
          }
          

          desweiteren noch ArithmeticConstant +-*/ usw.

          was meinst Du mit manuelle Formatierung

          Ich meinte die von Dir überall verstreuten Aufrufe von toString oder auch getResult. Sowas sollten die Builder-Komponenten bei Bedarf selbst tun.

          es gibt kein __toString() sonder allefals $expression->getString() für den SQL Schnipsel und $expression->getValues() für ersetzungen der ? in der WHERE-Clausel.

          lgmb

          1. Hallo MB,

            so, hab zum Antworten ein neues Fenster aufgemacht, sonst wird das nix...

            Kontextwechsel - nie gehört

            Das erstaunt mich. Es ist im Forum ständiges Thema. Das Wiki hat einen Artikel. Der Comic bezieht sich auf einen Schüler mit dem Namen Robert'; DROP TABLE Students; --

            Das ist eine typische SQL Injektion zum Sabotieren von Websites, die massive Security-Fehler aufweisen. Man muss drei Fehler machen: Erstens: Diesen Namen blindlings in ein SQL Statement einbauen, z.B. so:

            $sql = "SELECT * FROM Students WHERE name='$_POST[name]'";
            

            dann entsteht daraus

            SELECT * FROM Students WHERE name='Robert'; DROP TABLE Students; --'
            

            Zweitens: Das Ausführen mehrerer Statements in einem Execute gestatten und Drittens: Dem DB-Account der Website mit zu hohen Rechten ausstatten. Trifft all das zusammen, ist die Students Tabelle nach einer Query auf diesen Namen futsch. Deswegen "Exploits of a Mom" - die Mama hat den Namen ihres Sohnes genutzt, um der Schule zu beweisen wie mies ihre Seite gestrickt ist, und macht sich dann auch auch noch lustig darüber, als die Schule anruft und sich beschwert. Randall Monroe at his best :)

            POPO

            Du hast meinen Einwand einfach nicht beachtet. POxO Objekte sind eine Methode, wie man sein fachliches Modell aufbaut. Außerhalb des fachlichen Modells hat der Begriff nichts zu suchen. Zumindest habe ich das so verstanden. Es mag andere Sichten geben. Die Wikipedia schreibt zum POJO, dass der Begriff "hauptsächlich" im ORM-Bereich auftaucht. Das schließt weitere Nutzungen, wie bei Dir, nicht aus. Insofern: streiche diesen Einwand.

            Key/Value Ordnung im Tables Array wird vom Validator sichergestellt

            Was erkennt dein Validator?

            SELECT *
            FROM table A LEFT JOIN table B ON a.id = b.parent_id
            

            Würde der Validator einen solchen Self-Join (den man zum Auflösen von Hierarchien braucht) zurückweisen?

            AND und OR sind Keywords, das geht nicht

            Doch, das geht. Du musst nur PHP 7 nehmen. In PHP 5.6 wird es abgewiesen, aber das solltest Du ohnehin nicht mehr verwenden.

            Lektüre zu Helper-Funktionen / Fluid Interfaces

            Nö, hab ich nicht. Sowas ist mir einfach schon in anderen APIs begegnet und ich fand es klasse. Das muss man für den jeweiligen Zweck ohnehin selbst konzipieren. Es ist dabei oft so, dass solche Methoden entweder $this zurückgeben (für Kettenaufrufe wie $this->addColumn("a")->addColumn("b")) oder neue Objekte erzeugen und zurückgeben (das war mein $this->between(...)->and($this->in(...)) Beispiel). Ein Methodenname and setzt natürlich auch PHP 7 voraus, offenbar haben die den 7er Parser aufgeschlaut.

            Bei Helpern und Fluid gilt natürlich auf wieder: Nicht übertreiben.

            generalSelect vs groupSelect

            Ok, kann man machen. Aber es widerspricht der „don't make me think“ Idee und man muss die Methoden auch nicht zwingend trennen. Aggregatfunktionen oder HAVING ohne GROUP BY geht schief, ja, aber das fällt spätestens dem SQL Server auf. Da deine select-Funktion nicht in die Zukunft schauen kann, wäre es hilfreich, wenn Du deinen Generator in 2 Teile teilst: (1) Syntaxbaum aufbauen (also eine Objektstruktur, die das Statement repräsentiert und (2) SQL aus dem fertigen Baum generieren. Dann kann man vor der Generierung noch einen Plausi-Check einfügen. Man kann es aber auch lassen und die Fehlermeldung dem SQL Server überlassen. GIGO halt.

            Wie sähe das wirklich konkret aus???

            Tja - ich habe nichts als Vorlage. In dem Umfang habe ich noch nichts selbst gebaut. Was ich mal gemacht habe ist ein Logging-Helper mit Fluid-Interface, aber das ist betriebsintern und daher nicht veröffentlichbar. Was ich auch gemacht habe, ist ein Script-Interpreter für die Konfiguration unserer Callcenter-Software, da habe ich aber den Parser nicht selbst geschrieben sondern ANTLR verwendet. Der ANTLR Parser generiert aus dem Quelltext einen Syntaxbaum und verwendet dann das Besucherpattern, um den vom Parser erzeugten Syntaxbaum verarbeitbar zu machen - das war bisher mein intensivster Kontakt zu diesen Konzepten. Fluid Interfaces habe ich in der Patterns and Practices Library von Microsoft kennengelernt, die aber an vielen Stellen auch ein Musterbeispiel für Overengineering ist.

            Ich wollte gestern Abend mal - interessehalber - eine Basis für einen eigenen SQL Generator erzeugen, bin aber nicht weit gekommen, mangels anderer dringender Dinge die zu tun waren.

            Lektüre Lektüre Lektüre Referenzen Referenzen Referenzen

            Warum bist Du da derart hinterher? Verlangst Du Belege für die Tauglichkeit meiner Aussagen? Oder suchst Du bloß weiterführende Informationen? Wenn's ersteres ist: KFO. Wenn's letzeres ist: Tja. Ich erzähle hier letztlich über meine Erfahrungen aus 35 Jahren Softwareentwicklung. Ich habe im Laufe der Jahre viele Bücher gelesen, wie Robert Martins Clean Code, die Patterns der Gang of Four, vor langer Zeit auch die damals existierenden Teile von Donald Knuths Art of Computer Programming (das Game Of Thrones der Informatik) oder Robert Sedgewicks Algorithms - und viel viel Code anderer Leute und Texte im Netz. Daraus habe ich viel mehr gelernt als aus Büchern; vor allem hier im Forum.

            EquotationConstant

            Was ich sagen wollte: Es gibt den Begriff der „Eqoutation“ nicht. Es gibt Equation (Gleichung / Gleichsetzung). Du meinst Comparison (Vergleich). Und deine EquoationConstant sollte um <= und >= ergänzt werden.

            es gibt kein __toString()

            Das schreibst Du nun schon zweimal. Was ich meinte, ist: Das KÖNNTE es aber geben. __toString ist eine magic method von PHP, die von PHP automatisch aufgerufen wird wenn ein Objekt wie ein String verwendet wird. Das Gegenteil von automatisch ist manuell - und das machst Du, wenn Du ->toString() immer dann aufrufst wenn Du es brauchst.

            Rolf

            --
            sumpsi - posui - clusi
            1. moin,

              Kontextwechsel - nie gehört

              Das erstaunt mich.

              hmm, ist wohl an mir vorbei gerauscht wie so vieles in meinem Leben 😀.

              POPO

              Du hast meinen Einwand einfach nicht beachtet. POxO Objekte sind eine Methode, wie man sein fachliches Modell aufbaut.

              Oh ok. Sry. Mein kleines hobby Progrämmchen ist keine ORM 😕. Also Streiche ich das. Bedankt.

              Was erkennt dein Validator? […] Würde der Validator einen solchen Self-Join zurückweisen?

              Ich hab die Klasse…

              new EquiJoin (
                EquiJoinConstant::LEFT,
                new TableColum ( 'tA', 'id' ),
                new TableColum ( 'tB', 'id' )
                EquationConstant::EQUAL
              );
              

              …raus käme…

              FROM
                `FIRMA.TableA` `tA` LEFT JOIN `FIRM.TableB` `B`
                ON `tA`.`id` = `tB`.`id`
              

              Lektüre zu Helper-Funktionen / Fluid Interfaces

              Nö, hab ich nicht. […]

              Ok

              generalSelect vs groupSelect

              Ok, kann man machen. Aber es widerspricht der „don't make me think“.

              hmm. ok das wäre n punkt

              […] wenn Du deinen Generator in 2 Teile teilst: (1) Syntaxbaum aufbauen

              hab isch. ich sammle sequeniell alle SQL Schnippel als Package und validiere sie unteranderem auf echtheit des Context…

              […] (2) SQL aus dem fertigen Baum generieren.

              …und baue sie nach builder methode zusammen, nach dem ich sie in der Korrektheit der Clausel validiert habe

              Dann kann man vor der Generierung noch einen Plausi-Check einfügen. Man kann es aber auch lassen und die Fehlermeldung dem SQL Server überlassen.

              Alles gemacht nur eben den Kontext nicht da man ja "noch" keinen zugriff zur DB hat.

              Lektüre Lektüre Lektüre Referenzen Referenzen Referenzen

              Warum bist Du da derart hinterher? […]

              Sry. Das zweit. Mir ist Bewusst an wenn ich meine Fragen richte 😉. Dewegen meine Fragen u.a. an Dich. Ich wollte nur wissen, ob du grad eben ein z.B. Wikiartikel zur Hand hast. Ich hab befürchtet, das man zwangsläufig auf Bücher zurück greifen muss, um die Infos zu bekommen. Bücher wälzen kann ich nicht und schaffe ich nicht (zeitlich, sprachlich, belastungsmäßig) 😕. Deswegen.

              […] und viel viel Code anderer Leute und Texte im Netz. Daraus habe ich viel mehr gelernt als aus Büchern; vor allem hier im Forum.

              Das mach mir hoffnung 😀

              EquotationConstant

              Es gibt den Begriff der „Eqoutation“ nicht.

              Ups, danke! ich muss das gkeich mal ändern 😟.

              es gibt kein __toString()

              Das schreibst Du nun schon zweimal. Was ich meinte, ist: Das KÖNNTE es aber geben.

              Man hat mir gesagt MagicMethods sind erzeugnungtechnisch Teuer. Versteh mich nicht falsch. Wenn man nur ein String ausgeben muss ohne weiteres, dann hätte ich diese __toString()-Methode schon verwendet. Aber 1. sollte ja ein Objekt als Konstruktorparameter übergeben werden und kein string, damit man vorab die Kategorie des Objektes prüfen kann. 2. Müssen man ja noch die Values als array für bindValue() übergeben werden. 3. der Context der Tabellen. So jabe ich $expression->getString(), $expression->getValues(), $expression->sendContext() verwendet. Das macht das ein wenig einfacher finde ich.

              lgmb

  2. Der Aufbau, der vorgestellte Klasse, ist bei jeder anderen Klasse, dieser Klassengruppe, die eine SQL-Schnippsel bereit stellt, die gleiche.

    Typischer Fall für Vererbung. Stelle eine Basisklasse von der die die Aufgaben spezialisierende Klasse erbt. Gggf. werden in der Subklasse die gerbten Methoden überschrieben und ggf. auch der Konstruktor der Baisklasse.

    MFG

    1. moin,

      Typischer Fall für Vererbung. […]

      Richtig aus meinem Beispiel mit dem Schlüsselwort extends abgeleitet. Doch das ist in meinen Augen nicht das Thema des Threads. Bitte versteh das.

      lgmb

      1. moin,

        Typischer Fall für Vererbung. […]

        Richtig aus meinem Beispiel mit dem Schlüsselwort extends abgeleitet. Doch das ist in meinen Augen nicht das Thema des Threads. Bitte versteh das.

        Natürlich. Du willst Deinen Code besser organisieren. Überlege Dir bitte, warum Du bei dieser Vererbung den Vaterkonstruktor nicht erben willst.

        MFG

        1. moin,

          Natürlich. Du willst Deinen Code besser organisieren. Überlege Dir bitte, warum Du bei dieser Vererbung den Vaterkonstruktor nicht erben willst.

          Da verwirrst du mich 😀. Meine Super Klasse im Beispiel ist ja AbstractConstruct und die hat Traits, welche einem die Methoden _addString() und _addValues() zur verfügung stellt. Meintest du das?

          lgmb

          P.S.: Sorry bin sein heite Krankgeschrieben, blöde Erkältung 😕. Die mich ausknockt.

          1. moin,

            Natürlich. Du willst Deinen Code besser organisieren. Überlege Dir bitte, warum Du bei dieser Vererbung den Vaterkonstruktor nicht erben willst.

            Da verwirrst du mich 😀. Meine Super Klasse im Beispiel ist ja AbstractConstruct und die hat Traits, welche einem die Methoden _addString() und _addValues() zur verfügung stellt. Meintest du das?

            Du überschreibst den Konstruktor der Elternklasse. Was macht dann eine Vererbung für einen Sinn?

            Und was macht Dein Interface ohne Methoden?

            MFG

            1. moin,

              Du überschreibst den Konstruktor der Elternklasse. Was macht dann eine Vererbung für einen Sinn?

              Die Elternklasse hat keinen Konstruktor denn ich überschrieben habe.

              abstract class AbstractConstruct implements PredicateInterface {
              
                use TraitContainer;
                use TraitContext;
              }
              
              class Between extends AbstractConstruct {
                // Einsetzung der Trait Methoden
              }
              

              Und was macht Dein Interface ohne Methoden?

              zur identifikation des Objektes und Die Methoden sind in den Trait

              lgmb

              1. Die Elternklasse hat keinen Konstruktor denn ich überschrieben habe.

                Du schlägst das Erbe des Vaterkonstruktors aus.

                Die Methoden sind in den Trait

                Und damit schlägst Du das Erbe der Methoden aus. Deine Vererbung ist sinnbefreit.

                Und im Übrigen ist Dein Code ziemlich unübersichtlich. So schreibt man keinen Code den man später wiederaufnehmen kann.

                MFG

                1. Tach!

                  Die Elternklasse hat keinen Konstruktor denn ich überschrieben habe.

                  Du schlägst das Erbe des Vaterkonstruktors aus.

                  Was soll da ausgeschlagen werden, wenn es nichts gibt?

                  Die Methoden sind in den Trait

                  Und damit schlägst Du das Erbe der Methoden aus. Deine Vererbung ist sinnbefreit.

                  Diese Methoden stehen der erbenden Klasse zur Verfügung. Solange sie nicht neu definiert werden, ist doch alles in Ordnung. Oder inwiefern soll "das Erbe ausgeschlagen" sein?

                  dedlfix.

  3. Kann man generalisierende "Hilfsmethode" bereit stellen die den Code etwas organisieter machen? Ich komme da einfach nicht weiter, wie ich den Code besser organisieren kann 😕. Kann man überhaupt noch etwas dran drehen oder ist das Ende, so ziehmlich erreicht?

    Ein paar Ideen:

    • Verlagere die Erzeugung des SQL-Strings vom Konstruktor in eine eigene Methode toSql.
    • Verändere bei der String-Erzeugung nicht den iternen Zustand des Objekts. Setze den String lokal zusammen und lass ihn von der Methode zurückgeben.
    • Entferne den $negation-Parameter und schreibe dir eine eigene Klasse Not für die Negierung
    • Schreibe ein Interface BooleanValue. Die Klassen Between und Not sollten dieses Interface implementieren. Not bekommt im Konstruktor ebenfalls ein Objekt vom BooleanValue übergeben.
    • Entferne den $safe-Operator. Ich würde stattdessen ein Interface NumercialValue schreiben und dan zwei Klassen NumberLiteral und NumberPlaceholder, die beide das Interface implementieren. NumberLiteral konvertiert eine PHP Wert vom Typ number in einen SQL-String. NumberPlaceholder erzeugt einfach ein Fragezeichen.
    • Entferne die Klasse ColumnInterface und schreibe dir eine Klasse NumberColumn, die ebenfalls NumericalInterface implementiert.

    Du kannst den Code von Between dann auf das folgende reduzieren:

    final class Between implements BooleanValue {
    
        private NumericalValue $value;
    
        private NumericalValue $min;
    
        private NumericalValue $max;
    
        public function __construct(NumericalValue $value, NumericalValue $min, NumericalValue $max)
        {
            $this->value = $value;
            $this->min = $min;
            $this->max = $max;
        }
    
        public function toSql() : string
        {
            $value = $this->value->toSql();
            $min = $this->min->toSql();
            $max = $this->max->toSql();
            return " $value BETWEEN $min AND $max ";
        }
    
    }
    

    Also nochmal Zusammengefasst, Interfaces:

    • NumericalValue
    • BooleanValue

    Und Klassen:

    • NumberLiteral implements NumericalValue
    • NumberPlaceholder implements NumericalValue
    • NumberColumn implements NumericalValue
    • BoolLiteral implements BooleanValue
    • BoolPlaceholder implements BooleanValue
    • Between implements BooleanValue
    • Not implements BooleanValue

    Wenn du das für andere MySQL-Datentypen so fortsetzt, erhälst du eine eine Sammmlung vieler sehr primitiver Klassen, die alle mehr oder weniger den Aufbau wie Between oben haben. Ein Konstruktor, der nur die Felder initialisiert und eine Methode toSql, die den dazu passenden SQL-String erzeugt. Solche Klassen lassen sich sehr einfach testen, weil sie keinen internen veränderlichen Zustand haben. Außerdem erleichtert eine so fein-granulare Architektur die Verwendung der Library, weil die Typen eine präzise Anleitung dafür liefern, was wie wo verwendet werden darf. Typfehler können auch schon zur Entwicklungszeit angezeigt werden, das erleichtert das Debugging.

    Der Nachteil ist natürlich, dass das sehr viel Schreibarbeit ist.

    1. Hallo 1unitedpower,

      Entferne den $safe-Operator.

      das sehe ich anders. Das ist keine Frage der beteiligten Operanden, sondern eine Frage der SQL Generierung. Der Query-Builder verwendet Spaltennamen und Literale, denen es im Sinne von Separation-of-Concerns egal sein sollte, ob inline-Werte oder prepare verwendet wird - das sollte komplett und transparent vom der SQL Generierung behandelt werden.

      Letztlich muss der Builder einen Syntax-Tree für die Query erzeugen, mit Namen von DB-Objekten (Table, Column) als Blätter und mit Operationen als Knoten. Dieser Tree muss so unabhängig von konkreten DB-Implementierungen wie nur möglich sein. Escaping, Parameterdeklarationen oder konkrete Schlüsselwörter gehören da nicht hinein. Dafür ist die zweite Komponente zuständig, der SQL-Codegenerator. Der sollte eine eigene Klasse sein. Entweder übergibt man dem Codegenerator den Baum zum Fraß, oder man steckt den Codegenerator in den Builder und lässt ihn vom Builder aufrufen. Ist letztlich egal. Und drittens braucht man ggf. noch eine Art Executor, gerade bei prepared statements, der generiertes SQL und Parameter sauber zusammenbringt und sich um die Instanziierung der Result-Objekte kümmert. Bei mysqli ist das Arbeit. PDO macht davon schon einiges automatisch, es muss nur in MBs Konzept eingepasst werden.

      Rolf

      --
      sumpsi - posui - clusi
      1. das sehe ich anders. Das ist keine Frage der beteiligten Operanden, sondern eine Frage der SQL Generierung. Der Query-Builder verwendet Spaltennamen und Literale, denen es im Sinne von Separation-of-Concerns egal sein sollte, ob inline-Werte oder prepare verwendet wird - das sollte komplett und transparent vom der SQL Generierung behandelt werden.

        Ja, da gehe ich mit. In meinem Beispiel sind AST und CodeGenierung gekoppelt. Man kann sie entkoppeln, indem man den Knoten Getter verpasst und die toSQL-Methoden auslagert. Entweder in eine große Funktion mit einem riesigen if-else-Konstrukt, oder in jeweils eizelne Funktionen. Also, zum Beipsiel so:

        final class Between implements BooleanValue {
        
            private NumericalValue $value;
        
            private NumericalValue $min;
        
            private NumericalValue $max;
        
            public function __construct(NumericalValue $value, NumericalValue $min, NumericalValue $max)
            {
                $this->value = $value;
                $this->min = $min;
                $this->max = $max;
            }
        
            public function value() : NumericalValue
            {
                return $this->value;
            }
        
            public function min() : NumericalValue
            {
                return $this->min;
            }
        
            public function max() : NumericalValue
            {
                return $this->max;
            }
        
        }
        
        final class Generator {
        
            public static function between(Between $node) : string
            {
                $value = Generator::number($node->value());
                $min = Generator::number($node->min());
                $max = Generator::number($node->max());
                return " $value BETWEEN $min AND $max ";
            }
            
            public static function number(NumericalValue $node) : string
            {
                if ($node instanceof NumberLiteral) {
                    return (string)$node->value();
                } elseif ($node instanceof NumberPlaceholder) {
                    return " ? ";
                } elseif ($node instanceof NumberColumn) {
                    return Generator::column($node);
                }
            }
        
            // usw.
        }
        

        Statt einer einzelnen Generator-Klasse könnte man auch für jeden Knoten-Typen jeweils eine Generator-Klasse haben mit einem gemeinsamen Interface.

    2. moin,

      Kann man generalisierende "Hilfsmethode" bereit stellen die den Code etwas organisieter machen?

      Ein paar Ideen: […]

      Danke Dir und @Rolf B für die Ideen 😉! Ich hoffe @Rolf B schreibtr nochmal zu meiner Anfrage. Ich werde es aufnehmen.

      Das mit der Not-, And-, Or-Klasse? Was ist denn an LogicConstant::_AND usw. Konstanten, als Parameter für den weiteren Kontrollfluss, weniger geeignet oder ist das Jacke wie Hose und nur Anwendungsfall speziell? Wenn ja, was gibts für Pro/Cons?

      lgmb

      1. Hallo MB,

        die Anfrage ist wohl eher ein Anroman :)

        Den hatte ich übersehen, ich guck mir das gleich mal an.

        Rolf

        --
        sumpsi - posui - clusi
  4. hi @MB

    Die Erzeugung eines SQL Statements (String) lässt sich auch über ein Interface lösen. Das reduziert den ganzen Code auf 2 Klassen:

    # SQL zum Auslesen eines Thread
    $m = new Thread(array(
        'threadid' => 2,
        'tabn'     => 'tunguskaforum',
        'fields'   => array('mesgid','author','subject','mesg','mesgdate'),
        'orderby'  => 'mesgdate'
    ));
    
    # Rufe die Methoden des Interface auf
    $m->select();
    $m->from();
    $m->where();
    $m->order();
    $m->limit();
    
    # Das Interface ist hier eine Klasse
    class SqlString{
        # SQL Fragmente erfassen
        function __construct($frag){
            $this->FRAG = $frag; 
        }
       
        function select(){}
        function from(){}
        function where(){}
        function order(){}
        function limit(){}
        function between(){}
        function join(){}
    }
    
    # Spezialisiere die Aufgabe
    # durch Überlagerung der Interfacemethoden
    class Thread extends SqlString{
        function select(){
            echo "SELECT ".join(", ", $this->FRAG['fields']);
        }
        function from(){
            echo " FROM ".$this->FRAG['tabn'];
        }
        function where(){
            echo " WHERE threadid = ".$this->FRAG['threadid'];
        }
        function order(){
            echo " ORDER BY ".$this->FRAG['orderby']." DESC";
        }
    }
    

    Ergebnis:

    SELECT mesgid, author, subject, mesg, mesgdate 
    FROM tunguskaforum WHERE threadid = 2 
    ORDER BY mesgdate DESC
    

    Also, je nachdem welches Statement gebaut werden soll, wird eine Instanz der entsprechenden Subklasse erzeugt. Ich habe hier die Ausgabe mal ins echo gepuffert, natürlich kann man das Ergebnis als String auch in einer Eigenschaft der Instanz puffern und dann mit __toString() ausgeben.

    Das Prinzip: Zum Erzeugen beliebiger SQL Strings werden nacheinander einfach nur die Methoden eines Interface aufgerufen was hier als eine Klasse implementiert ist. Und: Es werden immer alle Methoden in der gleichen Reihenfolge aufgerufen.

    MFG

    1. Tach!

      Die Erzeugung eines SQL Statements (String) lässt sich auch über ein Interface lösen.

      Du verwendest (wieder einmal) den Begriff Interface anders als es unter PHP und einer Reihe anderer Sprachen üblich ist.

      dedlfix.

      1. Ein Interface? Bitteschön:

        <?php
        
        interface SqlString{    
            function select();
            function from();
            function where();
            function order();
            function limit();
        }
        
        # Basisklasse
        class SqlBase{
            # SQL Fragmente erfassen
            function __construct($frag){
                $this->FRAG = $frag;
                $hthis->SQL = '';
            }
        }
        
        # Spezialisiere die Aufgabe
        # durch Überlagerung der Interfacemethoden
        class Thread extends SqlBase implements SqlString{
            function select(){
                echo "SELECT ".join(", ", $this->FRAG['fields']);
            }
            function from(){
                echo " FROM ".$this->FRAG['tabn'];
            }
            function where(){
                echo " WHERE threadid = ".$this->FRAG['threadid'];
            }
            function order(){
                echo " ORDER BY ".$this->FRAG['orderby']." DESC";
            }
            function limit(){}
        }
        
        
        # SQL zum Auslesen eines Thread
        $m = new Thread(array(
            'threadid' => 2,
            'tabn'     => 'tunguskaforum',
            'fields'   => array('mesgid','author','subject','mesg','mesgdate'),
            'orderby'  => 'mesgdate'
        ));
        
        # Rufe die Methoden des Interface auf
        $m->select();
        $m->from();
        $m->where();
        $m->order();
        $m->limit();
        

        MFG

    2. Hallo pl,

      Dein "interface" ist eine abstrakte Klasse, wie es aussieht. Dann kannst Du das auch so in PHP formulieren.

      Aber warum sollte man das hier tun:

      # Rufe die Methoden des Interface auf
      $m->select();
      $m->from();
      $m->where();
      $m->order();
      $m->limit();
      

      Das muss immer gemacht werden, um ein SQL Statement zu bekommen, ja? Dann kann man das auch als Teil einer getSQL() Methode an den SqlString hängen statt es vom Nutzer machen zu lassen.

      Abgesehen ist dein Beispiel viel zu simpel, bzw. es ist ein Builder für eine spezifische Query auf die Thread-Tabelle. MB baut einen generischen SQL Builder.

      Rolf

      --
      sumpsi - posui - clusi
      1. MB baut einen generischen SQL Builder.

        Bitteschön:

        <?php
        
        interface SqlString{    
            function select();
            function from();
            function where();
            function order();
            function limit();
        }
        
        # Basisklasse
        class SqlBase{
            # SQL Fragmente erfassen
            function __construct($frag){
                $this->FRAG = $frag;
                $hthis->SQL = '';
            }
            function __toString(){
                return $this->SQL;
            }
        }
        
        class Moon extends SqlBase implements SqlString{
            function select(){
                $this->SQL .= "SELECT ".join(", ", $this->FRAG['fields']);
            }
            function from(){
                $this->SQL .= " FROM ".$this->FRAG['tabn'];
            }
            function where(){
                $this->SQL .= " WHERE julianday > ".$this->FRAG['julianday'];
            }
            function order(){
            }
            function limit(){
                $this->SQL .= " LIMIT ".$this->FRAG['limit'];
            }
        }
        
        
        # SQL zum Auslesen der Mondphasen
        $m = new Moon(array(
            'julianday' => 2440588,
            'tabn'     => 'moon',
            'fields'   => array('julianday','phase'),
            'limit'  => 10
        ));
        
        # Rufe die Methoden des Interface auf
        $m->select();
        $m->from();
        $m->where();
        $m->order();
        $m->limit();
        
        # SQL String Ausgeben
        echo $m;
        
        SELECT julianday, phase FROM moon WHERE julianday > 2440588 LIMIT 10
        

        Genau deswegen ja bauen wir ein Interface, damit wir beliebiges SQL generieren. Es kommt darauf an das Prinzip und die Wirkungsweise eines Interface zu verstehen!

        MFG

        1. Tach!

          Genau deswegen ja bauen wir ein Interface, damit wir beliebiges SQL generieren. Es kommt darauf an das Prinzip und die Wirkungsweise eines Interface zu verstehen!

          Dann versuch doch bitte Interfaces im Sinne der OOP zu verstehen, oder verwende ein anderes Wort für das, was du sagen möchtest, das nicht mit dem bereits belegten Wort Interface bei OOP kollidiert.

          Bitteschön:

          Wenn du das Interface einfach nur nachträglich hineinstrickst, kannst du es auch weglassen.

          Das was du da gebaut hast, benötigt kein Interface. Es sei denn, die Implementationen der von SqlString abgeleiteten Klassen können voneinander abweichen. Das ist aber bei SQL-Statements nicht der Fall. Die haben eine genau definierte Syntax. Es ist nicht besonders sinnvoll, mehrere Implementationen zu schreiben, die alle zum selben Ergebnis kommen (müssen).

          Höre ich da "SQL-Dialekte" als Einwurf? Nun, wenn sich das Interface auf den kleinsten gemeinsam möglichen Funktionsumfang beschränkt, berücksichtigen wir ja nicht die Unterschiede in den Dialekten, sondern den kleinsten gemeinsamen Standard, und benötigen dafür auch wieder nur eine Klasse. Unterschiede beim Quotieren können durch Überschreiben der entsprechenden Hilfsmethoden gelöst werden.

          dedlfix.

          1. Das was du da gebaut hast, benötigt kein Interface.

            Was ich hier gezeigt habe ist ein Interface. Ein Interface baut man zum automatisieren von Abläufen in Gestalt von Methoden die dazu stets in einundderselben Reihenfolge aufgerufen werden (wie z.B. zum Erzeugen von SQL Statements).

            PHP versteht unter einem Interface welche Methoden eine Klasse implementieren muss.

            Von daher ist ein PHP'Interface eben kein Interface sondern ein Pragma.

            MFG

        2. Es kommt darauf an das Prinzip und die Wirkungsweise eines Interface zu verstehen!

          Es kommt darauf an, dass man verstehen muss, dass man verstehen muss, weil man sonst nicht versteht, dass man verstehen muss!

          1. Hallo,

            Es kommt darauf an das Prinzip und die Wirkungsweise eines Interface zu verstehen!

            Es kommt darauf an, dass man verstehen muss, dass man verstehen muss, weil man sonst nicht versteht, dass man verstehen muss!

            das hat einer mal viel treffender formuliert.

            So long,
             Martin

            --
            Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
            1. das hat einer mal viel treffender formuliert.

              Das ist mir zu niveauvoll 😉

              Dazu folgender Beitrag:

              https://www.youtube.com/watch?v=ElWOGVqNGTQ

          2. moin,

            Es kommt darauf an das Prinzip und die Wirkungsweise eines Interface zu verstehen!

            Es kommt darauf an, dass man verstehen muss, dass man verstehen muss, weil man sonst nicht versteht, dass man verstehen muss!

            da fällt mir was von Loriot ein 😀 Loriot - Bundestagsrede

            lgmb

            1. moin,

              @MB

              Es kommt darauf an das Prinzip und die Wirkungsweise eines Interface zu verstehen!

              Es kommt darauf an, dass man verstehen muss, dass man verstehen muss, weil man sonst nicht versteht, dass man verstehen muss!

              da fällt mir was von Loriot ein 😀 Loriot - Bundestagsrede

              Wenn das alles ist was Du von meinem Code verstanden hast, ist Programmieren nichts für Dich.

              MFG

              1. moin,

                da fällt mir was von Loriot ein 😀 Loriot - Bundestagsrede

                Wenn das alles ist was Du von meinem Code verstanden hast, ist Programmieren nichts für Dich.

                Lieber @pl: ich habe nicht auf dich geantwortet sonder auf @Mitleser.

                Außerdem was sollt das @pl!

                lgmb

                1. hi @MB

                  Du wolltest wissen wie Du Deinen Code besser organisieren kannst damit Du ihn später wiederaufnehmen kannst.

                  Des Weiteren hast Du festgestellt, daß Du mit Deinem Code in eine Sackgasse geraten bist. Das ist alles nichts Tragisches, das macht jeder mal durch und deswegen bist Du auch hier aufgeschlagen.

                  Was an Deinem Code schlecht ist und warum Du da feststeckst habe ich Dir erklärt. Und ich habe Dir eine Lösung vorgeschlagen, die typisch für Deine Aufgabenstellung ist und darin besteht daß man sowas mit einem Interface löst und Vererbung zweckmäßig einsetzt.

                  Das macht die Sache übersichtlich, skalierbar, wartungsfreundlich und erweiterbar.

                  MFG

                  1. moin,

                    Du wolltest wissen wie Du Deinen Code besser organisieren kannst damit Du ihn später wiederaufnehmen kannst.

                    Korrekt

                    Des Weiteren hast Du festgestellt, daß Du mit Deinem Code in eine Sackgasse geraten bist.

                    Nicht Korrekt. Ich wollte wisse wie ich es optimieren kann oder ob die Optimierung ausgereitzt ist

                    Das ist alles nichts Tragisches, das macht jeder mal durch und deswegen bist Du auch hier aufgeschlagen.

                    Korrekt

                    Was an Deinem Code schlecht ist und warum Du da feststeckst habe ich Dir erklärt. Und ich habe Dir eine Lösung vorgeschlagen […]

                    Das hast du. Nur nicht das was ich möchte. Man hat viel zu viele Einschränkungen mit deinem Code. So ist dein Code für mich, in seinem Umfng, völlig Wertlose.

                    […] die typisch für Deine Aufgabenstellung ist und darin besteht daß man sowas mit einem Interface löst und Vererbung zweckmäßig einsetzt.

                    Korrekt. Bin ganz bei dir. Aber sag sowas nicht mir. Ich fühl mich wieder wie als ob du mir das einmaleins bei bringen willst. Lass es!

                    Das macht die Sache übersichtlich, skalierbar, wartungsfreundlich und erweiterbar.

                    Siehe oben!

                    lgmb

                2. Lieber @pl: ich habe nicht auf dich geantwortet sonder auf @Mitleser.

                  @MB

                  Hast Du von dem jemals eine Lösung für eines Deiner hier dargestellten Probleme bekommen?

                  Wie auch immer, mein Artikel über PHP'Interfaces ist fertig. Der beschreibt das, was in der PHP'Dokumentation vergessen wurde zu erwähnen.

                  Mein nächster Artikel wird sich etwas näher mit Vererbung unter PHP befassen. Warum man überhaupt von einer Klasse erbt und was man erbt. Und in welchen Fällen man den Vaterkonstruktor aufruft und in welchen Fällen nicht. Und warum eine Überschreibung des Vaterkonstruktors das Erbe sämtlicher Eigenschaften ausschlägt die in der Vaterklasse nicht als statisch deklariert sind.

                  Das ist nämlich das was Du mit Deinem Code machst und damit auf die Ebene einer prozeduralen Programmierung zurückfällst.

                  MFG

                  1. Hallo pl,

                    Wie auch immer, mein Artikel über PHP'Interfaces ist fertig. Der beschreibt das, was in der PHP'Dokumentation vergessen wurde zu erwähnen.

                    Ich bin unterwältigt!

                    Merke: Ein PHP-Interface ist nur ein Pragma was einer Klasse vorschreibt, welche Methoden zu definieren sind.

                    Satz 1 der PHP Doku zu Interfaces:

                    Object interfaces allow you to create code which specifies which methods a class must implement, without having to define how these methods are implemented.

                    Das fehlt also schonmal nicht im PHP Handbuch. Weiter schreibst Du:

                    Das eigentliche Interface ist stets eine Methode oder eine Folge von Methoden, die eine Instanz derjenigen Klasse aufruft in welcher diese Methoden definiert sind.

                    Gut, dass das nicht im PHP Handbuch steht.

                    Man kann zwar behaupten, dass zu einer Schnittstelle immer zwei Seiten gehören, Stecker und Buchse, und somit der Aufrufer einer per implements genutzten interface Deklaration ebenfalls ein Interface darstellt.

                    Aber das verfehlt den Umstand, dass der Sinn einer Interface-Deklaration darin besteht

                    • einer Klasse vorzuschreiben, was sie zu implementieren hat
                    • einem Nutzer ein Versprechen zu machen, was das Objekt ihm anbietet

                    Wenn Du Methoden programmierst, die den Aufruf der in einem interface deklarieren Methoden beinhalten, dann sind diese Methoden nicht ihrerseits Interfaces. Sondern Adapter. Oder Dekoratoren.

                    Rolf

                    --
                    sumpsi - posui - clusi
                    1. FPDF hat ein Interface. Zu den Methoden Header und Footer heißt es:

                      This method is used to render the page footer. It is automatically called by AddPage() and Close() and should not be called directly by the application. The implementation in FPDF is empty, so you have to subclass it and override the method if you want a specific processing.

                      Das macht die Zweckbestimmung deutlich, hier ist es ein SQL-String bei FPDF ist es eine PDF-Datei, beides sind also Strings zu deren Erzeugung immer wieder dieselben Prozesse ablaufen.

                      Nur die Inhalte sind halt unterschiedlich, bei SQL unterschiedliche Where-Klausen, bei PDF unterschiedliche Header und Footer auf jeder Seite.

                      Man kann auch sagen, ein Interface implementiert ein Framework, es bildet den Rahmen für bestimmte Abläufe. Das ist die Idee hinter einem Interface und das wissen auch PHP-Programmierer.

                      MFG

                      1. Hallo pl,

                        jetzt rührst Du auch noch virtuelle Methoden in die Suppe, die als Platzhalter zum Customizing gedacht sind? Ich sehe da weder eine Interfacedeklaration noch eine implements-Angabe bei class FPDF.

                        /*******************************************************************************
                        * FPDF                                                                         *
                        *                                                                              *
                        * Version: 1.82                                                                *
                        * Date:    2019-12-07                                                          *
                        * Author:  Olivier PLATHEY                                                     *
                        *******************************************************************************/
                        
                        define('FPDF_VERSION','1.82');
                        
                        class FPDF
                        {
                        protected $page;               // current page number
                        protected $n;                  // current object number
                        protected $offsets;            // array of object offsets
                        

                        ein Interface implementiert ein Framework

                        Nein. Ein Interface deklariert eine Liste von Methoden, die der Implementierer des Interfaces anbieten muss. Das Interface implementiert überhaupt nichts.

                        Äh, hm. Wenn da nicht C# 8 und Java 8 mit ihren default implementations in Interfaces wären. Von Java habe ich keine Ahnung, und C#8 ist noch beta, von daher fehlt mir damit die Erfahrung.

                        Wie auch immer. Ein Interface definiert einen Vertrag. Wer es implementiert, verpflichtet sich, ihn einzuhalten. Wer es nutzt, verlässt sich darauf, dass er eingehalten wird. Nicht nur PHP Programmierer wissen das.

                        Rolf

                        --
                        sumpsi - posui - clusi
                        1. Wie auch immer. Ein Interface definiert einen Vertrag. Wer es implementiert, verpflichtet sich, ihn einzuhalten. Wer es nutzt, verlässt sich darauf, dass er eingehalten wird. Nicht nur PHP Programmierer wissen das.

                          Ein Interface definiert, was auch immer Rolf Rost, pl und Hotti für richtig erachten. Egal in welcher Sprache.

                        2. Genau. Die Besonderheit in PHP ist, daß man mit dem Schlüsselwort interface die Deklaration von der Definition trennen kann. Im Übrigen gibt es Interfaces nicht nur in PHP. Und In der Praxis ist es entscheidend zu wissen was man damit machen kann und wie man es zweckmäßig einsetzt. Genau das habe ich auch hier mit meinem Beispiel gezeigt, nämlich daß man infolge zweckmäßigen Einsatz eines Interface die Klassenflut des OP auf ganze 2 Klassen reduzieren kann! Und OOP+Vererbung sinnvoll anwendet. Alles Andere ist sowas von nebensächlich, aber das ist eben auch typisch für dieses Forum hier daß man sich an den Nebensächlichkeiten festkrallt ohne die Idee verstanden zu haben.

                          Und noch etwas. Sicher kennt Oracle kein LIMIT wie SQL aber man kann auch in Oracle ein Limit setzen nur halt mit einem anderen Syntax und deswegen kann eine diesbezügliche Funktion trotzdem limit heißen, da weiß jeder was damit gemeint ist. Und wenn wir schon bei diesem Thema sind, das hatten wir sogar hier neulich, in Oracle wird anders gequotet zum Vermeiden von SQL'Injectionen. Wenn man also ein universellen SQL-Builder schreibt, dann wird man hierzu die Methoden nutzen welcher der DB-Treiber in der jeweiligen Programmierspache hierzu bietet. PDO jedenfalls bietet keine quote-Methode und auch keine quote_identifier-Methode wie z.B. Perl::DBI.

                          MFG

                          1. Hallo pl,

                            PDO jedenfalls bietet keine quote-Methode

                            dieser Irrtum wird durch Wiederholen nicht besser.

                            PHP Programmierer wissen das.

                            Bei LIMIT hast du recht, das ist non-standard und verlangt unterschiedliches SQL. Ein Generator sollte hier das Strategiemuster einsetzen.

                            Rolf

                            --
                            sumpsi - posui - clusi
                          2. moin,

                            […] aber das ist eben auch typisch für dieses Forum hier daß man sich an den Nebensächlichkeiten festkrallt ohne die Idee verstanden zu haben.

                            ganz im Ernst. Warum läst du das SelfHTML-Forum nicht Forum sein, und findest dich damit ab deine Lehrinhalte zu lassen? Rein "empirisch" betrachtet: Du…

                            • verwirrst Leser (mich z.B.)
                            • bist ständig dabei (ich glaube du postest die meisten Beiträge)
                            • bringst keinen sinnhaften nennenswerten "lehrhaften" Beitrag
                            • postest ständig Lehrinhalte, wie das Entwickler-Einmaleins, was hier jeder drin haben sollte.

                            […] Wenn man also ein universellen SQL-Builder schreibt, dann wird man hierzu die Methoden nutzen welcher der DB-Treiber in der jeweiligen Programmierspache hierzu bietet.

                            Dewegen gibts Unverselle SQL Builder so einen wie ich ihn kreire

                            PDO jedenfalls bietet keine quote-Methode […].

                            PDO::quote

                            Ich hab selbst das Problem, mit dem Sprachverständnis (Schlaganfall, Globale Aphasie). Deswegen halte ich mich ja zurück.

                            Ein sehr konkretes aber abstraktes Beispiel: @Rolf B hat mich netterweise drauf hingewiesen, dass es "Equotation" nicht gibt, worauf ich mich bedankt habe und das Wort korrigiert habe. Mir wäre es nicht aufgefallen. Wenn ich z.B. lehrenderweise das Wörtchen "Equotation" verwendet hätte, würde es manche Leute, die von mir eine Dokumentation lesen, zu noch mehr Verwirrung führen. Also Bitte.

                            lgmb

                        3. moin,

                          Von Java habe ich keine Ahnung

                          Ich hab mit Java 8 gearbeitet - bin kein Profi! - und es ist so wie du vermutest. Aber wie gesagt, ich kann mich auch irren - bin kein Profi.

                          lgmb

                  2. moin,

                    Hast Du von dem jemals eine Lösung für eines Deiner hier dargestellten Probleme bekommen?

                    Warum stelle ich den hier Fragen wenn ich keine AW bekämme?

                    Wie auch immer, mein Artikel über PHP'Interfaces ist fertig.

                    Ich bitte dich es nicht zu posten. Folgende Grüne:

                    • "Vaterkonstrukt" ist nicht verständlich - für mich aber Aufgrund des Kontextes und weil ich dich kenne.
                    • "Interface" muss vom Begriff eindeutig sein. Nich wie bei dir. Du verwendest Elternklassen die abstrakt sein können und betitelst sie als Interface. DAS GEHT NICHT.

                    Wenn ein Mensch verstehen muss, was die Begriffe bedeuten, und deine Dokumentation liest, wird er mehr verwirrt als sonst. Bitte, bitte, lass es.

                    lgmb

        3. moin,

          Neben bei ewähnt: LIMIT ist von MySQL proprietär. Alle anderen Datenbanken Oracle usw. können mit LIMIT nichts anfangen und werfen n Fehler. Ich glaube das wollte @Rolf B unteranderem dir sagen.

          lgmb

          1. @MB

            Neben bei ewähnt: LIMIT ist von MySQL proprietär. Alle anderen Datenbanken Oracle usw. können mit LIMIT nichts anfangen und werfen n Fehler. Ich glaube das wollte @Rolf B unteranderem dir sagen.

            Überlege Dir eine Interface-Methode between()! Das ist das was ich Dir sagen wollte.

            Also wie man die Erzeugung eines SQL Statements auf Methoden aufteilt.

            MFG