Doctrine ORM
Beispielschema für die webbasierte Produktion eines Magazins

Martin Herr, 26.06.2009 - 07:58 | 3 Kommentare |  |  Teilen

doctrine_websiteEigentlich arbeite ich gerade an einigen anderen Baustellen, zum Beispiel am Meta-Projekt, dass gute Fortschritte macht und an der Infrastruktur drüben beim Social-Media-Vermarkter adnation. Irgendwie lässt mich aber seit Tagen die Idee nicht mehr los, mich mal kurz hinzusetzen und ein Doctrine-Schema analog zu unserem Produktionsablauf bei der Produktion des t3n Magazins in einer ersten Version zusammen zu stellen.

Heute morgen habe ich mir dann endlich mal 30min Zeit genommen eine erste Version zu bauen. Ist noch nicht getestet und wahrscheinlich fallen mir auf dem Weg ins Büro noch 10 weitere Verbesserungen ein, aber für eine erste Version sollte das schon ganz gut funktionieren.

Mir war es wichtig, dass die vorhandenen Eigenschaften von Doctrine verwendet werden (z.B. versionable und timstampable) und von Anfang an eine korrektes Verhalten des Models beim Löschen einzelnen Elemente definiert wurde (onDelete).

UPDATE: Was ist Doctrine und warum ist das cool? Doctrine ist ein Object-Relationship-Manager in PHP, der es ermöglicht Objekte und deren Beziehungen zueinander einmalig in einem Schema (meist im yml-Format) zu definieren und daraus dann die Models zu generieren. Das tolle an dieser Vorgehensweise ist, dass man sich nicht mehr mit SQL rumschlagen muss und zum Beispiel beim Löschen eines Objekts, verknüpfte Unterobjekt je nach Definition mit gelöscht werden (falls man das möchte). Doctrine bietet aber noch viele weitere Funktionen.. einfach mal anschauen.

Jetzt aber genug der langen Worte. Hier die erste Version eines Doctrine-Models zum Aufbau eines webbasierten Workflows zur Produktion eines Magazins (hier in Verbindung mit Symfony 1.2):

# User-Model 1:1 Relation zu sf_guard
yeeCoreUser:
  columns:
    sf_guard_user_id: integer(4)
    firstname: string(255)
    lastname: string(255)
    title: string(255)
    position: string(255)
    email: string(255)
    fon: string(255)
    birthday: string(255)
    mobile: string(255)
    description: string
    picture: string(255)
    token: string(255)
  actAs:
    Timestampable:
  relations:
    User:
      class: sfGuardUser
      local: sf_guard_user_id
      foreignType: one
      foreignAlias: User
      onDelete: CASCADE

# Dateien
yeeCoreFile:
  actAs:
    Timestampable:
  columns:
    name: string(255)
    type: string(255)
    size: integer
    copyright_mode: string(255)
    copyright_value: string(255)
    metainfo: string
  relations:
    User:
      class: yeeCoreUser
      local: user_id
      foreignAlias: Issues
      onDelete: CASCADE

# Alle Magazine
yeeMagIssue:
  actAs:
    Timestampable:
  columns:
    type: string(255)
    status: string(255)
    name: string(255)
    description: string
    user_id: integer
    xml_cache: clob
  relations:
    User:
      class: yeeCoreUser
      local: user_id
      foreignAlias: Issues
      onDelete: CASCADE
    Users:
      class: yeeCoreUser
      refClass: yeeMagIssueUser
      foreignAlias: Issues
      local: issue_id
      foreign: user_id

# Die Kategorien in Magazinen
yeeMagIssueCategory:
  columns:
    name:
      type: string(255)
      notnull: true
    issue_id:
      type: integer
      notnull: true

# Kreuztabelle für die Verknüpfung von Magazinen und Benutzern = diese Leute arbeiten an einem Magazin
yeeMagIssueUser:
  actAs:
  columns:
    issue_id:
      type: integer
      primary: true
    user_id:
      type: integer
      primary: true
  relations:
    Issue:
      class: yeeMagIssue
      local: issue_id
      onDelete: CASCADE
    User:
      class: yeeCoreUser
      local: user_id
      onDelete: CASCADE

# Article
yeeMagArticle:
  actAs:
    Timestampable:
    Versionable:
  columns:
    type: string(255)
    name: string(255)
    description: string
    xml_cache: clob
    element_cache: clob

# Alle Inhalte eines Artikels (z.B. Textblock, Foto, Auflistung,...)
yeeMagElement:
  actAs:
    Timestampable:
    Versionable:
  columns:
    type: string(255)
    header: string(255)
    content: clob
  relations:
    Files:
      class: yeeMagFile
      refClass: yeeMagElementFile
      foreignAlias: Elements
      local: element_id
      foreign: file_id

