intro wiki download guestbook contact

WebTek::Model

Beschreibung

Stellt die Verbindung zwischen einer Datenbanktabelle und einem Perl- Object, welches in WebTek verwendet wird, her.

Anwendung

zuerst einmal muss eine Datenbanktabelle angelegt werden. z.B. mit:

CREATE TABLE person (
   id int(10) NOT NULL auto_increment,
   name varchar(100) NOT NULL,
   create_time datetime NOT NULL,
   properties tinytext,
   PRIMARY KEY id (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

das Charset sollte immer mit Utf8 definiert sein, um Probleme mit der Zeichenkonvertierung zu vermeiden. Desweiteren muss jede Tabelle einen PRIMARY KEY besitzen. Jetzt erstellen wir das Model (= einfach ein Perl Modul) Test::Model::Person. In diesem Model schreiben wir folgenden Code:

package Test::Model::Person;
 
use strict;
use WebTek::Globals;
 
use base qw( WebTek::Model );
  
1;

und schon sind wir fertig. Wir können jetzt z.B. einen neuen Eintrag in die Tabelle machen:

my $person = Test::Model::Person->new(
   'name' => 'max'
   'create_time' => '2006-03-20 12:00:00'
);
$person->save();

oder diesen Eintrag fetchen (aus der Datenbank holen):

my $person = Test::Model::Person->find_one('name' => 'max');

des weitern gibt es in Test::Model::Person Methoden für alle Tablecolumns. d.h. es funktioniert:

print $person->name;
print $person->create_time;

oder wir können die Datenbankreihe auch ändern:

$person->name('prozessor13');
$person->create_time(date('now'));
$person->save;

und schließlich können wir die Reihe auch wieder löschen:

$person->delete();

Naming Convention

  • der Name der Tabelle muss immer gleich dem Namen des Model sein. d.h. bei der Tabelle person funktionieren folgende Klassennamen:
    • Test::Model::Person
    • Person
    • Test::Model::User::Person
    • falls aus irgendeinem Grund der Name des Models ungleich dem Namen der Tabelle sind, kann auch die Funktion TABLE_NAME gesetzt werden. Mehr dazu spaeter.
  • die Row- Id (sofern es eine gibt) muss immer id heissen, wenn sie, mit der automatischen Model Logik, auch als Fremdschlüssel funktioniern soll.
  • Fremdschlüssel müssen immer mit id aufhören. z.b. _user_id, folder_id ...
  • Boolean- Felder müssen mit is_, has_ oder show_ beginnen. z.b. is_enabled, has_photos, show_folder, ...

Fremdschlüssel (= Foreign Keys)

Es gibt die Möglichkeit, daß Tabellen andere Tabellen referenzieren. Hierfür dienen Fremdschlüssel (= Foreign Keys). Diese Beziehungen werden vom Model automatisch erkannt, wenn eine Tabellenspalte den Namen xyz_id hat, wobei xyz der Name des (Foreign) Models ist. Wenn aus irgendwelchen Gründen andere Foreign Key Beziehungen hergestellt werden wollen/müssen, dann kann die Methode has_a verwenden werden. Wie has_a genau verwendet wird, ist weiter unten beschrieben.
Zusätzlich werden noch neue accessors angelegt. d.h. wenn eine Spalte mit user_id existiert, dann gibt es folgende Methoden:

$model->user_id(); # liefert die id
$model->user();  # liefert das User Object für die user_id

Models in einer Pageklasse verwenden

Wenn ein Model in einer Pageklasse verwendet wird, dann gibt es die Möglichkeit, daß das Model automatisch Macros, für die jeweiligen Tabellenspalten, in der Pageklasse erzeugt. Das geschieht, wenn das Model ge'use'd wird. Hierzu gibt es folgende Syntax:

use Test::Model::Person
   'accessor' => 'person',
   'macros' => [ 'name', 'create_time' ],
   'public' => [ 'name', 'person', 'create_time' ];

Was heist das? Der accessor ist die Funktion, welche auf eine Instanz von dem Model zugreift. Für das setzten dieser Instanz ist man selbst (in der Pageklasse, z.b. im Konstuktor, verantwortlich. mehr dazu im Tutorial. Für die Liste, die bei macros, übergeben wird, werden Macros in der Pageklasse erstellt. Zusätzlich kann man noch in der public Liste die Methoden definieren, welche auch das Attribute Public besitzen sollen (damit sie in den Subklassen verwendet werden können).

Datentypen

Zusatzlich zu SCALARen werten kann man in der Datenbank date und struct werte abspeichern. date speichert Datum und Uhrzeit in die Datenbank, und mit struct kann man Perl- Objekte als JSON Strings in die Datenbank speichern. Der Datentyp date wird automatisch verwendet, wenn die Tabellenspalte mit datetime deklariert ist, den Datentyp struct muss man per Hand festlegen. Hierzu definieren wir die methode DATATYPES:

sub DATATYPES { {
   'properties' => DATA_TYPE_STRUCT,
} }

mit dieser Definition kann man folgendes machen:

$person->properties({'a' => 1, 'b' => 2});
$person->save;
$person->properties->{'a'};

Weiters werden folgende Tabellendefinitionen in folgende Datentypen umgewandelt.

  • int, double, float, decimal, ... werden zu DATA_TYPE_NUMBER
  • is_xyz, has_xyz, ... werden zu DATA_TYPE_BOOLEAN
  • date, time, year, ... werden zu DATA_TYPE_DATE
  • blob wird zu SCALAR (=byte string)
  • alle anderen relationen (sofern sie nicht SCALAR sein sollen) muessen in DATATYPES definiert werden

Model Eigenschaften

Mit folgenden Methoden kann man die Model Eigenschaften manipulieren:

  • TABLE_NAME liefert den Tabellennamen für das Model
  • PROTECTED alle Columns (=Spalten) welche als Protected markiert sind, können mit der Funktion update (weiter unten beschrieben) nicht automatisch gesetzt werden. Das hat den Sinn, damit man kein Form- Hacking betreiben werden kann. z.b:

    sub PROTECTED { [ 'id', 'create_time' ] }
    

  • PROPERTIES hier kann man pro Column eine Regular Expression oder Code Referenz angeben, welche bei der Fehlerüberprüfung angewendet wird. z.b:

    sub PROPERTIES { {
       'name' => '^.{3,20}$'
       'properties' => sub {
          my ($class, $name, $value) =  @_;
          return length $value < 65535;
       },
    } }
    

  • primary_keys retourniert eine Array- Reference mit allen Primary Key Spaltennamen.
  • columns retourniert eine Array- Reference mit Hash- Referencen für jede Spalte. Jede Hash- Reference hat folgende Keys:
    • name beinhaltet den Namen der Column
    • type mit dem Column Typ
    • nullable beinhaltet yes oder no, jenachdem ob die Column NULL-able ist.
    • default beinhaltet die default Value
    • length beinhaltet die maximale Länge von dem Datebankfeld (bei Mysql ist das die Anzahl der Zeichen, bei Postgres die Anzahl der Bytes).
  • foreign_keys liefert eine Array- Reference auf lauter Array- Referencen. folgendes Code Beispiel soll als weitere Erklärung dienen:

    foreach my $f_key (@{$model->foreign_keys()}) {
       my ($column, $accessor, $model, $constructor) = @$f_key;
    }
    

  • has_a($name, $model, $constructor) erstellt eine Foreign Key Verbindung zu einem anderen Model. Es ist zu beachten, wenn Models verwendet werden, welche keine Kinder von WebTek::Model sind, daß diese die Methode new(%param) und id unterstützen werden. Jetzt noch eine Erklärung über die Methodenparameter von has_a:
    • $name definiert den Namen der Column (=Spalte). Der Name kann mit oder ohne id sein. d.h. _user_id und user funktionieren gleich.
    • $model hier kann optional ein Model Klassenname übergeben werden, von welchem Typ der Foreign Key sein soll. Wenn $model nicht definiert ist, dann ist der Name der Klasse gleich dem Namen der Column und der Klassenprefix wird gleichbehalten. d.h: für die column user_id in der tabelle photo und dem Model Test::Model::Photo wir als Klassennamen Test::Model::User angenommen.
    • $constructor hier kann, ebenfalls optional, der Konstruktor angegeben werden, mit welchem das Foreign Model erstellt wird. Wird kein Konstruktor angegeben, wird new verwendet.

Methoden

  • new(%content) erstellt ein neues Model. Optional können gleich Content- Werte mit übergeben werden (PROTECTED Parameter werden ignoriert).
  • init initialisiert das Model. Die dafür notwendigen Information kommen vom DB Objekt über die Methoden table_info und primary_keys.
  • update(%content) updated das Model mit den übergebenen Content- Werten (PROTECTED Parameter werden ignoriert).
  • describe retourniert einen String mit allen Information über das Model (ähnlich dem desc tablename in Mysql).
  • to_string retourniert einen String mit allen Content- Werten für das Model.
  • to_perl retourniert eine Hash Referenz mit allen Content- Werten für das Model.

Methoden zum Model fetchen

  • find(%params) macht ein SELECT auf die Datenbank. die WHERE- Clause wird durch die übergebenen parameter bestimmt:
    • order definiert ORDER BY des Ergebnisses,
    • combine bestimt wie die Parameter miteinander verknüpft werden. Gültige Werte sind and und or, wobei and als default verwendet wird.
    • limit definiert LIMIT im Sql
    • offset definiert OFFSET im Sql
    • FETCH definiert den fetch part vom SELECT:

      Test::Model::Person->find('FETCH' => 'name'); // wird zu SELECT name from person
      

    • alle anderen parameter werden in die WHERE clause mit key = 'value' eingebunden. Wobei es hier auch wieder mehr Moeglichkeiten gibt:
      • mehrere Werte fuer eine Column erlauben:

        Test::Model::Person->find('id' => [1, 2, 3]);
        // wird zu SELECT * from person where id = 1 or id = 2 or id = 3)
        

      • die Kombination beeinflussen:

        Test::Model::Persion->find('name' => 'max', 'or id' => [ 1, 2, 3]);
        // wird zu SELECT * from person where name = 'max' or (id=1 or id=2 or id=3)
        

      • den match beeinflussen:

        Test::Model::Persion->find('name like' => '%max%');
        // wird zu SELECT * from person where name like '%max%'
        

      • man kann natuerlich auch alles wild miteinander kombinieren
  • find_one(%params) ist genau gleich wie find(%params), mit dem Unterschied, daß immer nur maximal eine Zeile (und zwar die erste) als Model Objekt zurückgegben wird. Zusätzlich speichert das Model das Ergebnis in den Cache, falls dieser über use WebTek::Cache aktiviert wurde, oder liefert das Ergebnis aus diesem (siehe WebTek::Cache.
  • count(%params) gleich wie die find methode, allerdings wird hier nur die zahl der gefundenen Reihen retourniert (select count() from ...)
  • where($where, @args) hier kann die WHERE- Clause nach belieben selbst definieren.

    Test::Model::Person->where(qq{
       id in (1, 2, 3)
       and name like '%max%'
       order by create_time
    })
    

  • count_where gleich wie count, nur fuer where.

Methoden zum Persistieren / Löschen

  • save speichert das Model in die Datenbank. Vor dem speichern wird noch die Mehtode is_valid aufgerufen. Wenn dieses false liefert, wird eine WebTek::Exception::ModelInvalid geworfen.
  • delete löscht das Model aus der Datenbank (das Model- Objekt besteht aber weiterhin, nur die Zeile aus der Datenbank ist weg).

Methoden über den Zustand des Models

  • is_valid liefert 0 oder 1, jenachdem ob das Model gültig ist oder nicht, sprich, ob alle Content- Werte gültig sind oder nicht.
  • error_on($column) liefert 0 oder 1, jenachdem ob die Column (=Spalte) gültig ist oder nicht.
  • error_keys liefert eine Array- Reference mit lauter Message Keys. Ein Message Key setzt sich zusammen aus dem Modelname.column_name.fehlergrund. Als Fehlergrund gibt es entweder invalid oder empty.

Events