Notifiche con Doctrine: ereditarietà con relazioni diverse per i figli

Stavo cercando di creare un sistema di notifiche (tipo wall di FB) di ciò che accade agli utenti del mio nuovo sito di ricette e mi sono imbattuto in un problema: ereditarietà con Doctrine e relazioni diverse per ogni figlio.
Premetto che il sito è realizzato con Sf 1.4 e Doctrine 1.2.
Accantonato per il momento l’utilizzo di database documentali e varianti, rimanendo nel mondo del relazionale, per l’implementazione del sistema di notifiche ho fatto il seguente ragionamento:
ci sono principalmente due modi per memorizzare le informazioni su “cosa sta accadendo” (es. nuova ricetta di un amico, commento ad una mia ricetta, follow, like, etc.):

  • Leggere ogni volta tutti gli inserimenti dalle rispettive tabelle (Recipe, Comment, Follow, Like)

    • Pro: non devo aggiungere modelli al db
    • Contro: la lettura delle informazioni è lentisssssima (devo leggere tutte le tabelle), la computazione (merge e ordinamento) è impegnativa e aumenta al crescere delle informazioni memorizzate
  • Scrivere e leggere su una tabella che raggruppa le notifiche.
    • Pro: il fetch è velocissimo, posso mantenere la distinzione delle tipologie di notifica, non è necessaria computazione aggiuntiva oltre al fetch per merge e ordinamento
    • Contro: devo realizzare e gestire un repository per le notifiche, la mappatura ai vari tipi di notifica non è banale

Naturalmente ho scelto la seconda, che sicuramente è la soluzione più pulita e performante.
Di seguito lo schema (semplificato) dei modelli da “notificare”.

Recipe:
  id
:
  user_id
:
  title
:
 ...
relations
:
    User
: { class: sfGuardUser, ... }

Comment
:
  id
:
  user_id
:
  recipe_id
:
  comment
:
 ...
relations
:
    User
: { class: sfGuardUser, ... }
    Recipe
: { class: Recipe, ... }

Follow
:
  user_id
:
  followed_user_id
:
relations
:
    User
: { class: sfGuardUser, ... }
    FollowedUser
: { class: sfGuardUser, ... }

Like
:
  id
:
  user_id
:
  recipe_id
:
 ...
relations
:
    User
: { class: sfGuardUser, ... }
    Recipe
: { class: Recipe, ... }

Ed eccoci a modellare le notifice: per realizzare la mappatura alle varie tipologie di notifica ho deciso di utilizzare l’ereditarietà con aggregazione di colonne di Doctrine.

Notification:
  columns
:
    user_id
:
    dest_user_id
:
    subject_model
:
    subject_id
:
   ...
  relations
:
    User
: { class: sfGuardUser ... }
    DestUser
: { class: sfGuardUser, ... }
  indexes
:
    subject_id_index
:
      fields
: [subject_id]

NotificationComment
:
  inheritance
: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 1 }
NotificationRecipe
:
  inheritance
: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 2 }
NotificationFollow
:
  inheritance
: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 3 }
NotificationLike
:
  inheritance
: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 4 }

Bene, ora ho quello che mi serve… o quasi. Considerando che subject_id sia l’id del soggetto della notifica (es. Ricetta o Commento) mi serve una relazione con il repository del soggetto stesso, cosicchè possa avere direttamente l’oggetto relativo alla notifica:

$notification_recipe = Doctrine::getTable('NotificationRecipe')->find(1);
$recipe = $notification_recipe->getRecipe();

Come aggiungo le relazioni? Ho provato con le relazioni “classiche”:

NotificationComment:
  inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 1 }
  relations:
    Comment: { class: Comment, ... }
NotificationRecipe:
  inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 2 }
  relations:
    Recipe: { class: Recipe, ... }

Funziona! Ma… il problema è che alla creazione del database le relazioni le crea… TUTTE nella stessa tabella, quindi abbiamo Notification che ha come constraint tutte le relazioni definite, con Recipe, Comment, Like e Follow.
Il che non funziona affatto, poichè ad ogni inserimento avremo errori di chiavi importate inesistenti nella tabella Notification. Sigh!
Ricominciamo ed eliminiamo le relazioni dallo schema.yml.
Cercando in rete ho trovato un attributo che fa al caso mio: Doctrine::EXPORT_CONSTRAINTS
Quindi ho modificato i modelli dicendo di non esportare le constraint:

class NotificationRecipe extends BaseNotificationRecipe
{
  public function setUp()
  {
      parent::setUp();
      $this->hasOne('Recipe', array(
           'local' => 'subject_id',
           'foreign' => 'id',
           'onDelete' => 'cascade'));
      $this->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL ^ Doctrine::EXPORT_CONSTRAINTS);
  }
}

Si può fare anche direttamente nello schema.yml ma non ho indagato otre, perchè già così… FUNZIONA!!!

Buon divertimento

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someone
Written by kea

2 Comments

Leave a Reply

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *