Wie soll ein Modell in MVC strukturiert sein? [geschlossen]


551

Ich habe gerade einen Überblick über das MVC-Framework und frage mich oft, wie viel Code im Modell enthalten sein soll. Ich neige dazu, eine Datenzugriffsklasse zu haben, die Methoden wie diese hat:

public function CheckUsername($connection, $username)
{
    try
    {
        $data = array();
        $data['Username'] = $username;

        //// SQL
        $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE Username = :Username";

        //// Execute statement
        return $this->ExecuteObject($connection, $sql, $data);
    }
    catch(Exception $e)
    {
        throw $e;
    }
}

Meine Modelle sind in der Regel eine Entitätsklasse, die der Datenbanktabelle zugeordnet ist.

Sollte das Modellobjekt alle Eigenschaften der Datenbankzuordnung sowie den obigen Code haben, oder ist es in Ordnung, den Code zu trennen, der tatsächlich für die Datenbank funktioniert?

Werde ich am Ende vier Schichten haben?


133
Warum fangen Sie Ausnahmen, nur um sie wieder zu werfen?
Bailey Parker

9
@ Elias Van Ootegem: Sie haben den Punkt verpasst. In diesem Fall ist es sinnlos, sie zu fangen.
Karoly Horvath

4
@ Elias Van Ootegem: nicht wahr? Wenn es mit Rethrow funktioniert, bedeutet dies, dass eine obere Ebene die Ausnahme abfängt. Aber wenn es einen gibt, dann hätte er ihn ohne dieses sinnlose Umwerfen gefangen ... (wenn Sie ihn immer noch nicht bekommen, verspotten Sie bitte einen kleinen Testcode)
Karoly Horvath

3
@ Elias Van Ootegem: Ich habe keine Ahnung, wovon Sie sprechen. Wenn Sie eine Ausnahme auf einer bestimmten Ebene nicht behandeln, bedeutet dies nicht, dass die App angehalten wird. Bitte konstruieren Sie ein Codebeispiel (oder genauer gesagt: Fehler beim Konstruieren), bei dem dieses erneute Werfen erforderlich ist. Lassen Sie uns bitte
Karoly Horvath

6
@drrcknlsn: Das ist ein gültiges Argument, aber in diesem Fall wird zumindest die Ausnahme abgefangen, die Sie voraussichtlich auslösen werden. Das Generikum Exceptionhat nicht viel Dokumentationswert. Persönlich würde ich, wenn ich mich auf diesen Weg begeben würde, PHPDocs @exceptionoder einen ähnlichen Mechanismus wählen , damit er in der generierten Dokumentation angezeigt wird.
Karoly Horvath

Antworten:


903

Haftungsausschluss: Im Folgenden wird beschrieben, wie ich MVC-ähnliche Muster im Kontext von PHP-basierten Webanwendungen verstehe. Alle externen Links, die im Inhalt verwendet werden, dienen dazu, Begriffe und Konzepte zu erläutern und nicht meine eigene Glaubwürdigkeit in diesem Bereich zu implizieren.

Das erste, was ich klären muss, ist: Das Modell ist eine Ebene .

Zweitens: Es gibt einen Unterschied zwischen klassischem MVC und dem, was wir in der Webentwicklung verwenden. Hier ist eine ältere Antwort, die ich geschrieben habe und die kurz beschreibt, wie sie sich unterscheiden.

Was für ein Modell ist NICHT:

Das Modell ist keine Klasse oder ein einzelnes Objekt. Es ist ein sehr häufiger Fehler (ich habe es auch getan, obwohl die ursprüngliche Antwort geschrieben wurde, als ich anfing, etwas anderes zu lernen) , weil die meisten Frameworks dieses Missverständnis aufrechterhalten.

Es handelt sich weder um eine objektrelationale Zuordnungstechnik (ORM) noch um eine Abstraktion von Datenbanktabellen. Jeder, der Ihnen etwas anderes sagt, versucht höchstwahrscheinlich , ein anderes brandneues ORM oder ein ganzes Framework zu "verkaufen" .

Was für ein Modell ist:

