Ankündigung

Einklappen
Keine Ankündigung bisher.

Trennung von Daten und Controlls

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Trennung von Daten und Controlls

    Hallo ich brüte gerade an der Roadmap und Umsetzung für mein erstes kleines Projekt in PHP. Dabei bin ich auf eine "Design"-Frage gestoßen. (Software-Design wohlgemerkt).


    Ich habe diverse Klassen in meinem Projekt die miteinander interagieren sollen. Zum Beispiel habe ich zwei Klassen User und Event. Ein User kann an einem Event teilnehmen, die Teilnahme absagen, etc. Ein Event kann erstellt, geändert oder abgesagt werden.
    Gespeichert werden die Interaktionen in einer Datenbank. Schematisch grob könnte die DB so aussehen:


    td_User: userID, userName, userMail
    td_Event: eventID, eventCity, eventDate
    td_BookedEvent: event_ID, user_ID


    *Das ist nur grob schematisch und soll nur dazu dienen mein Problem zu schildern. Ich weiß dass das kein guter Entwurf der DB ist.

    Meine Klasse User könnte wie folgt aussehen:

    PHP-Code:
    class User
     
    {

     private 
    $Mail "Mailadresse";
     private 
    $Username "Username"


     public function 
    GetUserName(){

      echo 
    $this->Username;

     }


     public function 
    GetMail(){

      echo 
    $this.Mail;

     }

     public function 
    setUsername($name)
        {
           
    $this->Username $name;


        }

     public function 
    __construct($name$mail)
        {

            
    $this->Mail $mail;

            
    $this->Username $name;



        }

     } 


    Soweit so klar. Jetzt meine Frage. Ich habe mir zusätzlich ein include-File mit einer PDO-Verbindung geschrieben. Was ist aus Sicht des Software-Designs die elegantere und auch bessere Art die Manipulationen auf der DB zu implementieren?

    So wie ich das sehe habe ich zwei Möglichkeiten.

    1: Ich erstelle eine PHP-Datei die von einem HTML-Form aufgerufen wird, ich übermittle die Daten für einen neuen User an mein Script, ich rufe mit den Daten den Konstruktor auf, lasse mit den Get-Funktionen die Werte in Variablen speichern und übergebe diese Werte dann einer weiteren Funktion die mittels PDO die Werte auf die DB schreibt.

    ODER

    2: Ich implementiere den Datenbankzugriff bereits im Konstruktor. Sprich ich müsste wohl das PDO.php via Require einbinden.

    Ich persönlich würde die zweite Variante bevorzugen, da ich somit zu jeder Methode die aufgerufen werden kann, die aufgerufene Transaktion sowie die übergebenen Parameter besser prüfen und steuern kann (zumindest bilde ich mir das ein). Bin mir aber nicht sicher ob das ein Weg ist, den mir erfahrene Programmierer empfehlen könnten.


    Oder mache ich mich nur selbst mit der Frage verrückt?

    Sorry, wenn die Frage banal anmuten mag, aber ich will nicht viel Zeit in die Software investieren und später sagt mir dann jeder, was für nen Bullshit ich da zusammen gestümpert hätte.

    Danke auf jeden Fall für die Antworten vorab.






  • #2
    Es gibt gewisse Ansätze in der Webentwicklung die sich vielfach bewährt haben. Ein Ansatz/Pattern darunter ist das MVC-Pattern.
    Das würde ich mir anschauen, wenn du das ganze Ding wirklich richtig machen willst.

    Alternativ, statt dein eigenes "Framework" zu basteln und dir um so Sachen wie oben ein Kopf machen zu müssen kannst du auch zu einem Framework greifen.
    Da ist der Kern schon implementiert und du kannst direkt anfangen deine Anwendung zu erstellen, bzw. die Aktionen dafür.

    Um auf deine beiden Möglichkeiten zu kommen:
    Wo käme denn die Datenbankverbindung bei der in 1. genannten Funktion her?
    Die Klasse wie sie oben steht ist nur eine Abbildung der Tabelle User auf ein Objekt (Model). Über Models werden Daten aus einer Tabelle im Programmcode repräsentiert. In diesem Sinne kannst du dir auch ORM anschauen.
    Wenn du ein Repository für den Benutzer erstellst und daraus die Daten holst und dieses Repository die Datenbankabfragen macht (und logischerweise dann auch eine Datenbankverbindung zur Verfügung hat), dann ist das am ehesten ein wählbarer Ansatz.

    Der zweite Ansatz ist der falsche Weg. Du würdest da in jedem Objekt eine Datenbankinstanz erstellen (ließe sich mit Singleton umgehen, will man aber nicht). Außerdem kann und soll die Klasse User nicht wissen wie sie das Datenbankobjekt konfigurieren soll.
    Bei diesem Ansatz wäre "ein bisschen richtiger" Dependency Injection zu nutzen und die Datenbankverbindung in die Klasse mit zu übergeben.



    Außerdem inkludierst du heute eigentlich keine Dateien mehr manuell, mit Ausnahme des Autoloaders. Der Autoloader lädt alle Dateien automatisch bei der Instanzierung.
    "Software is like Sex, it's best if it's free." - Linus Torvalds

    Kommentar


    • #3
      Hallo,

      also ja, es gibt das Model-View-Control Konzept, dass Frameworks implementieren oder zumindest sowetwas in der Art. Es gibt ja Meinungen, dass man im Web Bereich das MVC Pattern nicht ganz so umsetzen kann, wie es ursprünglich in Lisp definiert wurde. Deswegen wird sich nicht jedes Framework damit brüsten, dass Sie das MVC Pattern benutzen.

      Zu deiner Frage mit der Datenbank: Am besten per Dependency Injection. Das musst du aber nicht mehr selber machen, das gibt es schon tausendfach im Internet. Ich empfiehel dir ganz klar den Doctrine Database Abstraction Layer (DBAL). Der ist intitiutiv zu bedienen, fast so wie SQL selbst und baut auf PDO auf. Siehe hier: http://www.doctrine-project.org/projects/dbal.html

      Und bitte:
      1. Wir leben in modernen Zeiten, du solltest bzw. musst kein include/require mehr verwenden. Sieh dir Composer an: http://www.getcomposer.org
      2. Dein Code ist absolut grauenvoll formatiert. Ich lege dir nahe die PSR Coding Standards (PSR 1 und 2) zu befolgen (was so ziemlich jeder heute macht, auch ich musste mich dem schon beugen). Siehe hier: http://www.php-fig.org/psr/psr-2/
      automatisch fixen kannst du das mit dem php-cs-fixer: http://cs.sensiolabs.org

      Ich rate dir auch dazu mit dem Symfony Framework zu entwickeln, was auch fast jeder benutzt, bzw. was beliebt in Firmen ist: http://www.symfony.com
      Falls dir das zu groß ist für den Anfang, dann fange erstmal mit Silex an. Das ist eine abgespeckte Symfony Version, siehe hier: http://silex.sensiolabs.org
      Die Dokumentation von Silex muss allerding überarbeitet werden, entspricht nicht mehr ganz so den Stand des Frameworks. Was mir bisher aufgefallen ist: für den URL Generator in Twig musst du nichts mehr einbinden, der ist standardgemäß drin. Also einfach den Twig Code aus der Dokumentation zu kopieren reicht völlig.

      Du solltest dir auch mal die Wissenssammlung vom Forum selbst durchlesen: https://php-de.github.io


      MFG derwunner

      Kommentar


      • #4
        Die Zuordnung zwischen Request und Controller wickelt ein Router ab (nikic/fast-route). Wenn eine deiner Klassen ein Datenbank-Objekt benötigt, dann "injizierst" du es am besten über den Constructor. Das Verwalten von Abhängigkeiten (Dependencies) wie bspw. dem angeführten Datenbank-Objekt übernimmt ein DI-Container (PHP-DI) -- im Idealfall kann der Container die Abhängigkeiten automatisiert auflösen und injizieren (Autowiring). Wenn du den Datenbank-Zugriff abstrahieren willst, nutzt du entweder einen Query-Builder oder einen ORM (illuminate/database).

        Kommentar


        • #5
          Zitat von lottikarotti Beitrag anzeigen
          Wenn du den Datenbank-Zugriff abstrahieren willst, nutzt du entweder einen Query-Builder oder einen ORM (illuminate/database).
          Wobei ich eher sagen würde, dass ORM nichts abstrahiert und im schlimmsten fall dein Controller von dem ORM abhängig macht.

          Besser wäre es ein Repository Interface als Dependency im Controller. Das ORM kann ja dann im Repository genutzt werden nur sollte es nicht im Controller sichtbar sein dass das Framework xyz genutzt wird
          apt-get install npm -> npm install -g bower -> bower install <package> YOLO

          Kommentar


          • #6
            Zitat von BlackScorp Beitrag anzeigen
            Wobei ich eher sagen würde, dass ORM nichts abstrahiert und im schlimmsten fall dein Controller von dem ORM abhängig macht.

            Besser wäre es ein Repository Interface als Dependency im Controller. Das ORM kann ja dann im Repository genutzt werden nur sollte es nicht im Controller sichtbar sein dass das Framework xyz genutzt wird
            Hast du hier zufällig ein frei zugängliches Beispielprojekt parat? Ich bin durch frameworkunabhängige Controller noch nicht ganz durchgestiegen und verwende gerade stetig den "schlimmsten Fall".

            Kommentar


            • #7
              Zitat von ChromOxid Beitrag anzeigen

              Hast du hier zufällig ein frei zugängliches Beispielprojekt parat? Ich bin durch frameworkunabhängige Controller noch nicht ganz durchgestiegen und verwende gerade stetig den "schlimmsten Fall".
              https://github.com/BlackScorp/guestb...seCase.php#L34 hier habe ich halt "UseCases" die nutzen intern das Repository (Man kann auch direkt im Controller das Repository anfragen). Wie du siehst ich frage das Repository nur ab "Finde mal mir XYZ"

              für inserts nutze ich factories die mir dann ein Objekt erstellen und diers wird dem Repository übergeben.

              Ich habe noch ein altes Projekt, da wirds nicht ganz richtig gemacht. Aber man kann es sich anschauen.

              https://github.com/Opentribes/Core/b...Repository.php das ist zb UserRepository, dieses nutzt Doctrine DBAL als Abhängigkeit. (Eigentlich gehört da noch eine Factory statt rowToEntity und entityToRow, die hatte ich damals nicht genutzt)

              Im UseCase siehst du wiederum nichts von DBAL usw https://github.com/Opentribes/Core/b...seCase.php#L35 du siehst nur das Interface
              apt-get install npm -> npm install -g bower -> bower install <package> YOLO

              Kommentar


              • #8
                Zitat von BlackScorp Beitrag anzeigen
                Wobei ich eher sagen würde, dass ORM nichts abstrahiert
                Für mich ist ein ORM eine Abstraktionsebene und stellt quasi den Adapter zwischen einer beliebigen Datenquelle und einer Objektstruktur dar. Damit verlasse ich den Kontext meiner Datenquelle und bewege mich, innerhalb meiner Anwendung, primär in einer virtuellen Objektstruktur.

                Zitat von BlackScorp Beitrag anzeigen
                und im schlimmsten fall dein Controller von dem ORM abhängig macht.
                Ob das nun pauschal mehr Nachteile mit sich bringt, als die Abhängigkeit an ein x-beliebiges Repository(Interface) - ich weiß nicht. Um das wirklich zu vergleichen müssten schon konkrete Beispiele her.

                Zitat von BlackScorp Beitrag anzeigen
                Besser wäre es ein Repository Interface als Dependency im Controller. Das ORM kann ja dann im Repository genutzt werden nur sollte es nicht im Controller sichtbar sein dass das Framework xyz genutzt wird
                Also ist die Aussage vielmehr: schiebe Interfaces zwischen "underlying dependencies" und deine Controller. Somit bleibst du unabhängig von den konkreten Implementierungen, aber abhängig von deinen Interfaces

                Kommentar


                • #9
                  Zitat von lottikarotti Beitrag anzeigen
                  Für mich ist ein ORM eine Abstraktionsebene und stellt quasi den Adapter zwischen einer beliebigen Datenquelle und einer Objektstruktur dar. Damit verlasse ich den Kontext meiner Datenquelle und bewege mich, innerhalb meiner Anwendung, primär in einer virtuellen Objektstruktur.
                  PHP-Code:
                  $user Users::findAll()->where('name' ,'LIKE',$foo )->orderBy('created','ASC'); 
                  sieht nicht aus wie "Abstrakt" und die Datenquelle ist nicht belibig. Vielleicht kennst du ja gute ORM Beispiele?

                  Zitat von lottikarotti Beitrag anzeigen
                  Ob das nun pauschal mehr Nachteile mit sich bringt, als die Abhängigkeit an ein x-beliebiges Repository(Interface) - ich weiß nicht. Um das wirklich zu vergleichen müssten schon konkrete Beispiele her.
                  Vorteile die ich da sehe:
                  1) Einfache Möglichkeit Tests zu schreiben. Du kannst ein Fake Repository erstellen und dieses injezieren, kannst einen Zustand einer Applikation simulieren ohne wirklich die Datenbank einzusetzen.
                  2) Refactoring. Gerade wenn man ein Feature Entwickelt und man sich nicht sicher ist ob eine Tabelle nun has_many oder has_one beziehung hat und man Dinge nachträglich verändert. Man hat dann mehrere Stellen.

                  PHP-Code:
                  echo $user->adress->street
                  und plötzlich gibt es ein Feature bei dem gesagt wird, ein User kann mehrere adressen haben.


                  Zitat von lottikarotti Beitrag anzeigen
                  Also ist die Aussage vielmehr: schiebe Interfaces zwischen "underlying dependencies" und deine Controller. Somit bleibst du unabhängig von den konkreten Implementierungen, aber abhängig von deinen Interfaces
                  abhängigkeiten zu Interfaces sind aber nicht schlimm. du kannst jedes Interface mal eben implementieren, auch mit leeren Methoden. Sollte es sowieso nicht dein Ziel sein dass deine Applikationslogik in php Klassen verpackt sind die du in jedem Framework nutzen kannst?


                  Bitte benk, dass ich nur so drüber nachdenke weil ich öfters noch an Symofny 1 Projekt arbeiten muss, im jahr 2017, weil es nicht so einfach mit nur eine Handvoll entwicklern, die Komplette Logik umschreiben können. Ständig wird refacotring aufgeschoben bzw ignoriert weil wir nicht wissen was alles schief gehen kann wenn man am quellcode was verändert. Auch bringt Refactoring kein Geld direkt ein(indirekt schon, wenn die Arbeit schneller umgesetzt werden kann).

                  ORM scheint am Anfang so einfach zu sein, du erstellt deine Klassen, kannst deine Felder in der DB Definieren und magisch kannst du Daten abespeichern uns auslesen etc.. und ehe du dich versiehst, hast du all deine Logik ins Model gepackt, deine Controller erstellen nur noch ein Model und übertragen Daten in das View.

                  Am Ende sitzt du da nur noch da und bekämpfst irgendwelche Probleme eines uralten Frameworks anstatt an dem eigentlichen Feature zu Arbeiten.


                  EDIT: aber repositories sind auch nicht "DIE" Lösung. Ich habe zum Beispiel ein Repository das hat schon 50 findBy<youNameIt> methoden. Weil ich nicht in der Lage bin, vernüftig ein Criteria Pattern anzuwenden. Also das das Criterion mir SQL Schnipsel liefert, die ich dann im Repository ausführe. Da es aber aktuell nur für den Spezialfall gebaut wurde, kann man damit leben. Man kann ja auch nicht alles schön im Quellcode haben.
                  apt-get install npm -> npm install -g bower -> bower install <package> YOLO

                  Kommentar


                  • #10
                    Zitat von BlackScorp Beitrag anzeigen
                    PHP-Code:
                    $user Users::findAll()->where('name' ,'LIKE',$foo )->orderBy('created','ASC'); 
                    Ein ORM ist nicht automatisch eine miese Active Record Implementierung mit statischen Methoden. Schau dir Doctrine an: dort kommt Data Mapper zum Einsatz.

                    Zitat von BlackScorp Beitrag anzeigen
                    Vorteile die ich da sehe:
                    1) Einfache Möglichkeit Tests zu schreiben. Du kannst ein Fake Repository erstellen und dieses injezieren, kannst einen Zustand einer Applikation simulieren ohne wirklich die Datenbank einzusetzen.
                    Letztlich kann ich doch zwischen alles Interfaces schieben. Dadurch wird alles austauschbar uns lässt sich problemlos faken.

                    Zitat von BlackScorp Beitrag anzeigen
                    2) Refactoring. Gerade wenn man ein Feature Entwickelt und man sich nicht sicher ist ob eine Tabelle nun has_many oder has_one beziehung hat und man Dinge nachträglich verändert. Man hat dann mehrere Stellen.
                    Wenn sich Objekt-Beziehungen verändern, wirst du immer ein Refactoring fahren müssen. Spätestens dort wo davon ausgegangen wird, dass ur eine Adresse geliefert wird. Und rein aus semantischen Gründen müsste ich sogar eine neue Methode für mein Repository schreiben. Statt findAdressByUser wäre das dann vielleicht findAdressesByUser.

                    Zitat von BlackScorp Beitrag anzeigen
                    abhängigkeiten zu Interfaces sind aber nicht schlimm. du kannst jedes Interface mal eben implementieren, auch mit leeren Methoden. Sollte es sowieso nicht dein Ziel sein dass deine Applikationslogik in php Klassen verpackt sind die du in jedem Framework nutzen kannst?
                    Das habe ich ja auch nicht behauptet. Ich habe nur deine Aussage korrigiert, denn diesen Vorteil genießt nicht nur das Repository-Pattern.

                    Zitat von BlackScorp Beitrag anzeigen
                    EDIT: aber repositories sind auch nicht "DIE" Lösung. Ich habe zum Beispiel ein Repository das hat schon 50 findBy<youNameIt> methoden. Weil ich nicht in der Lage bin, vernüftig ein Criteria Pattern anzuwenden. Also das das Criterion mir SQL Schnipsel liefert, die ich dann im Repository ausführe. Da es aber aktuell nur für den Spezialfall gebaut wurde, kann man damit leben. Man kann ja auch nicht alles schön im Quellcode haben.
                    Deshalb arbeite ich viel lieber mit einem Query-Builder.

                    Kommentar


                    • #11
                      Zitat von lottikarotti Beitrag anzeigen
                      Deshalb arbeite ich viel lieber mit einem Query-Builder.
                      Ich auch, nur nicht mit ORM
                      apt-get install npm -> npm install -g bower -> bower install <package> YOLO

                      Kommentar

                      Lädt...
                      X