2013-05-23 15 views
8

Intoduction:oggetto mappatura MongoDB (PHP) problema

Qual è la prassi migliore per costruire il mio class T oggetto, quando ricevo da un MongoCursor::getNext()? Per quanto possibile, la funzione getNext() di un MongoCursor restituisce con un array. Desidero utilizzare il risultato da quel punto come object di tipo T.

Devo scrivere il mio costruttore per il tipo T, che accetta uno array? Esiste una soluzione generica a questo, ad esempio quando il tipo T extends G e G esegue il lavoro come un modo normale, in modo ricorsivo (per i documenti nidificati).

Sono nuovo di MongoDB e mi piacerebbe creare il mio mapper generico con una bella interfaccia.

Bounty:

  • Quali sono i possibili approcci, modelli, e che si adatterebbe il concetto di MongoDB più dal punto di vista di PHP.
+0

Se spieghi di più su che tipo di risposta vuoi, probabilmente ne forniremo una, voglio dire a meno di programmarla per te cosa vuoi? – Sammaye

+0

Ti viene presentato un approccio molto costruttivo e utile per me, e ho già imparato molto sulla tua tecnica e implementazione. Grazie per questo! Inoltre, sono aperto a qualsiasi approccio diverso che è là fuori, sono disposto a vederlo, se esiste. Sto cercando di esplorare tutte le possibilità e i modelli su questo problema. – Dyin

risposta

3

Questa risposta è stata riscritta.

La maggior parte dei mappatori di dati funziona rappresentando un oggetto per classe o "modello" è normalmente il termine coniato. Se si desidera consentire l'accesso multiplo tramite un singolo oggetto (ad esempio $model->find()), viene normalmente disattivato in modo che il metodo non restituisca effettivamente un'istanza di se stesso ma invece quella di un array o di una classe di caricamento MongoCursor nello spazio.

Tale paradigma è normalmente collegato a "Active Record". Questo è il metodo che ORM, ODM e framework utilizzano tutti per comunicare in un modo o nell'altro in database, non solo per MongoDB ma anche per SQL e qualsiasi altro database che si verifica (Cassandra, CouchDB ecc. Ecc.).

Occorre notare immediatamente che anche se il record attivo fornisce molta potenza, non dovrebbe essere coperto nell'intera applicazione. Ci sono momenti in cui l'utilizzo diretto del driver sarebbe più vantaggioso. La maggior parte degli ORM, degli ODM e dei framework offre la possibilità di accedere rapidamente e senza sforzo al driver direttamente per questo motivo.

C'è, come molti direbbero, nessun mappatore di dati leggeri. Se hai intenzione di mappare i tuoi dati restituiti alle classi allora consumerà risorse, alla fine di. Il vantaggio di farlo è il potere che ricevi quando manipoli i tuoi oggetti.

Il record attivo è davvero ottimo per poter fornire eventi e trigger da PHP.Un buon esempio è quello di un ORM ho realizzato per Yu: https://github.com/Sammaye/MongoYii può fornire ganci:

  • afterConstruct
  • beforeFind
  • afterFind
  • beforeValidate
  • afterValidate
  • beforeSave
  • afterSave