Bei einer ordnungsgemäßen MVC-Anpassung enthält das M die gesamte Geschäftslogik der Domäne, und die Modellschicht besteht hauptsächlich aus drei Arten von Strukturen:

  • Domänenobjekte

    Ein Domänenobjekt ist ein logischer Container mit reinen Domäneninformationen. Es stellt normalerweise eine logische Entität im Bereich der Problemdomäne dar. Wird allgemein als Geschäftslogik bezeichnet .

    Hier legen Sie fest, wie Daten vor dem Senden einer Rechnung überprüft oder die Gesamtkosten einer Bestellung berechnet werden sollen. Gleichzeitig wissen Domänenobjekte nichts von der Speicherung - weder von wo (SQL-Datenbank, REST-API, Textdatei usw.) noch selbst wenn sie gespeichert oder abgerufen werden.

  • Datenmapper

    Diese Objekte sind nur für die Speicherung verantwortlich. Wenn Sie Informationen in einer Datenbank speichern, befindet sich hier SQL. Oder Sie verwenden eine XML-Datei zum Speichern von Daten, und Ihre Data Mapper analysieren von und zu XML-Dateien.

  • Dienstleistungen

    Sie können als von ihnen denken , „higher level Domain Objects“, sondern von Geschäftslogik, Dienstleistungen sind für die Interaktion verantwortlich zwischen Domain - Objekte und Mapper . Diese Strukturen schaffen schließlich eine "öffentliche" Schnittstelle für die Interaktion mit der Domänengeschäftslogik. Sie können sie vermeiden, aber mit der Strafe, dass einige Domänenlogiken in Controller übertragen werden .

    In der Frage zur ACL-Implementierung finden Sie eine entsprechende Antwort auf dieses Thema. Dies kann hilfreich sein.

Die Kommunikation zwischen der Modellschicht und anderen Teilen der MVC-Triade sollte nur über Services erfolgen . Die klare Trennung hat einige zusätzliche Vorteile:

  • Es hilft bei der Durchsetzung des Single-Responsibility-Prinzips (SRP).
  • Bietet zusätzlichen Spielraum für den Fall, dass sich die Logik ändert
  • hält die Steuerung so einfach wie möglich
  • gibt einen klaren Entwurf, falls Sie jemals eine externe API benötigen

 

Wie interagiere ich mit einem Modell?

Voraussetzungen: Sehen Sie sich die Vorträge "Global State and Singletons" und "Don't Look For Things!" An. aus den Clean Code Talks.

Zugriff auf Dienstinstanzen erhalten

Es gibt zwei allgemeine Ansätze, damit sowohl die View- als auch die Controller- Instanz (was Sie als "UI-Schicht" bezeichnen könnten) auf diese Dienste zugreifen können:

  1. Sie können die erforderlichen Services direkt in die Konstruktoren Ihrer Ansichten und Controller einfügen, vorzugsweise mithilfe eines DI-Containers.
  2. Verwenden einer Factory für Services als obligatorische Abhängigkeit für alle Ihre Ansichten und Controller.

Wie Sie vielleicht vermuten, ist der DI-Container eine viel elegantere Lösung (für Anfänger jedoch nicht die einfachste). Die beiden Bibliotheken, die ich für diese Funktionalität in Betracht ziehen möchte, sind die eigenständige DependencyInjection-Komponente von Syfmony oder Auryn .

Mit beiden Lösungen, die eine Factory und einen DI-Container verwenden, können Sie auch die Instanzen verschiedener Server gemeinsam nutzen, die von der ausgewählten Steuerung gemeinsam genutzt und für einen bestimmten Anforderungs- / Antwortzyklus angezeigt werden sollen.

Änderung des Modellzustands

Nachdem Sie in den Controllern auf die Modellebene zugreifen können, müssen Sie diese tatsächlich verwenden:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $identity = $this->identification->findIdentityByEmailAddress($email);
    $this->identification->loginWithPassword(
        $identity,
        $request->get('password')
    );
}

Ihre Controller haben eine sehr klare Aufgabe: Nehmen Sie die Benutzereingaben und ändern Sie basierend auf diesen Eingaben den aktuellen Status der Geschäftslogik. In diesem Beispiel sind die Status, zwischen denen geändert wird, "anonymer Benutzer" und "angemeldeter Benutzer".

