design pattern in php

Salve.

Vorrei capire qualche concetto basilare da applicare a PHP, quindi espongo i miei dubbi.

Solitamente l'MVC separa le diverse logiche di un'applicazione; i dati provengono da una base di dati gestita dal pattern DAO, che istanzia degli oggetti indicati nel Model del pattern MVC.

In molte guide si legge che il Model dell'MVC non è altro che il Business Logic dell'architettura 3-tier o ancora, una sorta di handler del BL, che resta così ulteriormente separato dal resto dell'applicazione; però in altre guide leggo che in altri contesti (java ad esempio, python, etc), dal DAO si ottengono oggetti di tipo DTO, VO, etc. pronti per essere inviati al View del pattern MVC.

Vorrei capire e sapere se è corretto in PHP quanto sto per ipotizzare, considerando che la semplicità deve fare da padrona e che ho una classe base, cioè la stdClass, utilizzata molte volte per rappresentare un record di una tabella.

Immaginando di utilizzare l'architettura 3-tier, conto di implementare il pattern DAO per gestire la persistenza dei dati. Le mie classi DAO (costituite da metodi statici con la definizione delle varie query), istanziano oggetti di tipo stdClass i quali, a loro volta saranno passati alle classi del Model.

Tali classi del Model, hanno una serie di metodi che modificano lo stato di tutto il Model; quindi il Model si comporta come se fosse il Business Logic.

Ad esempio, molto semplicemente:

<?php
class AutoDAO {
 public static function selectAutoById($id) {
  $query = " SELECT * FROM auto where id = ". $id;
  // esecuzione query e return oggetto stdClass $auto che rappresenta il record; poi:
  return new AutoBL($auto); //AutoBL è una classe del Model
 }
 public static function updateAutoByAuto($auto) {
  $query = " UPDATE...";
 }
}
class AutoBL {
 protected $id;
 protected $carburante; // in litri
 // getter & setter
 // metodi aggiuntivi
 public function accelera() {
  // un'accelerata decrementa il carburante ad esempio, ammesso ce ne sia >= 1
  $this->carburante--;
 }
 /*cast a stdClass per il view o per il DAO, utile per aggiornare i dati sul DB e/o per essere inseriti nella pagina html senza problemi*/
 public function toStdClass() {
  $auto = array('id' => $this->id, 'carburante' => $this->carburante);
  return (object)$auto;
 }
}
?>
<?php
// dal View arriva l'id dell'auto nella variabile $id e che si vuole accelerare, quindi nel Controller accade:
$autoBL = AutoDAO::selectAutoById($id);
$autoBL->accelera(); // $autoBL adesso ha modificato lo stato del Model
AutoDAO::updateAutoByAuto($autoBL);
// dopodiché si potrà avvisare l'utente dell'avvenuta operazione, con un return, redirect... passando l'oggetto $auto
$auto = $autoBL->toStdClass();
?>

La rispettiva pagina html, potrebbe facilmente implementare:

<html><body>
<p>L'auto ha accelerato, e ha <?php print $auto->carburante ?> litri di autonomia!</p>
</body></html>

Il precedente sorgente potrebbe essere migliorato (forse?) in questo modo:

<?php
// si aggiunge una classe Model alle precedenti:
class AutoModel {
 protected $autoBL;
 public function __construct($id) {
  $this->autoBL = AutoDAO::selectAutoById($id);
 }
 public function accelera() {
  $this->autoBL->accelera();
  AutoDAO::updateAutoByAuto($this->autoBL);
 }
 public function toStdClass() {
  return $this->autoBL->toStdClass();
 }
}
// un'ulteriore definizione di AutoModel:
class AutoModel extends AutoBL {}
// dipende sempre dal contesto in cui si opera...
?>
<?php
// quindi il Controller diventa:
$autoModel = new AutoModel($id);
$autoModel->accelera(); // $autoBL adesso ha modificato lo stato del Model
// e infine:
$auto = $autoModel->toStdClass();
?>

Che ne pensate? Avrei sbagliato qualcosa? Se si cosa?

Attendo feedback, grazie.

inviato 5 anni fa
larchitetto
X 0 X

Mi pare che tu sia ancora lontano da implementare un MVC: dove sono C e V?