Si deve notare che, quando si tratta di eventi come beforeSave e afterSave MongoDB non possiede trigger (https://jira.mongodb.org/browse/SERVER-124) quindi ha senso che l'applicazione deve gestire questa situazione. Oltre all'ovvia ragione per cui l'applicazione è in grado di gestirlo, è anche possibile gestire in modo migliore le funzioni di salvataggio, potendo chiamare le funzioni PHP native per manipolare ogni documento salvato prima di toccare il database.

La maggior parte dei mappatori di dati funziona utilizzando il CRUD di classe proprio PHP per rappresentare anche il loro. Ad esempio, per creare un nuovo record:

$d=new User(); 
$d->username='sammaye'; 
$d->save(); 

questo è abbastanza un buon approccio in quanto si crea un "nuovo" (https://github.com/Sammaye/MongoYii/blob/master/EMongoDocument.php#L46 mostra come mi preparo per un nuovo record nella MongoYii) classe per fare una "nuova" record. Si adatta abbastanza bene semanticamente.

Le funzioni di aggiornamento sono normalmente accessibili tramite le funzioni di lettura, non è possibile aggiornare un modello di cui non si conosce l'esistenza. Questo ci porta al prossimo passo del popolamento dei modelli.

Per gestire il popolamento di un modello, diversi ORM, ODM e framework si impegnano in metodi diversi. Ad esempio, la mia estensione MongoYii utilizza un metodo factory chiamato model in ogni classe per riportare una nuova istanza di se stesso in modo che possa chiamare il dinamico find e findOne e altri metodi simili.

Alcuni ORM, ODM e framework forniscono le funzioni di lettura come funzioni dirette static rendendole esse stesse dei metodi di fabbrica mentre alcuni utilizzano il modello singleton, tuttavia, ho scelto di non eseguire (https://stackoverflow.com/a/4596323/383478).

La maggior parte, se non tutti, implementa una qualche forma del cursore. Questo viene utilizzato per restituire multipli dei modelli e avvolge (normalmente) direttamente lo MongoCursor per sostituire il metodo current() con la restituzione di un modello di pre-compilazione.

Per esempio chiamando:

User::model()->find(); 

restituirebbe una EMongoCursor (in MongoYii), che avrebbe poi sotre il fatto che la classe User è stato utilizzato per creare un'istanza il cursore e quando viene chiamato come:

foreach(User::model() as $k=>$v){ 
    var_dump($v); 
} 

Chiamerebbe il metodo current() qui: https://github.com/Sammaye/MongoYii/blob/master/EMongoCursor.php#L102 restituendo una nuova istanza singola del modello.

Ci sono alcuni ORM, ODM e framework che implementano il caricamento di array desiderosi.Ciò significa che caricheranno l'intero risultato direttamente nella RAM come una serie di modelli. Personalmente non mi piace questo approccio, è uno spreco e non promette nulla di buono quando è necessario utilizzare il record attivo per gli aggiornamenti più grandi grazie all'aggiunta di alcune nuove funzionalità in luoghi che devono essere aggiunti ai vecchi record.

Un ultimo argomento prima di andare avanti è la natura schemaless di MongoDB. Il problema con l'utilizzo di classi PHP con MongoDB è che vuoi tutte le funzionalità di PHP ma con la natura variabile di MongoDB. Questo è facile da superare in SQL dal momento che ha uno schema predefinito, basta interrogarlo e fare i lavori; tuttavia, MongoDB non ha nulla del genere.

Ciò rende la gestione dello schema in MongoDB piuttosto pericolosa. La maggior parte degli ORM, degli ODM e dei framework richiede la pre-definizione dello schema in loco (ad esempio Doctrine 2) utilizzando le variabili private con i metodi get e set. In MongoYii, per rendere la mia vita facile ed elegante, ho deciso di mantenere la natura senza schemi di MongoDB utilizzando magie che rilevassero (https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L26 è il mio __get e https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L47 è il mio __set), se la proprietà è inaccessibile nella classe, se il campo era in un array interno _attributes e, in caso contrario, restituire semplicemente null. Allo stesso modo, per impostare un attributo, vorrei semplicemente impostare la variabile interna _attributes.

Quanto a che fare con il modo di assegnare questo schema ho lasciato l'assegnazione interna fino a che l'utente tuttavia, a che fare con l'impostazione delle proprietà da forme ecc Ho usato le regole di validazione (https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L236) chiamando una funzione chiamata getSafeAttributeNames() che restituirà un elenco di attributi che aveva delle regole di validazione contro di loro. Se non avessero regole di convalida, non sarebbero stati impostati quegli attributi che esistevano nell'array $_POST o $_GET in arrivo. Quindi questo ha fornito la possibilità di uno schema, ma sicuro, della struttura del modello.

Quindi abbiamo spiegato come utilizzare effettivamente il documento di root e si chiede anche come i Data Mapers gestiscono i documenti secondari. Doctrine 2 e molti altri forniscono documenti secondari basati su classi (http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/embedded-mapping.html) ma questo può essere estremamente ricco di risorse. Invece ho deciso che avrei fornito funzioni di supporto che consentissero un utilizzo flessibile del documento secondario senza caricarlo in modelli e quindi consumando RAM. Fondamentalmente quello che ho fatto è stato di lasciarli come sono un fornire un validatore (https://github.com/Sammaye/MongoYii/blob/master/validators/ESubdocumentValidator.php) per la convalida al loro interno. Ovviamente il validatore si auto genera, quindi se si avesse una regola nel validatore che usava di nuovo il validatore per emettere una convalida di un documento secondario nidificato allora funzionerebbe.

Quindi I penso che completa una discussione molto basilare di ORM, ODM e framework utilizzano i mappatori di dati. Naturalmente potrei probabilmente scrivere un intero saggio su questo argomento, ma questa è una discussione abbastanza buona per il momento in cui credo.

+0

Se si sta estendendo il 'MongoCursor', di' EMongoCursor', perché non 'estendersi (s)' esso? Cos'è '$ this-> partial' in' getNext() '? Cosa ne pensate di questo approccio, quando date il diritto a 'T' di comunicare effettivamente con il database, ad esempio:' $ t-> update() '? – Dyin

+0

@Dyin Ti consiglio vivamente di non estendere l'oggetto c di MongoCursor, ma sempre di cose brutte che ti arrivano. '$ this-> partial' è un errore, questo è effettivamente preso dal mio Yii ORM e modificato per essere autonomo.Per quanto riguarda la comunicazione "T", in realtà implemento il record attivo per Yii per impostazione predefinita, se riesci a capire che questo è il codice base: https://github.com/Sammaye/MongoYii ed eccone uno più generico: https : //github.com/Sammaye/mongoglue – Sammaye

+0

Potresti, per favore, migliorare la tua risposta aggiungendo note sulle decisioni di questo disegno e approccio? È una versione leggera di un pattern Data Mapper? Forse altri esempi di utilizzo sarebbero utili per capire meglio il tuo approccio. – Dyin