Ho già parlato di DBIx::Class.
Avendo result source con relazioni si possono ottenere risultati interessanti.
Esistono gli oggetti Folk ed Email, ove l’oggetto Folk sia in relazione con molteplici oggetti Email (folk has_many emails).
[…]
__PACKAGE__->has_many(‘emails’,‘My::Schema::Result::Email’,‘id’);
E’ possibile creare un record scrivendo i dati dell’oggetto/i relativo/i in un arrayref.
my $new_folk = $folks_rs->create({
name => ‘Test folk’,
emails => [
{
email => ‘mail@casa.name’,
comment => ‘Test mail casalinga’,
},
{
email => ‘mail@azienda.org’,
comment => ‘Test mail aziendale’,
},
],
});
E ci si libera di tutto con un bel
Come già accennato, le relazioni fra gli schemi (che rappresentano tabelle) avvengono grazie alla configurazione di relazioni.
Le due righe che seguono fanno esattamente la stessa cosa, e creano una relazione fra lo schema Folks e Addresses.
__PACKAGE__->has_many( ‘addresses’, ‘Tr0n3::Schema::Agenda::Result::Addresses’, ‘id’ );
Dunque se vorremo fare una join, potremo chiamare join sul metodo che determina la relazione (addresses, guarda caso l’ho chiamato come la tabella da joinare, ma si noti bene che quello è il nome della relazione segnato come primo parametro in has_many) proprio come segue:
che lancerà la seguente query:
Come si vede dall’SQL risultante, i dati della tabella joinata NON sono fetchati dal database (e nemmeno i metodi corrispettivi), comunque è possibile filtrare i dati su entrambe le tabelle sfruttando nella clausola WHERE anche i campi della seconda tabella.
Ad esempio:
Cerca i dati in Folks dove il nome del folk è ‘Simone’ e dove la città (in addresses) finisce per ‘a’.
Con prefetch al posto di join vengono fetchate tutte le colonne:
Come spiegato qua, per accedere ai dati si può usare un metodo già definito oppure get_column().
La selezione delle colonne avviene attraverso i metodi select e as
oppure con columns
che mi è parso una combinazione dei due.
Si può mettere un ‘+’ davanti al nome per aggiungere le colonne selezionate al resultset.
Ad esempio, dopo una join in cui vengono richiamati solo i campi della tabella principale, potremmo rendere disponibili le colonne di un altra tabella con ‘+columns’:
Inoltre grazie alle relazioni potremo anche raccattare dati da uno schema diverso dal resultset di partenza, come in questo caso:
che sarà rappresentato da queste due query consecutive:
SELECT me.id, me.address, me.zip, me.city, me.country FROM addresses me WHERE ( me.id = ? ): ’27′
Questi sono gli oggetti Addresses (cioè gli indirizzi) appartenenti al Folk num. 27:
my @addresses = $folk->addresses;
Dopo aver bestemmiato come un satanista, forse ne sono uscito. Finalmente.
Gli schemi che si mangia DBIx::Class sono di questo genere (ho tagliato la definizione delle colonne, perché irrilevante):
[…]
use base ‘DBIx::Class’;
__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "EncodedColumn", "Core");
__PACKAGE__->table("thoughts");
__PACKAGE__->add_columns(
"id", { […] },
"title", { […] },
"body", { […] },
"create_time", { […] },
"last_modif", { […] },
"author_id", { […] },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("thought_pkey", ["id"]);
__PACKAGE__->belongs_to(
"author_id", "Tr0n3::Schema::Main::Result::Authors", { id => "author_id" },
);
1;
e
[…]
use base ‘DBIx::Class’;
__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "EncodedColumn", "Core");
__PACKAGE__->table("authors");
__PACKAGE__->add_columns(
"id", { […] },
"nick", { […] },
"password", { […] },
"mail", { […] },
"create_time", { […] },
"last_login", { […] },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("authors_pkey", ["id"]);
__PACKAGE__->has_many(
"links", "Tr0n3::Schema::Main::Result::Links", { "foreign.author_id" => "self.id" },
);
__PACKAGE__->has_many( "thoughts", "Tr0n3::Schema::Main::Result::Thoughts", { "foreign.author_id" => "self.id" },
);
1;
Per fare una join con DBIC ci si appoggia alle relazioni:
Thoughts->belongs_to( "author_id", "Tr0n3::Schema::Main::Result::Authors", { id => "author_id" }, );
has_many crea una relazione “uno a molti”, e ha come argomenti: il nome dell’accessor, il nome della classe che rappresenta la tabella, un hashref con la reference (a cosa punta la foreign key).
In pratica lo schema Authors è in connessione uno a molti con Thoughs (per un Author ci possono essere molti Thoughts) , la relazione si chiama thoughts e vi si accede attraverso l’accessor thoughts, che punta allo schema Main::Result::Thoughts, secondo la relazione author_id (nella tabella thoughts) – id (nella tabella author).
In maniera analoga, lo schema Thoughts ha una relazione belongs_to con nome author_id, che punta allo schema Authors e che lega l’id in Authors con l’author_id di Thoughts.
Grazie a queste relazioni esplicitate nello schema, è possibile creare delle join in DBIx::Class:
Questa join parte dal ResultSet di Thoughts e senza filtri fa un JOIN con author attraverso l’accessor author_id.
L’SQL risultante è:
(Il prefisso “me” – inserito da DBIC – indica la tabella a sinistra del join.)
A forza di spaccarsi la testa sulle cose, ogni tanto se ne esce.
Avendo un database su PostgreSQL, e volendo manipolarlo con strumenti ORM in perl si può usare DBIx::Class (DBIC per gli amici).
DBIC si appoggia a “schemi” di tabelle nel database che gli permettono di interagire col db-server in maniera corretta (e ottimizzata).
Gli schemi sono un po’ pallosi da estrarre, ma a tutto c’è soluzione:
Dopo questo comando nella directory Sim ci sarà un pacchetto Prova.pm (lo schema) che caricherà le classi che rappresentano le tabelle del database XXX (nella dir Sim/Prova/tabella_*.pm).
Le rappresentazioni delle tabelle sono interessanti da guardare.
Da questo momento sarà possibile scrivere script che usino lo schema (Sim::Prova), si connettano attraverso di esso (metodo connect) ed agiscano sui dati del database XXX.
use Sim::Prova;
my $schema = Sim::Prova->connect(‘dbi:Pg:dbname=XXX’, ‘YYY_user’, ‘ZZZ_passwd’, {});
my @all_authors = $schema->resultset(‘Authors’)->all;
foreach (@all_authors) {
print $_->name ."\n";
}
L’oggetto $schema porta la connessione, tramite ->resultset(‘table’) capisce com’è organizzata la tabella nel db e poi accetta i metodi utili per fare qualcosa (CRUD: Create, Retrieve, Update, Delete).
$schema->resultset(‘tabella’)->create( {id => 1, name => ‘Simone’} );
# R
my @rows = $schema->resultset(‘tabella’)->all;
foreach $row (@rows) {
print $_->id ." | ". $_->name ."\n";
}
# U
my $author = $schema->resultset(‘tabella’)->find(1);
# dentro $author c’è il nostro record.
$author->name(‘Mario Rossi’);
# rimpiazza l’attributo "name" di $author con qualcosa di diverso.
# A questo punto il database ha ancora il record non cambiato, ma
# $author contiene un record differente.
$author->update();
# così aggiorno i dati sul db.
# D
# Come update, si identifica il record giusto e…
$author->delete();
Un po’ di basic usage e di manualistica.