Anche in M non mi sembra che stiamo messi molto bene: se devi fare una classe piena di metodi statici, sei praticamente rimasto al procedurale (hai solo un elenco di funzioni dentro una classe).

E poi, il tuo M vorrebbe implementare un ActiveRecord?

E ancora, che senso ha un metodo che mette in valori in un array, per poi far il cast a oggetto? Solo per avere un accesso alle proprietà in V? Con quali vantaggi rispetto a un accesso diretto sull'array?

risposto 5 anni fa
Massimiliano Arione
X 0 X

Ciao,

grazie della risposta; rispondo passo passo.

Mi pare che tu sia ancora lontano da implementare un MVC: dove sono C e V?

C e V li ho tralasciati in questo topic poiché non sono oggetto di discussione: volevo un po' chiarire il comportamento di M e di BL.

Anche in M non mi sembra che stiamo messi molto bene: se devi fare una classe piena di metodi statici, sei praticamente rimasto al procedurale (hai solo un elenco di funzioni dentro una classe).

Ho ragionato come segue: le DAO (magari con una super classe) solitamente memorizzano il driver per il DB, l'attuale connessione, i nomi delle tabelle, le query base (select, update, insert, delete)... Tutte queste informazioni (imo) sono statiche: se creo AutoDAO per la tabella auto, potrei creare una costante NOME_TABELLA, non serve un set per questo, anzi potrei manipolare semplicemente il valore della costante __CLASS__; per quanto riguarda il driver, solitamente è una stringa all'interno di una variabile in qualche file di configurazione, e la connessione viene eseguita una sola volta (pconnect) e gestita in altre porzioni di software.

Implementandole con metodi non statici, potrebbe diventare, sempre richiamando l'esempio precedente:

<?php
class AutoDAO {
 public function __construct() {}
 public function selectAutoById($id) {
  $query = " SELECT BLA BLA BLA...";
  return new AutoBL($auto);
 }
}
?>

Poi per richiamarla:<?php $autoDAO = new AutoDAO(); // magari via factory creo l'instanza... $autoBL = $autoDAO->selectAutoById($id); ?>In ogni caso non sembra si stravolga il concetto...

E poi, il tuo M vorrebbe implementare un ActiveRecord?

In che senso?

E ancora, che senso ha un metodo che mette in valori in un array, per poi far il cast a oggetto? Solo per avere un accesso alle proprietà in V? Con quali vantaggi rispetto a un accesso diretto sull'array?

Intendi il metodo toStdClass? Ho trovato comodo creare un array castato a oggetto, giusto per non scrivere un esempio con troppi fronzoli, ma il metodo casterebbe l'oggetto a stdClass, per avere poi nel View un oggetto con proprietà public (che non creano alcun problema) da poter utilizzare senza dover richiamare metodi e cose così...

Ho scelto di castare a stdClass (o classi equivalenti chiamate VO o Entity) perché è una classe speciale utilizzata da molti prodotti per il trasporto di dati da DB al View. Potrei implementare un metodo toArray pure, che casta ad array: sono scelte.

Sinceramente trovo più comodo scrivere in mezzo all'HTML qualcosa come <p><?php print $obj->property ?> oppure <?php print $arr['property'] ?></p> che ad esempio <p><?php print $obj->getProperty(); ?></p>. Se la property non esiste, via oggetto stdClass, mi rende comunque null che, nel caso di una pagina html sarà castato a stringa vuota e non mi devo curare di nient'altro.

Tra l'altro alcuni prodotti si aspettano un oggetto stdClass o un array che poi, in fase di costruzione del View, viene smembrato in diverse variabili, ancora più gestibili; dall'esempio precedente:

<?php
// da oggetto nel Controller
$obj = new stdClass();
$obj->p1 = "un valore";
$obj->p2 = "un altro valore";
// a variabili nel View
?>
<p>L'oggetto ha questi valori: <?php print $p1 ?> e <?php print $p2 ?>...</p>

Tornando al metodo, avrei potuto implementare:

<?php
public function toStdClass() {
 $auto = new stdClass();
 $auto->id = $this->id;
 $auto->carburante = $this->carburante;
 return $auto;
}
// oppure
public function toStdClass() {
 $auto = new stdClass();
 foreach ($this as $property => $value) {
  $auto->$property = $value;
 }
 return $auto;
}
// oppure
public function toStdClass() {
 return new stdClass(array('id' => $this->id, 'carburante' => $this->carburante));
}
// etc
?>