Der Controller ist nicht für die Überprüfung der Benutzereingaben verantwortlich, da dies Teil der Geschäftsregeln ist und der Controller definitiv keine SQL-Abfragen aufruft, wie Sie sie hier oder hier sehen würden (bitte hassen Sie sie nicht, sie sind fehlgeleitet, nicht böse).

Zeigt dem Benutzer die Statusänderung an.

Ok, Benutzer hat sich angemeldet (oder ist fehlgeschlagen). Was jetzt? Der Benutzer ist sich dessen immer noch nicht bewusst. Sie müssen also tatsächlich eine Antwort erstellen, und das liegt in der Verantwortung einer Ansicht.

public function postLogin()
{
    $path = '/login';
    if ($this->identification->isUserLoggedIn()) {
        $path = '/dashboard';
    }
    return new RedirectResponse($path); 
}

In diesem Fall ergab die Ansicht eine von zwei möglichen Antworten, basierend auf dem aktuellen Status der Modellschicht. Für einen anderen Anwendungsfall hätten Sie die Ansicht, verschiedene Vorlagen zum Rendern auszuwählen, basierend auf etwas wie "aktuell aus Artikel ausgewählt".

Die Präsentationsebene kann tatsächlich ziemlich aufwändig werden, wie hier beschrieben: Grundlegendes zu MVC-Ansichten in PHP .

Aber ich mache gerade eine REST-API!

Natürlich gibt es Situationen, in denen dies ein Overkill ist.

MVC ist nur eine konkrete Lösung für das Prinzip der Trennung von Bedenken . MVC trennt die Benutzeroberfläche von der Geschäftslogik und in der Benutzeroberfläche die Behandlung der Benutzereingaben und der Präsentation. Das ist entscheidend. Während die Leute es oft als "Triade" beschreiben, besteht es nicht aus drei unabhängigen Teilen. Die Struktur ist eher so:

MVC-Trennung

Wenn die Logik Ihrer Präsentationsebene so gut wie nicht vorhanden ist, besteht der pragmatische Ansatz darin, sie als einzelne Ebene beizubehalten. Es kann auch einige Aspekte der Modellschicht wesentlich vereinfachen.

Mit diesem Ansatz kann das Anmeldebeispiel (für eine API) wie folgt geschrieben werden:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $data = [
        'status' => 'ok',
    ];
    try {
        $identity = $this->identification->findIdentityByEmailAddress($email);
        $token = $this->identification->loginWithPassword(
            $identity,
            $request->get('password')
        );
    } catch (FailedIdentification $exception) {
        $data = [
            'status' => 'error',
            'message' => 'Login failed!',
        ]
    }

    return new JsonResponse($data);
}

Dies ist zwar nicht nachhaltig, aber wenn Sie eine komplizierte Logik zum Rendern eines Antwortkörpers haben, ist diese Vereinfachung für trivialere Szenarien sehr nützlich. Aber seien Sie gewarnt , dieser Ansatz wird zu einem Albtraum, wenn Sie versuchen, ihn in großen Codebasen mit komplexer Präsentationslogik zu verwenden.

 

Wie baue ich das Modell?

Da es keine einzige "Modell" -Klasse gibt (wie oben erläutert), erstellen Sie das Modell wirklich nicht. Stattdessen beginnen Sie mit der Erstellung von Diensten , die bestimmte Methoden ausführen können. Und dann implementieren Domain - Objekte und Mapper .

Ein Beispiel für eine Servicemethode:

In beiden oben genannten Ansätzen gab es diese Anmeldemethode für den Identifikationsdienst. Wie würde es eigentlich aussehen? Ich verwende eine leicht modifizierte Version derselben Funktionalität aus einer Bibliothek , die ich geschrieben habe. Weil ich faul bin:

public function loginWithPassword(Identity $identity, string $password): string
{
    if ($identity->matchPassword($password) === false) {
        $this->logWrongPasswordNotice($identity, [
            'email' => $identity->getEmailAddress(),
            'key' => $password, // this is the wrong password
        ]);

        throw new PasswordMismatch;
    }

    $identity->setPassword($password);
    $this->updateIdentityOnUse($identity);
    $cookie = $this->createCookieIdentity($identity);

    $this->logger->info('login successful', [
        'input' => [
            'email' => $identity->getEmailAddress(),
        ],
        'user' => [
            'account' => $identity->getAccountId(),
            'identity' => $identity->getId(),
        ],
    ]);

    return $cookie->getToken();
}

