Joomla XML Form: selezione multipla e relazioni molti a molti

Il framework impiegato da Joomla 2.5+ aiuta molto nell'editing dei dati, ma diversi argomenti non vengono mai trattati nelle varie guide che si trovano in rete. Uno di questi argomenti è la gestione delle relazioni molti a molti (o enne a enne) ignote ai programmatori dilettanti ma abbastanza comuni nei data base strutturati.

La relazione molti a molti

... stiamo parlando di database. Una situazione comune, nella progettazione dei database, è avere a che fare con questo tipo di relazione.

Prendete un esempio, abbastanza comune, che viene citato nei vari esempi di programmazione: quello dei libri (o dei cd, o dei films...). In tutti gli esempi si suppone che ogni autore abbia scritto diversi libri, ma che ogni libro abbia un solo autore (relazione uno a molti o uno a enne). Seppure la cosa possa sembrare sensata ad una una prima analisi, trasferendosi nel mondo reale ci si accorge subito che non è proprio così. Il libro sacro dei programmatori OOP non è forse stato scritto dalla "Gang of Four"?

Assolutamente da leggere: Design Patterns: Elements of Reusable Object-Oriented Software
di Gamma, E., Helm, R., Johnson, R. e Vlissides, J. ovvero la "Gang of Four".

Nel caso che tratteremo l'esigenza occorsa è la seguente: una azienda di produzione gestisce diversi marchi commerciali e una rete di rivenditori. non tutti i rivenditori possono però trattare tutti i marchi e la azienda decide quali marchi dare in gestione allo specifico rivenditore.