In altri ambienti (java ad esempio), ho visto oggetti BL che avevano un metodo getDTO, ad esempio: il DTO è un oggetto che si occupa di trasportare i dati: ciò che farebbe l'entità stdClass o array che sia...

Alla fine di tutto ciò, non volevo capire come implementare un MVC in php, bensì capire se Model e Business Logic sono la stessa cosa, oppure cos'hanno di diverso e in che modo utilizzarli, pur rimandendo nell'ambito della stessa applicazione in php.

  • Nel primo esempio, infatti, l'oggetto BL fa delle operazioni che alterano lo stato dell'applicazione, ma poi qualcuno dovrebbe avvisare in qualche modo la base di dati dell'avvenuto cambiamento, che avviene all'interno del Controller stesso: M e BL sarebbero quindi la stessa cosa.

  • Oppure, nel secondo esempio, BL fa delle operazioni secondo le sue regole e poi è il Model che si occupa di chiamare il BL e di aggiornare la base di dati; immagino che una classe del genere potrebbe avere tra le proprietà un oggetto BL e il rispettivo DAO - eliminando i metodi statici...

Come si risolve infine?

Grazie.

risposto 5 anni fa
larchitetto
X 0 X

La mia esperienza è strettamente legata a un'implementazione di M con AR, quindi forse limitata.

Per me quindi M e BL sono la stessa cosa.

Un riferimento: http://en.wikipedia.org/wiki/Active_record#PHP

risposto 5 anni fa
Massimiliano Arione
X 0 X

Letto il link, grazie.

Mai usato AR, però ho visto un po' come funziona.

Quindi potrei fare come da te suggerito o, in base alla situazione in cui mi trovo, anche considerare l'eventualità di avere una struttura classi in M / BL un po' più complessa atta a gestire meglio le operazioni fatte e i relativi aggiornamento di stato su DB...

risposto 5 anni fa
larchitetto
X 0 X

I vantaggi di AR sono che hai una mappatura 1:1 tra il database e i tuoi oggetti. Se poi usi un ORM, come ad esempio Propel, ti risparmi un sacco di lavoro (ma proprio tanto!), perché hai il 90% di M generato in base a un file di configurazione.

Gli svantaggi sono proprio nella mappatura 1:1, se per te è troppo stretta (il che è abbastanza raro). Un altro svantaggio è la maggiore difficoltà nello scrivere test unitari, perché tutta la parte relativa alla persistenza va mockata.

risposto 5 anni fa
Massimiliano Arione
X 0 X
I vantaggi di AR sono che hai una mappatura 1:1 tra il database e i tuoi oggetti. Se poi usi un ORM, come ad esempio Propel, ti risparmi un sacco di lavoro (ma proprio tanto!), perché hai il 90% di M generato in base a un file di configurazione.

1:1 sta ad indicare che per ogni tabella esiste una classe dedicata? l'ORM mi ispira...Gli svantaggi sono proprio nella mappatura 1:1, se per te è troppo stretta (il che è abbastanza raro). Un altro svantaggio è la maggiore difficoltà nello scrivere test unitari, perché tutta la parte relativa alla persistenza va mockata.test unitari? "mockata"?

Io devo realizzare un prototipo di applicativo in php, una cosa semplicissima giusto per vedere come potrebbe venir fuori il software finale; quindi niente che ci porti a pensare come progettare e sviluppare l'applicativo per un ambiente di produzione.

Se esiste qualche cosa che mi genera tutti gli oggetti di cui ho bisogno dando qualche cosa, ben venga...

risposto 5 anni fa
larchitetto
X 0 X
1:1 sta ad indicare che per ogni tabella esiste una classe dedicata? l'ORM mi ispira...

In realtà ne vengono generate due: una che rappresenta una riga della tabella, un'altra che contiene i metodi per gestire operazioni su insiemi di righe (tipicamente l'esecuzione di query)

test unitari? "mockata"?

Io devo realizzare un prototipo di applicativo in php, una cosa semplicissima giusto per vedere come potrebbe venir fuori il software finale; quindi niente che ci porti a pensare come progettare e sviluppare l'applicativo per un ambiente di produzione.