Wie Sie sehen können, gibt es auf dieser Abstraktionsebene keinen Hinweis darauf, woher die Daten abgerufen wurden. Es kann sich um eine Datenbank handeln, es kann sich jedoch auch nur um ein Scheinobjekt zu Testzwecken handeln. Sogar die Datenmapper, die tatsächlich dafür verwendet werden, sind in den privateMethoden dieses Dienstes versteckt .

private function changeIdentityStatus(Entity\Identity $identity, int $status)
{
    $identity->setStatus($status);
    $identity->setLastUsed(time());
    $mapper = $this->mapperFactory->create(Mapper\Identity::class);
    $mapper->store($identity);
}

Möglichkeiten zum Erstellen von Mappern

Um eine Abstraktion der Persistenz zu implementieren, müssen bei den flexibelsten Ansätzen benutzerdefinierte Datenzuordnungen erstellt werden .

Mapper-Diagramm

Aus: PoEAA- Buch

In der Praxis werden sie für die Interaktion mit bestimmten Klassen oder Oberklassen implementiert. Nehmen wir an, Sie haben Customerund Adminin Ihrem Code (beide erben von einer UserOberklasse). Beide würden wahrscheinlich einen separaten passenden Mapper haben, da sie unterschiedliche Felder enthalten. Sie werden aber auch gemeinsame und häufig verwendete Vorgänge haben. Zum Beispiel: Aktualisieren der "zuletzt online gesehenen" Zeit. Und anstatt die vorhandenen Mapper komplizierter zu machen, besteht der pragmatischere Ansatz darin, einen allgemeinen "User Mapper" zu haben, der nur diesen Zeitstempel aktualisiert.

