Neuigkeiten
  • Die modified eCommerce Shopsoftware ist kostenlos, aber nicht umsonst.
    Spenden
  • Damit wir die modified eCommerce Shopsoftware auch zukünftig kostenlos anbieten können:
    Spenden
  • Thema: PHP - Framework OOP Datenbankklassen - Das M aus MVC

    Thomas K.

    • Mitglied
    • Beiträge: 214
    WICHTIG: Diese Anleitung richtet sich an fortgeschrittene Entwickler und soll Ihnen ein neues Hilfsmittel erklären. Es handelt sich um kein fertiges Modul!!!

    Hey,

    ich arbeite zur Zeit bei purpleleaves.de als Chef-Entwickler.
    Wir benutzen modified eCommerce Shopsoftware.
    Da wir ständig Custom-Lösungen benötigen bin ich also recht tief in xtc:m eingestiegen.
    Was mich anfangs sehr gestört hat, war die fehlende Objektorientierung und vor allem die fehlende objektgesteuerte Datenbankabwicklung. Da in xtc:m bereits ein templatingsystem vorhanden ist kann man sagen, dass mir das M aus MVC (http://de.wikipedia.org/wiki/Model_View_Controller) gefehlt hat.

    Ich arbeite bei mir bereits live mit dem System, das ich euch nun vorstelle.
    Vielleicht ist es ja genau das, was xtc:m einen Schritt weiter bringen kann.

    Ursprünglich von Kevin Skoglung auf Lynda.com´s "PHP - BEYOND THE BASICS" (absolut empfehlenswert - http://www.lynda.com/PHP-tutorials/php-with-OOP-beyond-the-basics/653-2.html) präsentiert, habe ich es an die Eigenarten von xtc:m angepasst. Ich werde nicht wirklich erklären wie es funktioniert, bzw. was genau welche Funktion macht. Solltet ihr euch mehr damit befassen wollen idealerweise obig angepriesenen Online-Video-Kurs anschauen. Den gibt es ebenfalls auf diverse dubiosen Webseiten kostenlos.

    Das framework ging vor allem davon aus, dass eine Klasse immer eine "id" hat, die immer automatisch gefüllt wird. Das ist in xtc:m leider selten der Fall, oft heißt sie anders, z.b. "reviews_id", aber manchmal wird diese id sogar "per hand" gesetzt.
    Diesen Teil habe ich angepasst, sodass diese Fälle abgedeckt worden sein sollten.

    Ich bin nun auf eure Reaktion gespannt, ob ihr es ebenfalls so genial wie ich finde, und ob und wie man das framework nun komplett integriert. Ich habe bisher nur einige wenigen Klassen erstellt. Es gibt noch viel zu tun.
    Interessant ist an dieser Stelle auch Late Static Binding. Die Klassenfunktionen lassen sich von der "database" Klasse bisher (also pre 5.3) nicht vererben. D.h. die DB-Funktionen sind in jeder Klasse erneut enthalten. Dort wird zwischen "own methods" und "common database methods" unterschieden. Zukünftig (mit lsb) sollten die nur noch in der database-klasse sein.

    Außerdem gibt es eine Session-Klasse, die aber bisher von mir nur minimal genutzt wird (is_logged_in()) sowie die customers_id des eingeloggten users lässt sich abfragen.

    Hier eine Beispiel Klasse customers.php

    Code: PHP  [Auswählen]
    <?php
    // If it's going to need the database, then it's
    // probably smart to require it before we start.
    require_once(LIB_PATH.DS.'database.php');

    class Customer extends DatabaseObject {
           
            protected static $table_name="customers";
            protected static $prim_key="customers_id";
            protected static $db_fields = array(
            'customers_id',
            'customers_cid',
            'customers_vat_id',
            'customers_vat_id_status',
            'customers_warning',
            'customers_status',
            'customers_gender',
            'customers_firstname',
            'customers_lastname',
            'customers_dob',
            'customers_email_address',
            'customers_default_address_id',
            'customers_telephone',
            'customers_fax',
            'customers_password',
            'customers_newsletter',
            'customers_newsletter_mode',
            'member_flag',
            'delete_user',
            'account_type',
            'password_request_key',
            'payment_unallowed',
            'shipping_unallowed',
            'refferers_id',
            'customers_date_added',
            'customers_last_modified');
           
            public $customers_id;
            public $customers_cid;
            public $customers_vat_id;
            public $customers_vat_id_status;
            public $customers_warning;
            public $customers_status;
            public $customers_gender;
            public $customers_firstname;
            public $customers_lastname;
            public $customers_dob;
            public $customers_email_address;
            public $customers_default_adress_id;
            public $customers_telephone;
            public $customers_fax;
            public $customers_password;
            public $customers_newsletter;
            public $customers_newsletter_mode;
            public $member_flag;
            public $delete_user;
            public $account_type;
            public $password_request_key;
            public $payment_unallowed;
            public $shipping_unallowed;
            public $refferers_id;
            public $customers_date_added;
            public $customers_last_modified;
           
           

            // own methods
            public function is_potential() {
                    return Kundengruppe::find_by_customers_id($this->customers_id);
            }
           
           
            public static function find_by_email($email) {
            $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name."
                    WHERE customers_email_address='{$email}' LIMIT 1"
    );
                    return !empty($result_array) ? array_shift($result_array) : false;
            }
           
            public function login() {
                    $email = $this->customers_email_address;
                    $password = $this->customers_password;
                   
                    if($customer = Customer::find_by_email($email)) {
                            $check_customer = xtc_db_fetch_array($check_customer_query);
                                    if (SESSION_RECREATE == 'True') {
                                            xtc_session_recreate();
                                    }

                                    # TODO Länder CHECK in OOP
                                    #$check_country_query = xtc_db_query("select entry_country_id, entry_zone_id from ".TABLE_ADDRESS_BOOK." where customers_id = '".(int) $check_customer['customers_id']."' and address_book_id = '".$check_customer['customers_default_address_id']."'");
                                    #$check_country = xtc_db_fetch_array($check_country_query);
           
                                    $_SESSION['customer_gender'] = $this->customers_gender;
                                    $_SESSION['customer_first_name'] = $this->customers_firstname;
                                    $_SESSION['customer_last_name'] = $this->customers_lastname;
                                    $_SESSION['customer_id'] = $this->customers_id;
                                    $_SESSION['customer_vat_id'] = $this->customers_vat_id;
                                    $_SESSION['customer_default_address_id'] = $this->customers_default_address_id;
                                    $_SESSION['customer_country_id'] = $this->entry_country_id;
                                    $_SESSION['customer_zone_id'] = $this->entry_zone_id;
           
                                    $datetime = date('Y-m-d H:i:s', time());
           
                                    # TODO Customers_info in OOP
                                    xtc_db_query("update ".TABLE_CUSTOMERS_INFO." SET customers_info_date_of_last_logon = now(), customers_info_number_of_logons = customers_info_number_of_logons+1 WHERE customers_info_id = '".(int) $this->customers_id."'");
                                    xtc_write_user_info((int) $this->customers_id);
                                    // restore cart contents
                                    $_SESSION['cart']->restore_contents();
                                   
                                    if (is_object($econda)) $econda->_loginUser();
                                    if (isset($_SESSION['REFERER']) && !empty($_SESSION['REFERER'])) {
                                            xtc_redirect(xtc_href_link($_SESSION['REFERER']));
                                    } elseif ($_SESSION['cart']->count_contents() > 0) {
                                            xtc_redirect(xtc_href_link(FILENAME_SHOPPING_CART),'NONSSL');
                        } else {
                            xtc_redirect(xtc_href_link(FILENAME_DEFAULT),'NONSSL');
                        }
                            #}
                    } else {
                            # FEHLER
                    }
            }
           

            // Common Database Methods
            public static function find_all() {
                    return self::find_by_sql("SELECT * FROM ".self::$table_name);
            }
           
            public static function find_by_id($id=0) {
                    $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE " . self::$prim_key . "='{$id}' LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
            }
           
            public static function find_by_sql($sql="") {
                    global $database;
                    $result_set = $database->query($sql);
                    $object_array = array();
                    while ($row = $database->fetch_array($result_set)) {
                            $object_array[] = self::instantiate($row);
                    }
                    return $object_array;
            }
           
            private static function instantiate($record) {
                    // Could check that $record exists and is an array
                    $object = new self;
                    // Simple, long-form approach:
                    // $object->id                          = $record['id'];
                    // $object->username    = $record['username'];
                    // $object->password    = $record['password'];
                    // $object->first_name = $record['first_name'];
                    // $object->last_name   = $record['last_name'];
           
                    // More dynamic, short-form approach:
                    foreach($record as $attribute=>$value){
                            if($object->has_attribute($attribute)) {
                                    $object->$attribute = $value;
                            }
                    }
                    return $object;
            }
           
            private function has_attribute($attribute) {
                    // We don't care about the value, we just want to know if the key exists
                    // Will return true or false
                    return array_key_exists($attribute, $this->attributes());
            }
           
            protected function attributes() {
                    // return an array of attribute names and their values
                    $attributes = array();
                    foreach(self::$db_fields as $field) {
                            if(property_exists($this, $field)) {
                                    $attributes[$field] = $this->$field;
                            }
                    }
                    return $attributes;
            }
           
            protected function sanitized_attributes() {
                    global $database;
                    $clean_attributes = array();
                    // sanitize the values before submitting
                    // Note: does not alter the actual value of each attribute
                    foreach($this->attributes() as $key => $value){
                            $clean_attributes[$key] = $database->escape_value($value);
                    }
                    return $clean_attributes;
            }
           
           
            public function save() {
                    // A new record won't have an id yet.
                    // KORREKTUR: Checken, falls id gesetzt, ob auch eintrag zu id existiert.
                    if(isset($this->{self::$prim_key})) {
                            if($xx = self::find_by_id($this->{self::$prim_key})) {
                                    return $this->update();
                            }
                    }
                    return $this->create();
            }
           
           
            public function create() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - INSERT INTO table (key, key) VALUES ('value', 'value')
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $sql = "INSERT INTO ".self::$table_name." (";
                    $sql .= join(", ", array_keys($attributes));
                    $sql .= ") VALUES ('";
                    $sql .= join("', '", array_values($attributes));
                    $sql .= "')";
                    if($database->query($sql)) {
                            $this->{self::$prim_key} = $database->insert_id();
                            return true;
                    } else {
                            return false;
                    }
            }
           
            public function update() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - UPDATE table SET key='value', key='value' WHERE condition
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $attribute_pairs = array();
                    foreach($attributes as $key => $value) {
                            $attribute_pairs[] = "{$key}='{$value}'";
                    }
                    $sql = "UPDATE ".self::$table_name." SET ";
                    $sql .= join(", ", $attribute_pairs);
                    $sql .= " WHERE " . self::$prim_key . "=". $database->escape_value($this->{self::$prim_key});
                    $database->query($sql);
                    return ($database->affected_rows() == 1) ? true : false;
            }
           
            public function delete() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - DELETE FROM table WHERE condition LIMIT 1
                    // - escape all values to prevent SQL injection
                    // - use LIMIT 1
                    $sql = "DELETE FROM ".self::$table_name;
                    $sql .= " WHERE {$this->prim_key}=". $database->escape_value($this->{$this->prim_key});
                    $sql .= " LIMIT 1";
                    $database->query($sql);
                    return ($database->affected_rows() == 1) ? true : false;
           
                    // NB: After deleting, the instance of User still
                    // exists, even though the database entry does not.
                    // This can be useful, as in:
                    //   echo $user->first_name . " was deleted";
                    // but, for example, we can't call $user->update()
                    // after calling $user->delete().
            }
           
            }
           
            ?>
    Mit den "// own methods" werdet ihr nichts anfangen können, die kann man sich eben selbst bei Bedarf hinzufügen. z.B. wie oben find_by_email

    Nun aber zum interessante Teil: Wie wendet man das framework an?

    Zuerst muss man die initialize.php einbinden und editieren.
    Diese befindet sich in dem von mir bereitgestellten Ordner "tk-includes".
    Einmalig muss man hier nun (wie auch in der "includes/configure.php" von xtc:m) den absoluten pfad zum html-verzeichnis des Webservers eintragen.
    Ich empfehle ein "require_once" aus der "includes/application_top.php" heraus (einfach als letztes hinzufügen), damit man jederzeit auf das framework zugreifen kann.

    Da das framework auch standalone funktioniert gibt es auch hier eine "config.php", in der man eine Datenbankverbindung herstellen könnte etc., außerdem werden hier die DB-Klassen eingebunden, sowie benötigte Pfade erstellt. Allerdings ist das nicht zwingend per Hand nötig, weil es dafür eine autoloader Funktion(in der "functions.php") gibt.
    Kritisch ist allerdings, falls eine Klasse bereits existiert und durch den autoloader neu deklariert werden soll. Deshalb gibt es auch keine "product.php" sondern eine "new_product.php" (weil die Klasse Product manchmal bereits existiert)

    Hier ein Anwendungsbeispiel (Demo unter http://www.purpleleaves.de/public):
    Ich benutze das Framework als stand-alone in diesem Fall, da ich mir den Templating-Teil für das Beispiel sparen wollte, ist aber problemlos möglich.
    Es wird ein customer (Datenbank customers) ausgewählt und bearbeitet. Beachtet bitte, wie man keine sql-Befehle mehr schreiben muss und mit extrem wenig Code auskommt. Ich habe glaube ich mehr kommentiert als wirklich geschrieben. Der Code müsste nur minimal angepasst werden, damit unbegrenzt viele customer angezeigt werden würden, die ebenfalls bearbeitbar wären.

    Code: PHP  [Auswählen]
    <?php
            require_once("../tk-includes/initialize.php");
    // Simulation. in xtc:m wäre, falls ein benutzer eingeloggt ist,
    // in der session die variable "customer_id" gesetzt.
    // die Klasse Session (also das Objekt $session) fängt diese nun id ab und
    // speichert sie in user_id
            $_SESSION['customer_id'] = '215';
            $session->check_login();
                   
            // Falls der Benutzer bearbeitet werden soll - Das Formular abgeschickt wurde
            if(isset($_POST['action']) and $_POST['action'] == 'edit_customer') {  
                    // Prüfen, ob es zur eingegebenen customers_id bereits einen Customer gibt
                    if($customer = Customer::find_by_id($_POST['customers_id'])) {
                            // tue nichts, bereits erfolgreich gefunden
                    } else {
                            // falls nicht, erzeuge einen neuen
                            $customer = new Customer();
                    }                      
    // hier beginnt die Magie!
    // Da $customer ein array-artiges Objekt ist, kann ich es mit foreach durchlaufen
    // $key gibt den key, also den Namen der aktuellen Klassenvariablen an, value den entsprechenden Wert.
                    foreach($customer as $key => $value) {
    // Die ID (wie auch immer sie heißt) soll nicht verändert werden können, überspringe also
                            if($key == Customer::$prim_key) {
                                    continue;
                            }
    // Prüft in einer Schleife, ob für Post-Variablen mit
     //name = klassenvariable gesetzt und nicht leer sind
                            if(isset($_POST[$key]) and $_POST[$key] != '') {
                                    // füge diese Post-Variablen nun dem Objekt hinzu.
                                    $customer->$key = $_POST[$key];
                            }
    // Dieser Befehl übernimmt für euch die Komplette Datenbankabwicklung!
    // Existiert zum primary-key (der id, wie auch immer sie heißt) bereits ein Objekt,
    // wird der Datenbankeintrag geupdated
    // ansonten wird ein neuer erzeugt.  
                            $customer->save();             
                    }
                    redirect_to($_SERVER['PHP_SELF']);
            }

    // statischer Aufruf der Klasse Customer
    // Zurückgegeben wird in diesem Fall ein Array von Objekten
    // In einer If-Abfrage geklammert wird false zurückgegeben, falls kein Objekt gefunden wird
            if($customer = Customer::find_by_id($session->user_id)) {
                    ?>
                    <h2>Eingeloggten Customer bearbeiten</h2>
                    <form action='<?php echo $_SERVER['PHP_SELF']; ?>' method='post'>
                    <table>
                    <?php
                    $x = 0;
                    foreach($customer as $key => $value) {
                            // zweispaltige darstellung
                            if($x % 2 == 0) {
                                    echo "<tr>";
                            }
                            echo "<td>";
                            // Aktuelle Klassenvariable
                            echo $key . ":";
                            echo "</td><td>";
    // Erzeuge ein Inputfeld dass als name den namen der Klassenvariablen hat,
    // als Wert deren Wert falls gesetzt
                            echo "<input type='text' name='".$key."' ";
    // Falls $key = id (wie auch immer sie heißt), dann input feld readonly schalten
                            if($key == Customer::$prim_key) {
                                    echo "readonly='readonly'";
                            }
                            echo "value='".$value."' />";
                            echo "</td>";
                            // zweispaltige darstellung
                            if($x % 2 == 1) {
                                    echo "</tr>";
                            }
                            $x++;
                    }
                   
                    ?>
                    <tr><td colspan='2'><input type='submit' /></td></tr>
                    </table>
                    <input type='hidden' name='action' value='edit_customer' />
                    </form>
                    <?php
            } else {
                    echo "Benutzer mit customers_id " . $session->user_id . " nicht vorhanden";
            }
           
            ?>

    Ich hoffe der Code ist mit den Kommentaren allen verständlich - spätestens mit der Online-Demo

    Das ganze lässt sich problem auch mit dem Templatingsystem verbinden.
    Allerdings wird mir die Schreiberei gerade zu viel um noch so ein Beispiel zu produzieren.

    Ich habe euch erstmal nur wenige Klasse bereitgestellt. Ich habe schon einige mehr vorbereitet, allerdings noch nicht komplett und getestet. Da viele DB-Tabellen purpleleaves-spezifische(meine Arbeit) Änderungen erfahren haben will ich das lieber noch einfach in Ruhe durchgehen.

    Was noch zu tun ist:
    - Session->Klasse ausbauen!
    - Alle Datenbank Tabellen als Klasse erzeugen
    - Late static binding recherchieren und ggf. umsetzen.

    Framework unter: http://purpleleaves.de/public/php-framework.zip

    Ist schon spät und ich recht müde, hoffe ich hab jetzt keine Fehler unnötig eingebaut und freue mich auf euer Feedback und Fragen.

    Linkback: https://www.modified-shop.org/forum/index.php?topic=21012.0

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #1 am: 09. Juli 2012, 10:49:02
    Beispiel vorerst offline!

    DokuMan

    • modified Team
    • Beiträge: 6.669
    • Geschlecht:
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #2 am: 09. Juli 2012, 21:41:35
    Ein sehr interessanter und wünschenswerter Ansatz ;)
    Allerdings mit sehr viel Umprogrammierungsaufwand verbunden.

    Das "Hauptproblem" wird der Bruch mit der Abwärtskompatibiliät sein

    cYbercOsmOnauT

    • modified Team
    • Beiträge: 914
    • Geschlecht:
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #3 am: 09. Juli 2012, 22:38:09
    Aus Entwicklersicht kann ich Dir nur gratulieren Thomas. Sehr gute Arbeit. Jedoch wie Dokuman schon schrieb, wenn wir das ganze Projekt nun komplett umschreiben, haben wir ein großes Problem mit der Kompatibilität, denn viele Modifikationen die direkt in den Code greifen und nicht modular aufgebaut sind, werden nicht mehr funktionieren.

    Ich denke nicht, das so eine "Nach mir die SYNFLOOD"-Lösung unserem Projekt Vorteile brächte. Vielleicht könnte man ja einen parallelen Zweig erzeugen der auf Dein Framework basiert und schauen, wie er bei den Usern von modified eCommerce Shopsoftware ankommt. Leider weiß ich aus Erfahrung, das nicht immer das Beste auch "gewinnt".

    Ich möchte trotzdem nochmal einen Dank aussprechen für einen Menschen, der den OS Gedanken noch hoch hält und der Communoty auch etwas zurück geben möchte. Ist heutzutage leider rar gesät.
    Viele Grüße,
    Tekin Birdüzen - Zend Certified Engineer

    vr

    • modified Team
    • Beiträge: 2.664
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #4 am: 10. Juli 2012, 00:20:04
    Hallo Thomas,

    danke für Deinen Beitrag. Objektorientierte DB-Abstraktionsschichten haben allerdings auch Nachteile. Das hat damit zu tun, dass versucht wird, zwei Konzepte zu vereinen, die wenig miteinander zu tun haben. Komponenten einer Datenbank sind keine Objekte, sondern Tabellen und Relationen. Hier wird nicht in Objekten, Vererbung, Methoden, Abläufen etc gedacht, sondern in *Mengen*. Das wird meistens ausgeblendet in dem Wunsch, den Zugriff zur Datenbank so aussehen zu lassen wie den Rest der Anwendungslogik.

    Ich habe Systeme gesehen, die dennoch diesen Ansatz verfolgt haben und bspw in Java oder Ruby objektrelationale Mapper einsetzten. Das Ergebnis war eine erbärmliche Performance, sobald nennenswerte Datenmengen zu verwalten waren. Und eine Datenbankferne, die dem Umstand überhaupt nicht gerecht wird, dass hauptsächlich die Datenbankoperationen einer Anwendung über deren Performance und Konsistenz (= Datenqualität) entscheiden. Zwangsläufig dabei eine Reduzierung einer mächtigen Sprache (SQL) auf eine minimale Teilmenge an Funktionen wie Auslesen, Einfügen und Löschen von Sätzen. In der Folge werden dann komplexere Abfragen durch Schleifen auf höherer Ebene abgebildet, und das skaliert nicht. Und wenn es zu offensichtlich Käse ist, doch der RAW-Modus des Frameworks benutzt und direkt auf die DB durchgegriffen. SQL ist nicht Assembler, sondern selbst eine Hochsprache, und die in einer anderen Hochsprache, zudem mit einem komplett anderen Paradigma so abzubilden, dass sie gleichwertig oder besser benutzbar ist, ist keine Kleinigkeit. Ich habe jedenfalls noch nichts Überzeugendes gesehen.

    Ich halte OO-DB-Abstraktionsschichten deswegen nur unter engen Voraussetzungen für praktikabel. Wenn man sicher ist, dass nur elementare Operationen benötigt werden und die Datenmengen nicht zu groß sind, kann es klappen.

    Dass modified eCommerce Shopsoftware grundsätzlich, auch unabhängig von der DB, mehr Abstraktion und Klassenorientierung gebrauchen könnte, ist richtig. Aber was die Daten angeht, würde ich sogar den umgekehrten Weg gehen und Anwendungslogik in die DB verlagern, die Abstraktion in der DB ansiedeln, denn eine Datenbank ist bereits eine mächtige Maschine, von allen beteiligten Komponenten die mächtigste im Umgang mit Daten. Leider wird der Ansatz von MySQL so schlecht unterstützt, dass er nicht Frage kommt. Andere DBs wie Postgres, Firebird und die kommerziellen DBs bieten da wesentlich mehr. Aber die Alternative kann auch nicht sein, das Rad neu zu erfinden.

    Grüße, Volker

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #5 am: 10. Juli 2012, 12:56:07
    Hallo und danke für euch Antworten,

    ich wollte vor allem meine Arbeit mit euch teilen.
    Ich werde es definitiv weiter verwenden, wenn es euch nicht passt ist das natürlich eure Sache.
    Trotzdem bin ich der festen Überzeugung, dass man sich modernen Programmierstandards (MVC, DRY) anpassen muss, um wettbewerbsfähig zu bleiben.

    Anbei auch mal ein Screenshot von auto-completion in eclipse pdt mit aptana. Absolut Nutzerfreundlich.

    @dokuman und cYbercOsmOnauT:
    Wie gesagt nutze ich das Framework bereits jetzt, sprich parallel. Alle Funktionalität, die ich neu entwickle, beruht darauf, wenn es Sinn macht.
    Abwärtskompatibel in Bezug auf PHP gäbe es erst Probleme, wenn man "late static binding" verwendet, um den "common database methods"-Code aus jeder Klasse in die "database.php" einzubinden. Das ist aber bisher nicht der Fall, sondern Zukunftsgeflüster. "late static binding" setzt PHP 5.3 voraus.
    Abwärtskompatibel in Bezug auf fehlende Daten ist für mich kein anderer Unterschied wie der Wechsel von SP1a zu SP1d.

    @vr: Ich fürchte ich bin anwendungsbezogen grundsätzlich anderer Meinung als du. Aber trotzdem ist auch mit so einem framework trotzdem die Möglichkeit gegeben komplexe SQL-Befehle einzubauen. Jede Klasse enthält einen "own methods" und "common database methods" Teil. Wenn komplexere SQL-Querys Sinn machen, kann man diese auch weiterhin einsetzen.
    Bzgl. der großen Datenmengen habe ich vor zwei Jahren mit dem gleichen Framework einen Online-Shop für Großhändler geschrieben. Darin hat ein Großhändler seine Produktdaten (mindestens jeweils 20000 Artikel im BNN-Format, sprich ca. 170 Felder) als CSV eingespielt, die dann jeweils als Objekt erzeugt wurden, per Schleife geparst wurden, etc.. Die Dateien waren mehrere MB groß, trotzdem hat das Einspielen im Shop nur sehr kurze Zeit benötigt.
    Meiner Meinung nach ist SQL zwar ein mächtiges Werkzeug, aber ein extrem Fehleranfälliges.
    Heutzutage ist man aus den großen PHP-Frameworks an MVC gewöhnt und kann vor allem seine Entwicklungszeit damit extrem reduzieren.
    Jedem Entwickler ist natürlich klar, dass ein Framework immer Nachteile hat, du nennst sie selbst, trotzdem verzichtet er deshalb nicht auf seine Hilfsmittel, wenn er dadurch viel schneller arbeiten kann, oder?

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #6 am: 13. Juli 2012, 12:59:49
    Es gab einen Fehler in der delete().
    Da ich fast nie lösche ist es nicht aufgefallen. die .zip ist korrigiert.
    Der korrekte Code lautet:

    public function delete() {
          global $database;
          // Don't forget your SQL syntax and good habits:
          // - DELETE FROM table WHERE condition LIMIT 1
          // - escape all values to prevent SQL injection
          // - use LIMIT 1
          $sql = "DELETE FROM ".self::$table_name;
          $sql .= " WHERE " . self::$prim_key . "=". $database->escape_value($this->{self::$prim_key});
          $sql .= " LIMIT 1";
          $database->query($sql);
          return ($database->affected_rows() == 1) ? true : false;

          // NB: After deleting, the instance of User still
          // exists, even though the database entry does not.
          // This can be useful, as in:
          //   echo $user->first_name . " was deleted";
          // but, for example, we can't call $user->update()
          // after calling $user->delete().
       }

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #7 am: 18. Oktober 2012, 14:33:07
    hier eine neue initialize.php mit aktualisierter autoload funktion sowie eine angepasste customers.php

    tk-includes/initialize.php:

    Code: PHP  [Auswählen]
    <?php

    // Define the core paths
    // Define them as absolute paths to make sure that require_once works as expected

    // DIRECTORY_SEPARATOR is a PHP pre-defined constant
    // (\ for Windows, / for Unix)
    defined('DS') ? null : define('DS', DIRECTORY_SEPARATOR);
    if($_SERVER['SERVER_NAME'] == 'localhost') {
            defined('SITE_ROOT') ? null : define('SITE_ROOT', DS.'Users'.DS.'tk'.DS.'Desktop'.DS.'web' . DS . 'purpleleaves2');
    } else {
            defined('SITE_ROOT') ? null : define('SITE_ROOT', DS.'vrmd'.DS.'homepages'.DS.'u43542'.DS.'purpleleaves');
    }


    defined('LIB_PATH') ? null : define('LIB_PATH', SITE_ROOT.DS.'tk-includes');

    // load config file first
    require_once(LIB_PATH.DS.'config.php');

    // load basic functions next so that everything after can use them
    require_once(LIB_PATH.DS.'functions.php');

    // load core objects
    require_once(LIB_PATH.DS.'session.php');

    require_once(LIB_PATH.DS.'database.php');
    require_once(LIB_PATH.DS.'database_object.php');

    // load database-related classes




    function tk_includes_loader($class_name) {
            $class_name = strtolower($class_name);
            $path = LIB_PATH.DS."{$class_name}.php";
            if(file_exists($path)) {
                    require_once($path);
            } else {
           
            }
    }

    # execute spl autoloeader instead of __autoload
    spl_autoload_register("tk_includes_loader");

    # Kann erst hier auf admin checken, da bei session deklarierung db strukturen noch nicht geladen
    $session->check_is_admin();

    ?>

    customers.php:

    Code: PHP  [Auswählen]
    <?php
    // If it's going to need the database, then it's
    // probably smart to require it before we start.
    require_once(LIB_PATH.DS.'database.php');

    class Customer extends DatabaseObject {
           
            protected static $table_name="customers";
            static $prim_key="customers_id";
            protected static $db_fields = array(
            'customers_id',
            'customers_cid',
            'customers_vat_id',
            'customers_vat_id_status',
            'customers_warning',
            'customers_status',
            'customers_gender',
            'customers_firstname',
            'customers_lastname',
            'customers_dob',
            'customers_email_address',
            'customers_default_address_id',
            'customers_telephone',
            'customers_fax',
            'customers_password',
            'customers_newsletter',
            'customers_newsletter_mode',
            'member_flag',
            'delete_user',
            'account_type',
            'password_request_key',
            'payment_unallowed',
            'shipping_unallowed',
            'refferers_id',
            'customers_date_added',
            'customers_last_modified');
           
            public $customers_id;
            public $customers_cid;
            public $customers_vat_id;
            public $customers_vat_id_status;
            public $customers_warning;
            public $customers_status;
            public $customers_gender;
            public $customers_firstname;
            public $customers_lastname;
            public $customers_dob;
            public $customers_email_address;
            public $customers_default_adress_id;
            public $customers_telephone;
            public $customers_fax;
            public $customers_password;
            public $customers_newsletter;
            public $customers_newsletter_mode;
            public $member_flag;
            public $delete_user;
            public $account_type;
            public $password_request_key;
            public $payment_unallowed;
            public $shipping_unallowed;
            public $refferers_id;
            public $customers_date_added;
            public $customers_last_modified;
           
           

           
            public function is_potential() {
                    return Kundengruppe::find_by_customers_id($this->customers_id);
            }
           
           
            public static function find_by_email($email) {
            $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name."
                    WHERE customers_email_address='{$email}' LIMIT 1"
    );
                    return !empty($result_array) ? array_shift($result_array) : false;
            }
           
            public static function get_all_designers() {
                    $result_array = self::find_by_sql("SELECT DISTINCT c.* FROM `customers` c, submission s WHERE c.customers_id = s.customers_id");
                    return $result_array;
            }
           
            public function login() {
                    $email = $this->customers_email_address;
                    if($customer = Customer::find_by_email($email)) {
                            $check_customer = xtc_db_fetch_array($check_customer_query);
                                    if (SESSION_RECREATE == 'True') {
                                            xtc_session_recreate();
                                    }

                                    # TODO Länder CHECK in OOP
                                    #$check_country_query = xtc_db_query("select entry_country_id, entry_zone_id from ".TABLE_ADDRESS_BOOK." where customers_id = '".(int) $check_customer['customers_id']."' and address_book_id = '".$check_customer['customers_default_address_id']."'");
                                    #$check_country = xtc_db_fetch_array($check_country_query);
           
                                    $_SESSION['customer_gender'] = $this->customers_gender;
                                    $_SESSION['customer_first_name'] = $this->customers_firstname;
                                    $_SESSION['customer_last_name'] = $this->customers_lastname;
                                    $_SESSION['customer_id'] = $this->customers_id;
                                    $_SESSION['customer_vat_id'] = $this->customers_vat_id;
                                    $_SESSION['customer_default_address_id'] = $this->customers_default_address_id;
                                    $_SESSION['customer_country_id'] = $this->entry_country_id;
                                    $_SESSION['customer_zone_id'] = $this->entry_zone_id;
           
                                    $datetime = date('Y-m-d H:i:s', time());
           
                                    xtc_db_query("update ".TABLE_CUSTOMERS_INFO." SET customers_info_date_of_last_logon = now(), customers_info_number_of_logons = customers_info_number_of_logons+1 WHERE customers_info_id = '".(int) $this->customers_id."'");
                                    xtc_write_user_info((int) $this->customers_id);
                                    // restore cart contents
                                    $_SESSION['cart']->restore_contents();
                                   
                                    if (is_object($econda)) $econda->_loginUser();
                                    if (isset($_SESSION['REFERER']) && !empty($_SESSION['REFERER'])) {
                                            # Führendes / entfernen
                                            while(substr($_SESSION['REFERER'], 0, 1)  == "/") {
                                                    $_SESSION['REFERER'] = substr($_SESSION['REFERER'], 1);
                                            }
                                            xtc_redirect(xtc_href_link($_SESSION['REFERER']));
                                    } elseif ($_SESSION['cart']->count_contents() > 0) {
                                            xtc_redirect(xtc_href_link(FILENAME_SHOPPING_CART),'NONSSL');
                        } else {
                            xtc_redirect(xtc_href_link(FILENAME_DEFAULT),'NONSSL');
                        }

                    } else {
                            # FEHLER
                    }
            }
           

    // Common Database Methods
            public static function find_all() {
                    return self::find_by_sql("SELECT * FROM ".self::$table_name);
            }

            public static function find_by_id($id=0) {
                    $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE " . self::$prim_key . "='{$id}' LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
            }
           
            public static function find_by_cid($cid=0) {
                    $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE customers_cid='{$cid}' LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
            }

            public static function find_by_sql($sql="") {
                    global $database;
                    $result_set = $database->query($sql);
                    $object_array = array();
                    while ($row = $database->fetch_array($result_set)) {
                            $object_array[] = self::instantiate($row);
                    }
                    return $object_array;
            }

            private static function instantiate($record) {
                    // Could check that $record exists and is an array
                    $object = new self;
                    // Simple, long-form approach:
                    // $object->id                          = $record['id'];
                    // $object->username    = $record['username'];
                    // $object->password    = $record['password'];
                    // $object->first_name = $record['first_name'];
                    // $object->last_name   = $record['last_name'];

                    // More dynamic, short-form approach:
                    foreach($record as $attribute=>$value){
                            if($object->has_attribute($attribute)) {
                                    $object->$attribute = $value;
                            }
                    }
                    return $object;
            }

            private function has_attribute($attribute) {
                    // We don't care about the value, we just want to know if the key exists
                    // Will return true or false
                    return array_key_exists($attribute, $this->attributes());
            }

            protected function attributes() {
                    // return an array of attribute names and their values
                    $attributes = array();
                    foreach(self::$db_fields as $field) {
                            if(property_exists($this, $field)) {
                                    $attributes[$field] = $this->$field;
                            }
                    }
                    return $attributes;
            }

            protected function sanitized_attributes() {
                    global $database;
                    $clean_attributes = array();
                    // sanitize the values before submitting
                    // Note: does not alter the actual value of each attribute
                    foreach($this->attributes() as $key => $value){
                            $clean_attributes[$key] = $database->escape_value($value);
                    }
                    return $clean_attributes;
            }


            public function save() {
                    // A new record won't have an id yet.
                    // KORREKTUR: Checken, falls id gesetzt, ob auch eintrag zu id existiert.
                    if(isset($this->{self::$prim_key})) {
                            if($xx = self::find_by_id($this->{self::$prim_key})) {
                                    return $this->update();
                            }
                    }
                    return $this->create();
            }


            public function create() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - INSERT INTO table (key, key) VALUES ('value', 'value')
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $sql = "INSERT INTO ".self::$table_name." (";
                    $sql .= join(", ", array_keys($attributes));
                    $sql .= ") VALUES ('";
                    $sql .= join("', '", array_values($attributes));
                    $sql .= "')";
                    if($database->query($sql)) {
              $this->{self::$prim_key} = $database->insert_id();
              return true;
                    } else {
                            return false;
                    }
            }

            public function update() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - UPDATE table SET key='value', key='value' WHERE condition
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $attribute_pairs = array();
                    foreach($attributes as $key => $value) {
                            $attribute_pairs[] = "{$key}='{$value}'";
                    }
                    $sql = "UPDATE ".self::$table_name." SET ";
                    $sql .= join(", ", $attribute_pairs);
                    $sql .= " WHERE " . self::$prim_key . "=". $database->escape_value($this->{self::$prim_key});
                    $database->query($sql);
                    return ($database->affected_rows() == 1) ? true : false;
            }

            public function delete() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - DELETE FROM table WHERE condition LIMIT 1
                    // - escape all values to prevent SQL injection
                    // - use LIMIT 1
                    $sql = "DELETE FROM ".self::$table_name;
                    $sql .= " WHERE " . self::$prim_key . "=". $database->escape_value($this->{self::$prim_key});
                    $sql .= " LIMIT 1";
                    $database->query($sql);
                    return ($database->affected_rows() == 1) ? true : false;

                    // NB: After deleting, the instance of User still
                    // exists, even though the database entry does not.
                    // This can be useful, as in:
                    //   echo $user->first_name . " was deleted";
                    // but, for example, we can't call $user->update()
                    // after calling $user->delete().
            }

    }

    ?>
     

    und

    kundengruppen.php
    diese Datei wird benötigt, um zu checken, ob ein Kunde "vollwertig" ist, oder ob noch Informationen fehlen.

    Code: PHP  [Auswählen]
    <?php
    // If it's going to need the database, then it's
    // probably smart to require it before we start.
    require_once(LIB_PATH.DS.'database.php');

    class Kundengruppe extends DatabaseObject {
           
            protected static $table_name="kundengruppen";
            protected static $db_fields = array('id', 'customers_id', 'kundengruppe');
           
            public $id;
            public $customers_id;
            public $kundengruppe;

            // Own Database Methods
            public static function find_by_customers_id($customersid=0) {
                    $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE customers_id={$customersid} LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
            }
           

            // Common Database Methods
            public static function find_all() {
                    return self::find_by_sql("SELECT * FROM ".self::$table_name);
      }
     
      public static function find_by_id($id=0) {
        $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE id={$id} LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
      }
     
      public static function find_by_sql($sql="") {
        global $database;
        $result_set = $database->query($sql);
        $object_array = array();
        while ($row = $database->fetch_array($result_set)) {
          $object_array[] = self::instantiate($row);
        }
        return $object_array;
      }

            private static function instantiate($record) {
                    // Could check that $record exists and is an array
        $object = new self;
                    // Simple, long-form approach:
                    // $object->id                          = $record['id'];
                    // $object->username    = $record['username'];
                    // $object->password    = $record['password'];
                    // $object->first_name = $record['first_name'];
                    // $object->last_name   = $record['last_name'];
                   
                    // More dynamic, short-form approach:
                    foreach($record as $attribute=>$value){
                      if($object->has_attribute($attribute)) {
                        $object->$attribute = $value;
                      }
                    }
                    return $object;
            }
           
            private function has_attribute($attribute) {
              // We don't care about the value, we just want to know if the key exists
              // Will return true or false
              return array_key_exists($attribute, $this->attributes());
            }

            protected function attributes() {
                    // return an array of attribute names and their values
              $attributes = array();
              foreach(self::$db_fields as $field) {
                if(property_exists($this, $field)) {
                  $attributes[$field] = $this->$field;
                }
              }
              return $attributes;
            }
           
            protected function sanitized_attributes() {
              global $database;
              $clean_attributes = array();
              // sanitize the values before submitting
              // Note: does not alter the actual value of each attribute
              foreach($this->attributes() as $key => $value){
                $clean_attributes[$key] = $database->escape_value($value);
              }
              return $clean_attributes;
            }
           
            public function save() {
              // A new record won't have an id yet.
              return isset($this->id) ? $this->update() : $this->create();
            }
           
            public function create() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - INSERT INTO table (key, key) VALUES ('value', 'value')
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
              $sql = "INSERT INTO ".self::$table_name." (";
                    $sql .= join(", ", array_keys($attributes));
              $sql .= ") VALUES ('";
                    $sql .= join("', '", array_values($attributes));
                    $sql .= "')";
              if($database->query($sql)) {
                $this->id = $database->insert_id();
                return true;
              } else {
                return false;
              }
            }

            public function update() {
              global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - UPDATE table SET key='value', key='value' WHERE condition
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $attribute_pairs = array();
                    foreach($attributes as $key => $value) {
                      $attribute_pairs[] = "{$key}='{$value}'";
                    }
                    $sql = "UPDATE ".self::$table_name." SET ";
                    $sql .= join(", ", $attribute_pairs);
                    $sql .= " WHERE id=". $database->escape_value($this->id);
              $database->query($sql);
              return ($database->affected_rows() == 1) ? true : false;
            }

            public function delete() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - DELETE FROM table WHERE condition LIMIT 1
                    // - escape all values to prevent SQL injection
                    // - use LIMIT 1
              $sql = "DELETE FROM ".self::$table_name;
              $sql .= " WHERE id=". $database->escape_value($this->id);
              $sql .= " LIMIT 1";
              $database->query($sql);
              return ($database->affected_rows() == 1) ? true : false;
           
                    // NB: After deleting, the instance of User still
                    // exists, even though the database entry does not.
                    // This can be useful, as in:
                    //   echo $user->first_name . " was deleted";
                    // but, for example, we can't call $user->update()
                    // after calling $user->delete().
            }

    }

    ?>
     

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #8 am: 18. Oktober 2012, 14:37:16
    dann noch die neue functions.php

    Code: PHP  [Auswählen]
    <?php

    function diff_time($differenz)
    {
            $woche = floor($differenz / (3600*24*7));
            $tag  = floor($differenz / (3600*24));
            $std  = floor($differenz / 3600 % 24);
            $min  = floor($differenz / 60 % 60);
            $sek  = floor($differenz % 60);

            return array("sek"=>$sek,"min"=>$min,"std"=>$std,"tag"=>$tag,"woche"=>$woche);
    }

    # falle außerdem fall ab, wenn date, und nicht date+time gesendet wird und gleich 0 ist
    function checkDateTime($data) {
            if (date('Y-m-d H:i:s', strtotime($data)) == $data or date('Y-m-d H:i:s', strtotime($data)) == $data  . " 00:00:00") {
                    return true;
            } else {
                    return false;
            }
    }


    function blowup($delimiter,$string){
            if(strpos($string,$delimiter)===false){
                    return false;
            }
            else{
                    return explode($delimiter, $string);
            }
    }

    function tk_get_action_name_by_uri($uri) {
            $uri = tk_get_href_link(tk_get_org_uri($uri));
            # ggf. auftretende führende / entfernen
            if(substr($uri, 0, 1)  == "/") {
                    while(substr($uri, 0, 1)  == "/") {
                            $uri = substr($uri, 1);
                    }
            }
            if($x = blowup("/", $uri)) {
                    return $x[0];
            } else {
                    return $uri;
            }
           
    }

    function tk_get_org_uri($uri) {
            # falls ein ? vorhanden, dann muss nicht rückwirkend umgewandelt werden
            # TODO Test verfeinern
            if($y = blowup("?", $uri)) {
                    return $uri;
            }
            if($y = blowup(":", $uri)) {
                    return $uri;
            }
            if($y = blowup(".php", $uri)) {
                    return $uri;
            }
           
            #  ggf. mehrere führende / entfernen
            if(substr($uri, 0, 1)  == "/") {
                    while(substr($uri, 0, 1)  == "/") {
                            $uri = substr($uri, 1);
                    }
                    $uri = "/" . $uri;
            }
            $str = "";
            $remember = "";
            $remember_profile = false;
            if($x = blowup("/", $uri)) {
                    $counter = 0;
                    foreach($x as $xi) {
                           
                            if($xi == '') {
                                    $remember = "/";
                                    continue;
                            } else if($y = blowup(",", $xi)) {
                                    # falls profile, und kommendes value paar mit komma getrennt, merke nicht mehr
                                    if ($remember_profile == true) {
                                            $remember_profile = false;
                                    }
                                    $str .= $y[0] . "=" . $y[1] . "&";
                            } else {
                                    # falls profile, dann ggf. erinnern. u.U. ist ID nicht extra gesetzt (Sonderfall)
                                    if($xi == 'profile' ) {
                                            $remember_profile = true;
                                            $str .= $xi . ".php?";
                                    } else if ($remember_profile == true) {
                                            $remember_profile = false;
                                            $str .= "id=". $xi. "&";
                                           
                                    } else {
                           
                                            $str .= $xi . ".php?";
                                    }
                                   
                            }
                            #echo $str . "<br />";
                    }
                    $str = substr($str, 0, -1);
                    return $remember . $str;
            } else {
                    return $uri;
            }
           
    }


    function tk_get_href_link($uri) {
            // index.php?id=4&abc=18&zz=2
            if($x = blowup(".php", $uri)) {
                    // x0 = index
                    $filename = $x[0];
                    if($y = blowup("?", $x[1])) {
                            // y0 = ""
                            // y1 = id=4&abc=18&zz=2
                            if($z = blowup("&", $y[1])) {
                                    $filename .= "/";
                                    foreach($z as $zi) {
                                            if($g = blowup("=", $zi)) {
                                                    $filename .= $g[0] . "," . $g[1];
                                            } else {
                                                    continue;
                                            }
                                            $filename .= "/";
                                    }
                                   
                                    # entferne letztes "/"
                                    $filename = substr($filename, 0, -1);
                                    return $filename;
                                    #return $uri;
                            } else {
                                    //z0 = id=4
                                    //z1 = abc=18
                                    //z2 = zz=2
                                    // falls parameter-?, aber keine werte
                                    $a = explode("=", $y[1]);
                                    if($a[0] == 'id') {
                                            $erg = $filename . "/id," . $a[1];
                                    } else {
                                            $erg = $filename . "/" . $a[0] . ",". $a[1];
                                    }
                                    return $erg;
                            }
                    } else {
                            return $filename;
                    }
                   
            } else {
                    return $uri;
            }
    }
    /*
    function pl_html_email_tpl($email_tpl_title, $email_tpl_message) {
            include('email_template.php');
            return $erg;
    }*/


    function is_logged_in() {
            if (!isset ($_SESSION['customer_id'])) {
                    return false;
            } else {
                    return true;
            }
    }

    function strip_zeros_from_date( $marked_string="" ) {
      // first remove the marked zeros
      $no_zeros = str_replace('*0', '', $marked_string);
      // then remove any remaining marks
      $cleaned_string = str_replace('*', '', $no_zeros);
      return $cleaned_string;
    }

    function redirect_to( $location = NULL ) {
      if ($location != NULL) {
        header("Location: {$location}");
        exit;
      }
    }

    function test_exist_not_empty($array) {
            $erg = true;
            foreach($array as $arr) {
                    #echo $_REQUEST[$arr];
                    if(isset($_REQUEST[$arr]) and $_REQUEST[$arr] != '') {
                           
                    } else {
                            $erg = false;
                    }
                   
            }
            return $erg;
    }

    function output_message($message="") {
      if (!empty($message)) {
        return "<p class=\"message\">{$message}</p>";
      } else {
        return "";
      }
    }
    /*
    function __autoload($class_name) {
      $class_name = strtolower($class_name);
      $path = LIB_PATH.DS."{$class_name}.php";
      if(file_exists($path)) {
        require_once($path);
      } else {
            # xtc läd auch andere klassen!!! dieses autoload ist nur für die tk-includes, deshalb keine harten fehlermeldungen aka die()
                    #die("The file {$class_name}.php could not be found.");
            }
    }*/







    function include_layout_template($template="") {
            include(SITE_ROOT.DS.'public'.DS.'layouts'.DS.$template);
    }

    function log_action($action, $message="") {
            $logfile = SITE_ROOT.DS.'logs'.DS.'log.txt';
            $new = file_exists($logfile) ? false : true;
      if($handle = fopen($logfile, 'a')) { // append
        $timestamp = strftime("%Y-%m-%d %H:%M:%S", time());
                    $content = "{$timestamp} | {$action}: {$message}\n";
        fwrite($handle, $content);
        fclose($handle);
        if($new) { chmod($logfile, 0755); }
      } else {
        echo "Could not open log file for writing.";
      }
    }

    ?>

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #9 am: 18. Oktober 2012, 14:38:29
    mir fehlt gerade die Zeit meine Anpassung im Detail zu entfernen.

    Die funtkionen tk_get_href_xxx passt geparste uris (/index.php?id=14&action=blabla) an an meine URL Settings per .htdocs. Würde empfehlen die Funktionen zu entschärfen, da sie für euch so nicht funktionieren werden!

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #10 am: 18. Oktober 2012, 14:40:41
    /tk-includes/session.php

    Code: PHP  [Auswählen]
    <?php
    // A class to help work with Sessions
    // In our case, primarily to manage logging users in and out

    // Keep in mind when working with sessions that it is generally
    // inadvisable to store DB-related objects in sessions

    class Session {
           
            private $logged_in=false;
            public $is_admin=false;
            public $user_id;
            public $message;
            public $error = array();
           
            function __construct() {
            #xtc modified anpassung
            if(isset($_SESSION) and $_SESSION['language'] != '') {
           
            } else {
                    session_start();
            }
                   
                   
                    $this->check_message();
                    $this->check_login();
                    $this->check_error();
        if($this->logged_in) {
          // actions to take right away if user is logged in
        } else {
          // actions to take right away if user is not logged in
        }
            }
           
      public function is_logged_in() {
        return $this->logged_in;
      }
     
      public function is_admin() {
            return $this->is_admin;        
      }
     
      public function log_out() {
            $this->logged_in = false;
            $this->is_admin = false;
            $this->user_id = null;
      }

            /*
             * Ohne Anpassung nicht anwendbar in XTC:M
             

            public function login($user) {
        // database should find user based on username/password
        if($user){
          $this->user_id = $_SESSION['user_id'] = $user->id;
          $this->logged_in = true;
        }
      }
     
      public function logout() {
        unset($_SESSION['user_id']);
        unset($this->user_id);
        $this->logged_in = false;
      }*/

            public function append_message($msg="") {
                    if(!empty($msg)) {
                            $_SESSION['message'] .= $msg;
                    }
            }
     
            public function message($msg="") {
              if(!empty($msg)) {
                // then this is "set message"
                // make sure you understand why $this->message=$msg wouldn't work
                $_SESSION['message'] = $msg;
              } else {
                // then this is "get message"
                            return $this->message;
              }
            }
           
            public function check_is_admin() {
                    if($customer = Customer::find_by_id($this->user_id) and $customer->customers_status == '0') {
                            $this->is_admin = true;
                    } else {
                            $this->is_admin = false;
                    }
            }

            private function check_login() {
        if(isset($_SESSION['customer_id'])) {
          $this->user_id = $_SESSION['customer_id'];
          $this->logged_in = true;
        } else {
          unset($this->user_id);
          $this->logged_in = false;
        }
      }
     
            private function check_message() {
                    // Is there a message stored in the session?
                    if(isset($_SESSION['message'])) {
                            // Add it as an attribute and erase the stored version
          $this->message = $_SESSION['message'];
          unset($_SESSION['message']);
        } else {
          $this->message = "";
        }
            }
           
            public function error_save() {
                    $_SESSION['tk_error'] = $this->error;
            }
           
            private function check_error() {
                    // Is there a message stored in the session?
                    if(isset($_SESSION['tk_error'])) {
                            // Add it as an attribute and erase the stored version
                            $this->error = $_SESSION['tk_error'];
                            unset($_SESSION['tk_error']);
                    } else {
                            $this->error = "";
                    }
            }
           
           
           
    }

    $session = new Session();
    $message = $session->message();

    ?>
     

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #11 am: 18. Oktober 2012, 14:50:27
    /tk-includes/address_book.php
    Code: PHP  [Auswählen]
    <?php
    // If it's going to need the database, then it's
    // probably smart to require it before we start.
    require_once(LIB_PATH.DS.'database.php');

    class Address_book extends DatabaseObject {
           
            protected static $table_name="address_book";
            protected static $db_fields=array(
            "address_book_id",
            "customers_id",
            "entry_gender",
            "entry_company",
            "entry_firstname",
            "entry_lastname",
            "entry_street_address",
            "entry_suburb",
            "entry_postcode",
            "entry_city",
            "entry_state",
            "entry_country_id",
            "entry_zone_id",
            "address_date_added",
            "address_last_modified",
            "address_class"
            );
           
            public $address_book_id;
            public $customers_id;
            public $entry_gender;
            public $entry_company;
            public $entry_firstname;
            public $entry_lastname;
            public $entry_street_address;
            public $entry_suburb;
            public $entry_postcode;
            public $entry_city;
            public $entry_state;
            public $entry_country_id;
            public $entry_zone_id;
            public $address_date_added;
            public $address_last_modified;
            public $address_class;
           
    // Own Methods
           
    // Common Database Methods
            public static function find_all() {
                    return self::find_by_sql("SELECT * FROM ".self::$table_name);
      }
     
      public static function find_by_id($id=0) {
        $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE address_book_id={$id} LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
      }
     
      public static function find_by_sql($sql="") {
        global $database;
        $result_set = $database->query($sql);
        $object_array = array();
        while ($row = $database->fetch_array($result_set)) {
          $object_array[] = self::instantiate($row);
        }
        return $object_array;
      }

            private static function instantiate($record) {
                    // Could check that $record exists and is an array
        $object = new self;
                    // Simple, long-form approach:
                    // $object->id                          = $record['id'];
                    // $object->username    = $record['username'];
                    // $object->password    = $record['password'];
                    // $object->first_name = $record['first_name'];
                    // $object->last_name   = $record['last_name'];
                   
                    // More dynamic, short-form approach:
                    foreach($record as $attribute=>$value){
                      if($object->has_attribute($attribute)) {
                        $object->$attribute = $value;
                      }
                    }
                    return $object;
            }
           
            private function has_attribute($attribute) {
              // We don't care about the value, we just want to know if the key exists
              // Will return true or false
              return array_key_exists($attribute, $this->attributes());
            }

            protected function attributes() {
                    // return an array of attribute names and their values
              $attributes = array();
              foreach(self::$db_fields as $field) {
                if(property_exists($this, $field)) {
                  $attributes[$field] = $this->$field;
                }
              }
              return $attributes;
            }
           
            protected function sanitized_attributes() {
              global $database;
              $clean_attributes = array();
              // sanitize the values before submitting
              // Note: does not alter the actual value of each attribute
              foreach($this->attributes() as $key => $value){
                $clean_attributes[$key] = $database->escape_value($value);
              }
              return $clean_attributes;
            }
           
            public function save() {
              // A new record won't have an id yet.
              return isset($this->address_book_id) ? $this->update() : $this->create();
            }
           
            public function create() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - INSERT INTO table (key, key) VALUES ('value', 'value')
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
              $sql = "INSERT INTO ".self::$table_name." (";
                    $sql .= join(", ", array_keys($attributes));
              $sql .= ") VALUES ('";
                    $sql .= join("', '", array_values($attributes));
                    $sql .= "')";
              if($database->query($sql)) {
                $this->address_book_id = $database->insert_id();
                return true;
              } else {
                return false;
              }
            }

            public function update() {
              global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - UPDATE table SET key='value', key='value' WHERE condition
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $attribute_pairs = array();
                    foreach($attributes as $key => $value) {
                      $attribute_pairs[] = "{$key}='{$value}'";
                    }
                    $sql = "UPDATE ".self::$table_name." SET ";
                    $sql .= join(", ", $attribute_pairs);
                    $sql .= " WHERE address_book_id=". $database->escape_value($this->address_book_id);
              $database->query($sql);
              return ($database->affected_rows() == 1) ? true : false;
            }

            public function delete() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - DELETE FROM table WHERE condition LIMIT 1
                    // - escape all values to prevent SQL injection
                    // - use LIMIT 1
              $sql = "DELETE FROM ".self::$table_name;
              $sql .= " WHERE address_book_id=". $database->escape_value($this->address_book_id);
              $sql .= " LIMIT 1";
              $database->query($sql);
              return ($database->affected_rows() == 1) ? true : false;
           
                    // NB: After deleting, the instance of User still
                    // exists, even though the database entry does not.
                    // This can be useful, as in:
                    //   echo $user->first_name . " was deleted";
                    // but, for example, we can't call $user->update()
                    // after calling $user->delete().
            }

    }

    ?>
     

    /tk-includes/customers_info.php
    Code: PHP  [Auswählen]
    <?php
    // If it's going to need the database, then it's
    // probably smart to require it before we start.
    require_once(LIB_PATH.DS.'database.php');

    class Customers_info extends DatabaseObject {
           
            protected static $table_name="customers_info";
            protected static $db_fields=array(
            "customers_info_id",
            "customers_info_date_of_last_logon",
            "customers_info_number_of_logons",
            "customers_info_date_account_created",
            "customers_info_date_account_last_modified",
            "global_product_notifications"
            );
           
            public $customers_info_id;
            public $customers_info_date_of_last_logon;
            public $customers_info_number_of_logons;
            public $customers_info_date_account_created;
            public $customers_info_date_account_last_modified;
            public $global_product_notifications;

           
    // Own Methods
           
    // Common Database Methods
            public static function find_all() {
                    return self::find_by_sql("SELECT * FROM ".self::$table_name);
      }
     
      public static function find_by_id($id=0) {
        $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE customers_info_id={$id} LIMIT 1");
                    return !empty($result_array) ? array_shift($result_array) : false;
      }
     
      public static function find_by_sql($sql="") {
        global $database;
        $result_set = $database->query($sql);
        $object_array = array();
        while ($row = $database->fetch_array($result_set)) {
          $object_array[] = self::instantiate($row);
        }
        return $object_array;
      }

            private static function instantiate($record) {
                    // Could check that $record exists and is an array
        $object = new self;
                    // Simple, long-form approach:
                    // $object->id                          = $record['id'];
                    // $object->username    = $record['username'];
                    // $object->password    = $record['password'];
                    // $object->first_name = $record['first_name'];
                    // $object->last_name   = $record['last_name'];
                   
                    // More dynamic, short-form approach:
                    foreach($record as $attribute=>$value){
                      if($object->has_attribute($attribute)) {
                        $object->$attribute = $value;
                      }
                    }
                    return $object;
            }
           
            private function has_attribute($attribute) {
              // We don't care about the value, we just want to know if the key exists
              // Will return true or false
              return array_key_exists($attribute, $this->attributes());
            }

            protected function attributes() {
                    // return an array of attribute names and their values
              $attributes = array();
              foreach(self::$db_fields as $field) {
                if(property_exists($this, $field)) {
                  $attributes[$field] = $this->$field;
                }
              }
              return $attributes;
            }
           
            protected function sanitized_attributes() {
              global $database;
              $clean_attributes = array();
              // sanitize the values before submitting
              // Note: does not alter the actual value of each attribute
              foreach($this->attributes() as $key => $value){
                $clean_attributes[$key] = $database->escape_value($value);
              }
              return $clean_attributes;
            }
           
            public function save() {
                     // A new record won't have an id yet.
                     // KORREKTUR: Checken, falls id gesetzt, ob auch eintrag zu id existiert.
                    if(isset($this->customers_info_id)) {
                            if($ci = self::find_by_id($this->customers_info_id)) {
                                    return $this->update();
                            }
                    }
                    return $this->create();
            }
           
            public function create() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - INSERT INTO table (key, key) VALUES ('value', 'value')
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
              $sql = "INSERT INTO ".self::$table_name." (";
                    $sql .= join(", ", array_keys($attributes));
              $sql .= ") VALUES ('";
                    $sql .= join("', '", array_values($attributes));
                    $sql .= "')";
              if($database->query($sql)) {
                $this->customers_info_id = $database->insert_id();
                return true;
              } else {
                return false;
              }
            }

            public function update() {
              global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - UPDATE table SET key='value', key='value' WHERE condition
                    // - single-quotes around all values
                    // - escape all values to prevent SQL injection
                    $attributes = $this->sanitized_attributes();
                    $attribute_pairs = array();
                    foreach($attributes as $key => $value) {
                      $attribute_pairs[] = "{$key}='{$value}'";
                    }
                    $sql = "UPDATE ".self::$table_name." SET ";
                    $sql .= join(", ", $attribute_pairs);
                    $sql .= " WHERE customers_info_id=". $database->escape_value($this->customers_info_id);
              $database->query($sql);
              return ($database->affected_rows() == 1) ? true : false;
            }

            public function delete() {
                    global $database;
                    // Don't forget your SQL syntax and good habits:
                    // - DELETE FROM table WHERE condition LIMIT 1
                    // - escape all values to prevent SQL injection
                    // - use LIMIT 1
              $sql = "DELETE FROM ".self::$table_name;
              $sql .= " WHERE customers_info_id=". $database->escape_value($this->customers_info_id);
              $sql .= " LIMIT 1";
              $database->query($sql);
              return ($database->affected_rows() == 1) ? true : false;
           
                    // NB: After deleting, the instance of User still
                    // exists, even though the database entry does not.
                    // This can be useful, as in:
                    //   echo $user->first_name . " was deleted";
                    // but, for example, we can't call $user->update()
                    // after calling $user->delete().
            }

    }

    ?>
     

    Thomas K.

    • Mitglied
    • Beiträge: 214
    Re: PHP - Framework OOP Datenbankklassen - Das M aus MVC
    Antwort #12 am: 18. Oktober 2012, 14:51:49
    Hier die Hilfstabelle kundengruppen
    Code: SQL  [Auswählen]
    --
    -- Tabellenstruktur für Tabelle `kundengruppen`
    --

    CREATE TABLE IF NOT EXISTS `kundengruppen` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `customers_id` VARCHAR(255) COLLATE latin1_german2_ci NOT NULL,
      `kundengruppe` VARCHAR(255) COLLATE latin1_german2_ci NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE=latin1_german2_ci AUTO_INCREMENT=2364 ;

    --
    -- Daten für Tabelle `kundengruppen`
    --
    Modulshop - Eine große Auswahl an neuen und hilfreichen Modulen für die modified eCommerce Shopsoftware
    13 Antworten
    6780 Aufrufe
    21. August 2009, 13:24:23 von Tomcraft