Se esiste qualche cosa che mi genera tutti gli oggetti di cui ho bisogno dando qualche cosa, ben venga...

Confermo che Propel, partendo da un semplicissimo schema scritto in YML (o in XML, se ti piace essere verboso), genera tutte le classi per gestire oggetti e query, comprese le relazioni.

Ho parlato di test perché dovrebbero essere una parte integrante di qualsiasi sviluppo, indipendentemente dallo scopo. Poi ovviamente sta alla responsabilità di ciascuno, valuta tu.

risposto 5 anni fa
Massimiliano Arione
X 0 X
In realtà ne vengono generate due: una che rappresenta una riga della tabella, un'altra che contiene i metodi per gestire operazioni su insiemi di righe (tipicamente l'esecuzione di query)

La cosiddetta Entity (che rappresenta un record) e la classe BL (che implementa dei metodi) - letto su diverse guide.

Confermo che Propel, partendo da un semplicissimo schema scritto in YML (o in XML, se ti piace essere verboso), genera tutte le classi per gestire oggetti e query, comprese le relazioni.

Ho parlato di test perché dovrebbero essere una parte integrante di qualsiasi sviluppo, indipendentemente dallo scopo. Poi ovviamente sta alla responsabilità di ciascuno, valuta tu.

Ok grazie per le info. Adesso mi prendo un momentino e mi spulcio questi prodotti...

risposto 5 anni fa
larchitetto
modificato 5 anni fa
X 0 X

Aggiungo solo che ORM e DAO svolgono un compito simile, quello di colmare il divario tra il modello relazionale e quello ad oggetti.

In realtà in PHP ORM e DAO possono essere utilizzati anche per passare da modello relazionale ad Array.

Rimane però da organizzare la business logic. Anche se gli attuali ORM permettono di inserire della business logic nelle loro classi, io preferisco realizzare un ulteriore strato dedicato essamente alla BL.

Il Model quindi si divide in 2 strati, quello più esterno contiene la BL metre quello strato più interno è costituito dall'ORM o dal DAO.

L'interazioe avviene in quest'ordine: Controller->BL->ORM/DAO->DATABASE

Nel controller non deve esserci alcun riferimento all'ORM/DAO.

Visto che stiamo parlando di PHP, i dati che transitano tra i vari livelli possono essere costituiti da oggetti o semplici array.

Circa lo strato ORM/DAO, oltre all'ActiveRecord è interessante provare anche il pattern Table Data Gateway e Row Data Gateway utilizzati nello ZF:

http://www.martinfowler.com/eaaCatalog/tableDataGateway.html

http://www.martinfowler.com/eaaCatalog/rowDataGateway.html

 :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

Grazie della risposta.

Quindi, se nel Controller non vi fosse alcun riferimento allo stato di ORM/DAO e tentassimo di separare la BL dall'ORM/DAO, nel Controller potrei avere: $autoBL = new AutoBL($targa);, la classe AutoBL avrebbe un costruttore simile: $this->dao = new AutoDAO(); $this->entity = $this->select($targa); (Entity, ValueObject... Una classe che trasporta un record del db).

Altrimenti non saprei come organizzare: L'interazioe avviene in quest'ordine: Controller->BL->ORM/DAO->DATABASE

risposto 5 anni fa
larchitetto
X 0 X

Sia il DAO che la entity non devono essere degli attributi della classe BL ma dei semplici oggetti istanziati di volta in volta nei metodi della BL

 :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

Ah... mmm... Ma così la BL non mi diventa una classe che raccoglie funzioni e basta? Sempre rifacendomi all'esempio, iptotizzavo che una BL potesse rappresentare un record di una tabella, applicandoci poi dei comportamenti.