Einige zusätzliche Kommentare:

  1. Datenbanktabellen und Modell

    Während manchmal eine direkte 1: 1: 1-Beziehung zwischen einer Datenbanktabelle, einem Domänenobjekt und Mapper besteht , ist diese in größeren Projekten möglicherweise weniger häufig als erwartet:

    • Informationen, die von einem einzelnen Domänenobjekt verwendet werden, können aus verschiedenen Tabellen zugeordnet werden, während das Objekt selbst keine Persistenz in der Datenbank aufweist.

      Beispiel: Wenn Sie einen monatlichen Bericht erstellen. Dies würde Informationen aus verschiedenen Tabellen sammeln, aber es gibt keine magische MonthlyReportTabelle in der Datenbank.

    • Ein einzelner Mapper kann mehrere Tabellen betreffen.

      Beispiel: Wenn Sie Daten aus dem UserObjekt speichern , kann dieses Domänenobjekt eine Sammlung anderer Domänenobjekte - GroupInstanzen - enthalten . Wenn Sie sie ändern und speichern User, muss der Data Mapper Einträge in mehreren Tabellen aktualisieren und / oder einfügen.

    • Daten von einem einzelnen Domänenobjekt werden in mehr als einer Tabelle gespeichert.

      Beispiel: In großen Systemen (denken Sie an ein mittelgroßes soziales Netzwerk) kann es pragmatisch sein, Benutzerauthentifizierungsdaten und Daten, auf die häufig zugegriffen wird, getrennt von größeren Inhaltsblöcken zu speichern, was selten erforderlich ist. In diesem Fall haben Sie möglicherweise noch eine einzelne UserKlasse, aber die darin enthaltenen Informationen hängen davon ab, ob alle Details abgerufen wurden.

    • Für jedes Domänenobjekt kann es mehr als einen Mapper geben

      Beispiel: Sie haben eine Nachrichtenseite mit einem gemeinsam genutzten Code, der sowohl für die Öffentlichkeit als auch für die Verwaltungssoftware verwendet wird. Obwohl beide Schnittstellen dieselbe ArticleKlasse verwenden, benötigt das Management viel mehr Informationen. In diesem Fall hätten Sie zwei separate Mapper: "intern" und "extern". Jeder führt unterschiedliche Abfragen durch oder verwendet sogar unterschiedliche Datenbanken (wie bei Master oder Slave).

  2. Eine Ansicht ist keine Vorlage

    Ansichtsinstanzen in MVC (wenn Sie nicht die MVP-Variante des Musters verwenden) sind für die Präsentationslogik verantwortlich. Dies bedeutet, dass jede Ansicht normalerweise mindestens einige Vorlagen jongliert. Es erfasst Daten aus der Modellebene und wählt dann basierend auf den empfangenen Informationen eine Vorlage aus und legt Werte fest.

    Einer der Vorteile, die Sie daraus ziehen, ist die Wiederverwendbarkeit. Wenn Sie eine ListViewKlasse erstellen , können Sie mit gut geschriebenem Code dieselbe Klasse die Präsentation der Benutzerliste und der Kommentare unter einem Artikel übergeben lassen. Weil beide dieselbe Präsentationslogik haben. Sie wechseln einfach die Vorlagen.

    Sie können entweder native PHP-Vorlagen oder eine Template-Engine eines Drittanbieters verwenden. Möglicherweise gibt es auch Bibliotheken von Drittanbietern, die View- Instanzen vollständig ersetzen können .

  3. Was ist mit der alten Version der Antwort?

    Die einzige wesentliche Änderung besteht darin, dass das, was in der alten Version als Modell bezeichnet wird, tatsächlich ein Dienst ist . Der Rest der "Bibliotheksanalogie" hält ziemlich gut mit.

    Der einzige Fehler, den ich sehe, ist, dass dies eine wirklich seltsame Bibliothek wäre, da sie Ihnen Informationen aus dem Buch zurückgeben würde, Sie das Buch selbst jedoch nicht berühren könnten, da sonst die Abstraktion "auslaufen" würde. Ich muss mir vielleicht eine passendere Analogie überlegen.

  4. Welche Beziehung besteht zwischen View- und Controller- Instanzen?

    Die MVC-Struktur besteht aus zwei Schichten: UI und Modell. Die Hauptstrukturen in der UI-Ebene sind Ansichten und Controller.

    Wenn Sie mit Websites arbeiten, die MVC-Entwurfsmuster verwenden, ist es am besten, eine 1: 1-Beziehung zwischen Ansichten und Controllern herzustellen. Jede Ansicht stellt eine ganze Seite Ihrer Website dar und verfügt über einen dedizierten Controller, der alle eingehenden Anforderungen für diese bestimmte Ansicht verarbeitet.

    Um beispielsweise einen geöffneten Artikel darzustellen, hätten Sie \Application\Controller\Documentund \Application\View\Document. Dies würde alle Hauptfunktionen für die UI-Ebene enthalten, wenn es um den Umgang mit Artikeln geht (natürlich haben Sie möglicherweise einige XHR- Komponenten, die nicht direkt mit Artikeln zusammenhängen) .


4
@Rinzler, Sie werden feststellen, dass nirgends in diesem Link etwas über Model gesagt wird (außer in einem Kommentar). Es ist nur "eine objektorientierte Schnittstelle zu Datenbanktabellen" . Wenn Sie versuchen, dies in eine modellähnliche Form zu bringen, verletzen Sie am Ende SRP und LSP .
Tereško

8
@hafichuk Nur Situationen, in denen es sinnvoll ist, ein ActiveRecord- Muster zu verwenden, dienen dem Prototyping. Wenn Sie anfangen, den Code zu schreiben, der für die Produktion bestimmt ist, wird er zu einem Anti-Pattern, da er Speicher- und Geschäftslogik mischt. Und da Model Layer die anderen MVC-Teile überhaupt nicht kennt. Dies ändert sich nicht in Abhängigkeit von der Variation des Originalmusters . Auch bei Verwendung von MVVM. Es gibt keine "Mehrfachmodelle" und sie sind nichts zugeordnet. Modell ist eine Schicht.
Tereško

3
Kurzversion - Modelle sind Datenstrukturen .
Eddie B

9
Nun, da er MVC erfunden hat, kann der Artikel einen gewissen Wert haben.
Eddie B

3
... oder auch nur eine Reihe von Funktionen. MVC muss nicht in einem OOP-Stil implementiert werden, obwohl es meistens auf diese Weise implementiert wird. Das Wichtigste ist, Schichten zu trennen und den richtigen Daten- und Kontrollfluss
herzustellen

37

Alles, was Geschäftslogik ist, gehört in ein Modell, sei es eine Datenbankabfrage, Berechnungen, ein REST-Aufruf usw.