# Kreuztabelle für Artikel-Elemente und Dateien
yeeMagElementFile:
  actAs:
  columns:
    element_id:
      type: integer
      primary: true
    file_id:
      type: integer
      primary: true
  relations:
    Element:
      class: yeeMagElement
      local: element_id
      onDelete: CASCADE
    File:
      class: yeeCoreFile
      local: file_id
      onDelete: CASCADE

# Alle Autoren (priority definiert die Reihenfolge)
yeeMagAuthor:
  columns:
    name:
      type: string(255)
      notnull: true
    article_id:
      type: integer
      notnull: true
    description: clob
    priority: integer
    relations:
      Articles:
        class: yeeMagArticle
        local: article_id
        onDelete: SET NULL

# Die Kategorien in der Artikel-Verwaltung
yeeMagArticleCategory:
  columns:
    name:
      type: string(255)
      notnull: true
	parent_category_id: integer
    issue_id:
      type: integer
      notnull: true
  relations:

# Softlinks (nur die ID)
yeeMagArticleSoftlinks:
  actAs:
    Timestampable:
  columns:
    content: clob
    article_id: integer
  relations:
    Article:
      class: yeeMagArticle
      local: article_id
      onDelete: CASCADE

# Soflink-Links
yeeMagArticleSoftlinks:
  actAs:
    Timestampable:
  columns:
    url: string
    description: string
    softlink_id: integer
  relations:
    Softlink:
      class: yeeMagSoftlink
      local: softlink_id
      onDelete: CASCADE

Beitrag mit anderen teilen:

  • Twitter
  • Facebook
  • FriendFeed
  • t3n Social News
  • del.icio.us
  • MisterWong.DE
  • Digg
  • Identi.ca
  • Technorati
  • RSS
  • E-mail this story to a friend!

Aktuelle News:

3 Antworten zu “Doctrine ORM: Beispielschema für die webbasierte Produktion eines Magazins”

  1. #1 Dirk

    Hallo Martin,

    coole Sache. Ich hätte jedoch ein paar Fragen - bin ziemlich neu in Symfony...

    Und zwar hast du in Tabelle yeeCoreUser bei der User-Relation onDelete: Cascade. Das heißt ja, wenn in sfGuardUser der Datensatz gelöscht wird wird auch der in yeeCoreUser gelöscht. Was passiert dann aber anders herum? Kann man das auch irgendwie festlegen?

    Wenn man nur actAs: Timestampable: (da fehlt die Tilde ~?!?!), werden die Felder created_at und updated_at und expires_at automatisch angelegt?

    Worin liegt denn genau der Vorteil von versionable? Habe das bisher nur auf englisch gelesen und nicht so wirklich kapiert...

    Ich hoffe, du kannst mir da weiterhelfen.

    Vielen Dank und Grüße,

    Dirk

  2. #2 Martin Herr

    Hi Dirk,

    > Was passiert dann aber anders herum? Kann man das auch irgendwie festlegen?

    Müsste man genauso im sfGuardUser-Model definieren. Ich hab das da im yeeCoreUser-Model mehr prophylaktisch definiert. Im Prinzip ist das Löschen eines Users aus einem Produktiv-System nicht besonders sinnvoll. Besser man macht den "blank", also überschreibt die Daten, deaktiviert ihn und markiert ihn als gelöscht (sonst kann man unter Umständen überhaupt nicht mehr nachvollziehen, was wer wann getan hat.) Ist aber auch ein Bißchen Geschmacksache.

    > Wenn man nur actAs: Timestampable: (da fehlt die Tilde ~?!?!), werden die Felder created_at und updated_at und expires_at automatisch angelegt?

    Ja. Die Tilde mache ich übrigens nie und der yaml-Parser hat mir das bisher immer verziehen.

    > Worin liegt denn genau der Vorteil von versionable? Habe das bisher nur auf englisch gelesen und nicht so wirklich kapiert...

    Das coole bei versionable ist, dass diese Eigenschaft dein Model automatisch versioniert und sich im Hintergrund um alles kümmert. D.h. es wird eine zweite Tabelle angelegt, wo die verschiedenen Versionen eines Datensatzes vorgehalten werden und du kannst bequem auf die Versionierungs-API zugreifen (Versionen switchen,Unterschiede anzeigen,...). Bei der Verwaltung von Artikeln ist das extrem wichtig, da mehrere Autoren sich sonst gegenseitig überschreiben könnten und man ohne Versionierung Gefahr läuft Daten zu verlieren.

    Gruß,
    Martin

  3. #3 Dirk

    Hi Martin,

    vielen Dank für deine ausführliche Antwort! Jetzt bin ich wieder ein Stück schlauer... ;)

    Vielen Dank und Grüße,

    Dirk

Du hast eine Ergänzung oder Frage zum Artikel? Teile sie jetzt mit!