Trovandomi nel Controller, l'utente seleziona un'auto, poi la fa accelerare e fa scalare di volta in volta il carburante (questo è uno dei comportamenti); infine (la BL, accorgendosene) ne salva lo stato sul db (l'auto ha meno carburante adesso)...

Mi verrebbe più logico implementare questo esempio seguendo il mio post precedente.

Altrimenti come si potrebbe fare?

La entity non ha metodi, ma solamente i campi della tabella; il DAO accede solamente al DB e compie delle operazioni...

risposto 5 anni fa
larchitetto
X 0 X

mmm L'unica soluzione che mi viene in mente per poter implementare qualcosaa che risponda a questi requisiti

Sia il DAO che la entity non devono essere degli attributi della classe BL ma dei semplici oggetti istanziati di volta in volta nei metodi della BL

sarebbe la seguente:

  • una entity contiene un record del db (e qui ci siamo);
  • una DAO esegue delle operazioni sul DB (e anche qui ci siamo);
  • la BL ha una serie di metodi statici (qualora non avesse attributi di altra natura) che riflettono i comportamenti previsti dall'applicativo, manipolando le entity in ingresso e richiamando le relative DAO e altre classi / funzioni utili.

Quindi, ho diverse classi che smembrano sostanzialmente le varie componenti di una classe - una parte contiene i dati, un'altra descrive i comportamenti, e una terza rende tutto ciò "persistente".

Il mio esempio diventerebbe:

<?php
// implementazione Model
class AutoDAO {
  // query su DB istanziando un oggetto via Factory.
}
// la vedo come una classe static sostanzialmente... Ma potrebbe variare in base al contesto.
class AutoBL {
  public static function find_auto($targa) {
    return DAOFactory::get_instance('auto')->select_auto($targa);
  }
  public static function accelera(stdClass &$auto) {
    if ($auto->carburante > 10) {
      $auto->carburante--;
      DAOFactory::get_instance('auto')->update_carburante($auto);
      // molto grossolanamente, potrei anche:
      MyEmailService::invia_notifica_auto_accelerata($auto); // invia un'email al proprietario di quell'auto, dicendo che è stata accelerata e ha consumato tot carburante.
    }
  }
  public static function find_auto_da_corsa_di_una_scuderia(stdClass $scuderia) {
    $le_auto = DAOFactory::get_instance('auto')->select_auto_per_scuderia(scuderia);
    return $le_auto;
  }
}

// implementazione Controller, ipotizzando che l'azione desiderata sia "accelera"
$auto = AutoBL::find_auto($targaFromHTTP);
AutoBL::accelera($auto);
// return del nuovo stato per il View...

// nuova implementazione Controller, ipotizzando che l'azione desiderata sia "cerca le auto di questa scuderia"
$scuderia = DAOFactory::get_instance('scuderia')->select_scuderia($scuderiaFromHTTP);
$auto = AutoBL::find_auto_da_corsa_di_una_scuderia($scuderia);
// return del nuovo stato per il View...
?>

Potrebbe essere così?

risposto 5 anni fa
larchitetto
modificato 5 anni fa
X 0 X

Si  O0 Puoi suddividere le classi BL secondo entità principali (Prodotto, Utente, ecc.) o secondo gruppi di funzionalità correlate (Gestione utenti, gestione acquisti, gestione magazzino, gestione fatturazione, ecc.)

In pratica stai realizzando quello che spesso viene chiamato service layer:

http://martinfowler.com/eaaCatalog/serviceLayer.html

 :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

 :D :D :D

Grazie della risposta.

Si  O0 Puoi suddividere le classi BL secondo entità principali (Prodotto, Utente, ecc.) o secondo gruppi di funzionalità correlate (Gestione utenti, gestione acquisti, gestione magazzino, gestione fatturazione, ecc.)

E qui il contesto potrebbe essere più complesso, istanziando diversi oggetti VO e DAO...

Ok, a dirla tutta così mi sembra molto più pulito, logico, funzionale...

In pratica stai realizzando quello che spesso viene chiamato service layer:

http://martinfowler.com/eaaCatalog/serviceLayer.html

 :bye:

Non ho potuto leggere attentamente per via dei tempi stretti (lo farò quando tornerò a casa)...

In sostanza (correggimi se erro), il concetto di base prevede che il Service layer sia una BL (non Model poiché racchiude anche ORM/DAO che non ci interessa in questo caso) che gestisce ciò che farebbe una BL però dal punto di vista del View.

Corretto?

risposto 5 anni fa
larchitetto
X 0 X

Il Service layer è un pattern architetturale costituito da uno strato software che implementa la Business Logic e utilizza e coordina i diversi DBMS, sistemi di storage, sistemi esterni (server, web services, ecc.).

 :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

Ooook, grazie.  :)

risposto 5 anni fa
larchitetto
X 0 X