Sie können den Datenzugriff im Modell selbst haben. Das MVC-Muster hindert Sie nicht daran. Sie können es mit Services, Mappern und was nicht beschönigen, aber die eigentliche Definition eines Modells ist eine Ebene, die die Geschäftslogik handhabt, nicht mehr und nicht weniger. Es kann eine Klasse, eine Funktion oder ein komplettes Modul mit einer Unmenge von Objekten sein, wenn Sie dies wünschen.

Es ist immer einfacher, ein separates Objekt zu haben, das die Datenbankabfragen tatsächlich ausführt, anstatt sie direkt im Modell ausführen zu lassen. Dies ist besonders nützlich beim Komponententest (da es einfach ist, eine Scheindatenbankabhängigkeit in Ihr Modell einzufügen):

class Database {
   protected $_conn;

   public function __construct($connection) {
       $this->_conn = $connection;
   }

   public function ExecuteObject($sql, $data) {
       // stuff
   }
}

abstract class Model {
   protected $_db;

   public function __construct(Database $db) {
       $this->_db = $db;
   }
}

class User extends Model {
   public function CheckUsername($username) {
       // ...
       $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE ...";
       return $this->_db->ExecuteObject($sql, $data);
   }
}

$db = new Database($conn);
$model = new User($db);
$model->CheckUsername('foo');

Außerdem müssen Sie in PHP selten Ausnahmen abfangen / erneut auslösen, da die Rückverfolgung erhalten bleibt, insbesondere in einem Fall wie Ihrem Beispiel. Lassen Sie die Ausnahme einfach auslösen und fangen Sie sie stattdessen im Controller ab.


Meine Struktur ist sehr ähnlich, ich denke, ich trenne sie nur ein bisschen mehr heraus. Der Grund, warum ich die Verbindung weitergab, war, dass ich Chunks in Transaktionen ausführen musste. Ich wollte einen Benutzer hinzufügen und dann den Benutzer zu einer Rolle hinzufügen, aber die Rolle zurück, wenn eine fehlschlug. Die einzige Möglichkeit, das zu klären, bestand darin, die Verbindung zu übergeben.
Dietpixel

10
-1: Es ist auch völlig falsch. Modell ist keine Abstraktion für eine Tabelle.
Tereško

1
Die UserKlasse erweitert das Modell grundsätzlich, ist aber kein Objekt. Der Benutzer sollte ein Objekt sein und Eigenschaften wie ID, Name ... haben, die Sie bereitstellenUser Klasse ist ein Helfer.
TomSawyer

1
Ich denke, Sie verstehen MVC, verstehen aber nicht, was OOP ist. In diesem Szenario steht, wie gesagt, Userfür ein Objekt, und es sollte Eigenschaften eines Benutzers haben, nicht Methoden wie CheckUsername: Was sollten Sie tun, wenn Sie ein neues UserObjekt erstellen möchten ? new User($db)
TomSawyer

@ TomSawyer OOP bedeutet nicht, dass Objekte Eigenschaften haben müssen. Was Sie beschreiben, ist ein Entwurfsmuster, das für die Frage oder eine Antwort auf diese Frage irrelevant ist. OOP ist ein Sprachmodell, kein Entwurfsmuster.
Netcoder

20

In Web- "MVC" können Sie tun, was Sie wollen.

Das ursprüngliche Konzept (1) beschrieb das Modell als Geschäftslogik. Es sollte den Anwendungsstatus darstellen und eine gewisse Datenkonsistenz erzwingen. Dieser Ansatz wird oft als "Fettmodell" bezeichnet.

Die meisten PHP-Frameworks verfolgen einen flacheren Ansatz, bei dem das Modell nur eine Datenbankschnittstelle ist. Zumindest sollten diese Modelle jedoch die eingehenden Daten und Beziehungen noch validieren.

In beiden Fällen sind Sie nicht weit entfernt, wenn Sie die SQL-Inhalte oder Datenbankaufrufe in eine andere Ebene unterteilen. Auf diese Weise müssen Sie sich nur mit den tatsächlichen Daten / Verhaltensweisen befassen, nicht mit der eigentlichen Speicher-API. (Es ist jedoch unvernünftig, es zu übertreiben. Sie können beispielsweise ein Datenbank-Backend niemals durch ein Dateispeicher ersetzen, wenn dies nicht im Voraus geplant wurde.)