Il nostro database conterrà dunque le tabelle per le due entità (#__resellers e #__logos) e una tabella intermedia (#__resellers_logos) per gestire la relazione molti a molti.

Non è possibile gestire direttamente la relazione molti a molti senza compromettere efficienza ed integrità delle relazioni, pertanto è necessario ricorrere ad una tabella intermedia in modo da scomporre la relazione originaria in due relazioni uno a molti.

La scomposizione della relazione molti a molti

Nello schema E-R sopra riportato, potete vedere la scomposizione della relazione molti a molti tra le tabelle #_resellers e #_logos. Così scomposta, la relazione è ora perfettamente gestibile dal server SQL che può continuare a gestire l'integrità referenziale. Nello schema potete vedere che la tabella di relazione (#__resellers_logos) contiene solo le due FK (foreign keys) e le relazioni di dipendenza (molti a uno) verso le tabelle delle entità.

 

Usare le relazioni molti a molti in Joomla

Per gestire la nostra relazione sopra vista in joomla abbiamo quattro interventi da fare:

  1. aggiungere il selettore dei loghi nella form XML di definizione dell'editing del rivenditore
  2. creare il custom field per ottenere l'elenco dei loghi
  3. modificare il model del rivenditore per salvare i loghi attribuiti in gestione nella tabella di relazione
  4. modificare il model del rivenditore per caricare i loghi attribuiti per mantenere le relazioni durante l'editing dei records esistenti

Vediamo ciascun passaggio.

 

Aggiungere il selettore dei loghi nella form XML

Modifichiamo la form di editing del record singolo del rivenditore (che si trova nella cartella sotto i models: /administrator/components/com_[mycomponent]/models/forms/) e che nel nostro caso si chiama reseller.xml ed aggiungiamo un campo che elenchi i vari loghi disponibili:

<field 
    name="id_logos" 
    type="ResellerslogoIdLogo" 
    default="" 
    label="COM_MYCOMPONENT_LOGOS_LABEL" 
    description="COM_MYCOMPONENT_LOGOS_DESC" 
    required="true" 
    multiple="true" 
/>

Lasciamo a parte gli attributi "label" e "description" che interessano relativamente e concentriamoci su quelli importanti:

  • name:
    questo è il nome della variabile che verrà trasmesso dalla form di editing al salvataggio dei dati ed il nome del campo che andrà trasmesso alla stessa prima dell'inizio dell'editing
  • type:
    dobbiamo fornire l'elenco dei loghi disponibili, informazione che è in una altra tabella quindi abbiamo bisogno di un custom field (più gestibile rispetto a type="SQL")
  • multiple:
    come abbiamo detto la relazione è molti a molti, quindi ad uno stesso rivenditore si possono associare più loghi.

 

Creare il custom field

Creiamo ora il custom field per il recupero dei marchi commerciali disponibili dalla relativa tabella (#__logos) e salviamolo nella cartella specifica (che si trova nella cartella sotto i models: /administrator/components/com_[mycomponent]/models/fields/), nel nostro caso resellerslogoidlogo.php

<?php
defined('_JEXEC') or die;
jimport('joomla.form.helper');
JFormHelper::loadFieldClass('list');
 
class JFormFieldResellerslogoIdLogo extends JFormFieldList{
    protected $type = 'ResellerslogoIdLogo';
 
    protected function getOptions(){
        $db = JFactory::getDBO();
        $query = $db->getQuery(true);
        $query->select('`id_logo` as `key`, `logo` as `value`');
        $query->from('`#__logos`');
        $query->where('`published`=1');
        $query->order('`logo`');
        $db->setQuery($query);
        $rows = $db->loadObjectList();
        $options = array();
        if ($rows){
            foreach($rows as $row){
                $options[] = JHtml::_('select.option', $row->key, $row->value);
            }
        }
        $options = array_merge(parent::getOptions(), $options);
        return $options;
    }
}
?>

Il codice recupera tutti i marchi commerciali disponibili nella tabella e restituisce una dropdown realizzata tramite il tag HTML select (notate che eredita da JFormFieldList): questo farà sì che sia presente l'elenco dei marchi commerciali nella form di editing del rivenditore.

Il framework di Joomla 3 provvederà poi ad abbellire la visualizzazione con un selettore un poco più moderno del classico, ed alquanto bruttino, select html.

 

Gestire i dati dalla e verso la form xml

È ora necessario gestire i dati che provengono dalla form di editing, così come fornire alla stessa i dati corretti per la visualizzazione.

Per fare ciò modificheremo il model che gestisce il singolo record del venditore nella cartella dei models (/administrator/components/com_[mycomponent]/models/), nel nostro caso reseller.php.

Per prima cosa salviamo, nella tabella di relazione, i dati inviatici dalla form e relativi ai marchi associati al cliente tramite un override del metodo save():

public function save($data){
    if(!parent::save($data)) return false;        
 
    $id_reseller = $this->getItem()->get('id_reseller');
    $db = JFactory::getDBO();
    $query = "DELETE FROM `#__resellers_logos` WHERE `id_reseller` = '{$id_reseller}'";
    $db->setQuery($query);
    $db->query();
    
    foreach($data['id_logos'] as $id_logo){
        $query = "INSERT INTO `#__resellers_logos` (`id_reseller`,`id_logo`) VALUES ('{$id_reseller}', '{$id_logo}')";
        $db->setQuery($query);
        $db->query();
    }
    return true;
}

Notate che usiamo il metodo getItem() per recuperare la chiave primaria della tabella e non la leggiamo dall'array associativo $data. Il perché è presto spiegato: se si tratta dell'inserimento di un record, e non di una modifica, la PK in $data non è valorizzata.

Ottenuta la chiave primaria della tabella dei rivenditori ne cancelliamo tutte le occorrenze dalla tabella di relazione, dopo di che provvediamo a inserire tutti gli id_logo dei marchi selezionati.

Il campo "id_logos" è stato definito a valori multipli, quindi la chiave 'id_logos', dell'array associativo $data, conterrà sempre un array, anche qualora sia stato selezionato un solo valore.

Ora che abbiamo salvato i dati nella tabella di relazione dobbiamo anche preoccuparci di recuperarli in caso di editing del record, in modo che il framework di Joomla possa indicare gli elementi già selezionati. Per fare ciò eseguiremo un override del metodo loadFormData():

protected function loadFormData(){
    // Check the session for previously entered form data.
    $data = (object) JFactory::getApplication()->getUserState('com_mycomponent.edit.reseller.data', array());
    if (empty($data)){
        $data = $this->getItem();
    }
    
    $db = JFactory::getDBO();
    $query = "SELECT `id_logo` FROM `#__resellers_logos` WHERE `id_reseller` = '{$data->id_reseller}'";
    $db->setQuery($query);
    $db->query();
    $data->id_logos=$db->loadColumn();
    return $data;
}

Ci interessa la seconda parte del metodo, quella ove vi è la query.

Recuperiamo gli id dei loghi associati allo specifico rivenditore dalla tabella di relazione e li inseriamo, come array di valori, nel campo 'id_logos' che è il campo definito nella form xml. In questo modo il framework di joomla provvederà ad indicare quali sono i marchi già associati alla visualizzazione della form di editing (ovvero aggiungerà l'attributo 'selected' al tag 'option').

 

Siamo giunti alla fine di questo articolo, tecnico sì, ma speriamo interessante. Se però vi serve lo sviluppo di un componente per Joomla e non siete in grado di implementare il sistema da soli, ricordatevi che noi siamo a vostra disposizione.

Buon lavoro a tutti,

marco maria leoni

 

 

 

Aggiungi commento

Please note: URL in text are not linked and user's site address is only for internal use and is not published.

Comments are human checked. All spam will be removed, so don't waste your time and, especially, mine!

Codice di sicurezza
Aggiorna