Riapro un po' questo topic per inserire la seguente: in un prodotto open source in java (penso che la logica sia la medesima a prescindere dal linguaggio) ho appena visto che le BL erano instanziate con tanto di oggetti come attributi.

Ad esempio, la classe che si occupava dei pagamenti (quindi non funzioni correlate), prendeva nel costruttore l'id del pagamento, e aveva al suo interno oggetti di tipo DTO, DAO che instanziava via via...

Tant'è che, quando il controller esauriva le sue istruzioni e rimandava il tutto al view, si aveva l'oggetto BL che rendeva l'entity con un metodo getDTO();.

Che ne pensate in merito?

Grazie.

risposto 5 anni fa
larchitetto
X 0 X

Non posso entrare nel merito della questione perché dovrei vedere esattamente il codice, comunque avere degli oggetti come attributi di una classe BL non è affatto raro.

Ad esempio uno dei casi in cui pratico tale approccio è la gestione delle dipendenze tra la BL ed i componenti/servizi. Guardate il seguente codice:

<?php
class BL {
   public function creaProdotto($prodotto) {
      $database = new MySQLDatabase();
      $database->inserisci($prodotto);
   }

   public function cancellaProdotto($prodotto) {
      $database = new MySQLDatabase();
      $database->cancella($prodotto);
   }
}
?>

Il limite di questa classe è dato dal fatto che dipende strettamente dalla classe MySQLDatabase e quindi se un domani voglio usare OracleDatabase sono costretto a modificare tutte le new MySQLDatabase presenti nel codice. La soluzione è la seguente:

<?php
class BL {
   protected $database;

   public function __construct($database) {
      $this->database = $database;
   }

   public function creaProdotto($prodotto) {
      $this->database->inserisci($prodotto);
   }

   public function cancellaProdotto($prodotto) {
      $this->database->cancella($prodotto);
   }
}
?>

In questo modo ho slegato BL dal tipo di database utilizzato. Passare il database all'atto della costruzione dell'oggetto mi garantisce che i metodi creaProdotto e cancellaProdotto richiamati successivamente abbiano tutto il necessario per funzionare. Questo discorso si può estendere a tutte quelle dipendenze che si vogliono disaccoppiare. Per la cronaca tale approccio viene chiamato dependency injection o inversion of control.

 :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

Appoggio sul dependency injector e segnalo questo componente, per implementare un DIC (dependency injector container): http://components.symfony-project.org/dependency-injection/

risposto 5 anni fa
Massimiliano Arione
X 0 X

Beh, ho postato l'esempio di un prodotto, ma ne stavo parlando in generale...

Giusto per restare in tema, esiste una sorta di manuale che spieghi in che modo realizzare una BL in base al contesto in cui dovrebbe agire?

Bene, grazie delle risposte (e ho letto il link)  :)

 :bye:

risposto 5 anni fa
larchitetto
X 0 X

Che io sappia no, principalmente per il fatto che non esiste un modo corretto in assoluto per realizzare la BL, ed infatti i diversi linguaggi e framework propongono sempre nuove soluzioni.

Forse la fonte più autorevole sulle varie architetture possibili è quella di Martin Fowler, "Catalog of Patterns of Enterprise Application Architecture":

http://martinfowler.com/eaaCatalog/

Vi segnalo anche un articolo illuminante di Carlo Pescio sui pro e contro delle architetture classiche, anche se un po' troppo interessato a sottolineare l'instabilità (innegabile) del design tradizionale.

http://www.eptacom.net/pubblicazioni/pub_it/recor1.html

 :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

Nulla è perfetto :P

Sto lavorando adesso all'integrazione di un software j2ee con php, utilizzando webservice soap. Mi vien da pensare che, organizzando un software php mediante MVC, la mia BL (in questo caso), anziché avere lo strato DAO, richiami le API per comunicare via soap e comunicare ciò che sta avvenendo...

Visto giusto?

Metto i link nei preferiti e stasera li leggo per bene; grazie.

risposto 5 anni fa
larchitetto
X 0 X

Esatto  :bye:

risposto 5 anni fa
Gianni Tomasicchio
X 0 X

Perfetto grazie.  :bye:

risposto 5 anni fa
larchitetto
X 0 X
Effettua l'accesso o registrati per rispondere a questa domanda