8
Link ist ungültig (404)
Kyslik


6

Die meisten Anwendungen mehr oftenly haben Daten, die Anzeige und Verarbeitung von Teil und wir alle nur diejenigen , die in den Buchstaben setzen M, Vund C.

Model ( M) -> Hat die Attribute, die den Anwendungsstatus enthalten, und es weiß nichts über Vund C.

View ( V) -> Hat ein Anzeigeformat für die Anwendung und kennt nur das Digest-Modell und kümmert sich nicht darum C.

Controller ( C) ----> hat einen Teil der Verarbeitung Anwendung und wirkt als Verdrahtung zwischen M und V , und es hängt sowohl von M, im VGegensatz zu MundV .

Insgesamt gibt es eine Trennung der Bedenken zwischen jedem. In Zukunft können Änderungen oder Erweiterungen sehr einfach hinzugefügt werden.


0

In meinem Fall habe ich eine Datenbankklasse, die alle direkten Datenbankinteraktionen wie Abfragen, Abrufen usw. abwickelt. Wenn ich also meine Datenbank von MySQL auf PostgreSQL ändern müsste, gäbe es kein Problem. Das Hinzufügen dieser zusätzlichen Ebene kann daher nützlich sein.

Jede Tabelle kann eine eigene Klasse und spezifische Methoden haben. Um die Daten tatsächlich abzurufen, kann die Datenbankklasse damit umgehen:

Datei Database.php

class Database {
    private static $connection;
    private static $current_query;
    ...

    public static function query($sql) {
        if (!self::$connection){
            self::open_connection();
        }
        self::$current_query = $sql;
        $result = mysql_query($sql,self::$connection);

        if (!$result){
            self::close_connection();
            // throw custom error
            // The query failed for some reason. here is query :: self::$current_query
            $error = new Error(2,"There is an Error in the query.\n<b>Query:</b>\n{$sql}\n");
            $error->handleError();
        }
        return $result;
    }
 ....

    public static function find_by_sql($sql){
        if (!is_string($sql))
            return false;

        $result_set = self::query($sql);
        $obj_arr = array();
        while ($row = self::fetch_array($result_set))
        {
            $obj_arr[] = self::instantiate($row);
        }
        return $obj_arr;
    }
}

Tabellenobjekt classL

class DomainPeer extends Database {

    public static function getDomainInfoList() {
        $sql = 'SELECT ';
        $sql .='d.`id`,';
        $sql .='d.`name`,';
        $sql .='d.`shortName`,';
        $sql .='d.`created_at`,';
        $sql .='d.`updated_at`,';
        $sql .='count(q.id) as queries ';
        $sql .='FROM `domains` d ';
        $sql .='LEFT JOIN queries q on q.domainId = d.id ';
        $sql .='GROUP BY d.id';
        return self::find_by_sql($sql);
    }

    ....
}

Ich hoffe, dieses Beispiel hilft Ihnen, eine gute Struktur zu schaffen.


12
"Wenn ich also meine Datenbank von MySQL auf PostgreSQL ändern müsste, gäbe es kein Problem." Uhhhmmm mit dem obigen Code hätten Sie ein großes Problem, irgendetwas imo zu ändern.
PeeHaa

Ich sehe, dass meine Antwort nach der Bearbeitung und mit der Zeit immer weniger Sinn macht. Aber es sollte hier bleiben
Ibu

2
Databaseim Beispiel ist keine Klasse. Es ist nur ein Wrapper für Funktionen. Wie kann man auch eine "Tabellenobjektklasse" ohne Objekt haben?
Tereško

2
@ tereško Ich habe viele Ihrer Beiträge gelesen und sie sind großartig. Aber ich kann nirgendwo einen vollständigen Rahmen zum Lernen finden. Kennen Sie einen, der "es richtig macht"? Oder zumindest einer, der es mag, dass Sie und einige andere hier auf SO sagen, zu tun? Vielen Dank.
Johnny

Ich mag viel zu spät kommen, aber ich möchte darauf hinweisen, dass PDO das Problem der Erstellung einer DB-Schicht fast löst, um zukünftige Änderungen zu erleichtern.
Matthew Goulart
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.