In questo progetto-palestra, creato per passione, per studio e per esigenze lavorative, l'architettura MVC è stata creata da me senza l'utilizzo di framework
già predisposti, la logica e la struttura credo siano molto ordinate, l'idea nasce per semplificare al massimo le numerose chiamate Ajax in un qualsiasi software di gestione dei dati lato web.
Tutte le azioni vengono inviate ad indice, in questa pagina ci sono due righe di codice,l'istanziamento della classe controller ed il metodo "invoke", l'indice riceve l'azione(variabile metodo=) e invoca il controller, quest'ultimo
controlla che la variabile metodo(metodo=nomeMetodo) sia valorizzata e successivamente, interagendo con il model, controlla tutte le classi presenti nella cartella model e tutti i metodi delle relative classi.
A questo punto ricerca un metodo che ha lo stesso nome passato alla varibile metodo, appena trova il metodo, dinamicamente istanzia la relativa classe ed invoca il corrispettivo metodo.
Tutti i cicli di controllo sono eseguiti con la TOKEN DI ZEND ENGINE(T_CLASS = classe), con il Recursive iterator iterator e con la ReflectionClass.
Per logica di ordine e di controllo, ho volutamente impedito l'override di metodi, poichè reputo che un progetto ordinato debba avere un metodo UNIVOCO e non doppio, o se doppio deve rispettare le regole della OOP quindi l'utilizzo delle interfacce.
In questo modo tutte le chiamate effettuate da Ajax o da un tasto submit ad indice?metodo=mioMetodo avranno necessariamente un metodo univoco nella cartella model, il debug è immediato.
Non vi sono problemi per l'ereditarietà tra classi, poichè il ciclo, grazie alla ReflectionClass ed i suoi metodi, ricava tutti i metodi corrispettivi delle classi, quindi se :
Class B extends A , il ciclo ricava i metodi EFFETTIVI di A e di B e poi controlla che non vi siano metodi doppi, questo è controllato con la ReflectionClass poichè la get_method_class()
ci informa anche dei metodi ereditati, quindi se avessi usato la get_method_class avrei ricavato i metodi di A ma per B avrei il totale dei metodi ereditati(anche quelli di A), poichè B eredita i metodi di A.
Per controllare i metodi doppi, si cicla quindi con la ReflectionClass e si tengono in considerazione i metodi EFFETTIVI delle classi.
Inoltre è permesso anche l'utilizzo delle interfacce, se vogliano appunto usare metodi con lo stesso nome in classi diverse.
Un'interfaccia ha determinati metodi, supponiano interface sopravvivenza{public method mangiare();public method dormire()}.
A questo punto tutte le classi che implementano l'interfaccia "sopravvivenza" devono necessariamente avere sia il metodo "mangiare" che il metodo "dormire"(regola delle interfacce nella OOP), e quindi potrebbero risultare doppi,
poichè se Class B implements sopravvivenza e class C implements sopravvivenza, ovviamente B e C devono avere i metodi dell'interfaccia per regole di OOP.
In questo caso non possiamo ricavare dinamicamente la classe poichè il parse non può sapere se vogliamo istanziare B o C quindi in questo caso basta invocare :
indice?metodo=mangiare&classe=B oppure
indice?metodo=dormire&classe=A etc...etc..
In questo modo il parse dinamicamente cerca il metodo mangiare o dormire che deve
necessariamente appartenere ad un'interfaccia e istanzia la relativa classe e ne invoca il metodo.
Ovviamente il metodo, passato dalla variabile "metodo=" deve appartenere ad un'interfaccia, altrimenti se fosse un normale metodo di una classe sarebbe univoco ed invocato con la normale dicitura:
indice?metodo=dormire , dove non vi è necessità di passare il nome della classe, perchè il metodo sarebbe univoco ed il nome della classe viene ricavato dalla libreria, ci sarà una sola classe che al suo
interno presenta il metodo, ad esempio, "dormire".
I costruttori vengono recuperati nel metodo magico
__construct e vengono passati da Ajax o dal PHP, poichè questa struttura permette anche di invocare metodi ed istanziare classi direttamente dal PHP, oltre che la normale
invocazione classica del PHP cioè
new class()->nomeMetodo(); nessuno ci impedisce di istanziare una classe con la normale scrittura del PHP ma per completezza ho creato anche una metodologia di invocazione dal server
ed usare come passaggio di costruttori un array di valori.
Ad esempio se dal PHP, e quindi non dal client, volessi istanziare una classe "persona" ed invocare un suo metodo "camminare" passandogli dei costruttori, mi basterà scrivere
new controller()->invocaMetodo( $classeServer, $nomeMetodo, $arrayCostruttori) ,
$arrayCostruttori è un array di costruttori quindi in questo caso, supponendo che camminare sia un metodo univoco nel model, scriveremo
new controller()->invocaMetodo("", "camminare", array("velocita=>$velocita", "distanza"=>$distanza,etc...)),
velocita e distanza, cioè le chiavi dell'array, sono i nostri costruttori,
$this->velocita, $this->distanza
recuperati nel metodo magico
__construct della classe persona, ovviamente se camminare appartenesse a più classi, lo dovrei interfacciare in un'interfaccia e passerei anche il nome della classe al metodo invocaMetodo.
Nella cartella model viene tutto eseguito con la spl_autoload_register quindi non c'è bisogno di includere file poichè vengono inclusi dinamicamente e la prima classe del file deve avere lo stesso nome del file, quindi:
variazione avrà la prima class variazione{} con tutti i suoi relativi metodi, il file datiRecapito avrà la prima class datiRecapito{} etc..etc...
Ma avendo introdotto anche un'invocazione da server PHP, come abbiamo detto prima, possiamo anche includere in uno stesso file più di una classe(chi conosce l'splAutoloadRegister sa bene che di solito un file = una classe ma con la libreria
e con la metodologia di invocazione che è stata creata, possiamo anche scrivere 20 classi in un file l'importante è che la prima si chiami come il file esclusa l'estensione),
possiamo accedere alla seconda, terza classe nel file con l'invocazione del controller()->invocaMetodo dal PHP mentre se accediamo da Ajax non vi è alcun problema basterà scrivere
indice?metodo=mioMetodo se metodo univoco o passare anche la classe se metodo doppio.
Un'altra cosa importante è che il controller include in require_once tutti i file che gli servono dopo aver chiamato la get_included_file(), quindi tutti i file che servono allo sviluppatore nella cartella model vengono inclusi nel controller(in tutti i file del model non vi è alcun include o require_once), in questa funzione,
il controller vede quali file sono già inclusi e se gli servono dei file li include, altrimenti, se sono già inclusi, bypassa e va oltre, ovviamente parliamo dei file che servono al programmatore ma che non presentano classi all'interno del model, ad esempio un file di funzioni.
Il controller è il vigile dell'architettura, include i file che non presentano classi(perchè se vi fossero classi ci penserebbe l'splAutoloadRegister, ad esempio, come detto sopra, potrebbe includere un file di funzioni o un file di configurazione), effettua tutti i controlli
scannerizza tutta la cartella model e ci informa su quali classi sono presenti, quali metodi...etc...etc... e dirotta il parse all'interno del model, è nel model che vi è il cuore dell'applicazione.
Questa struttura ci permette una comodità pazzesca perchè appena il parse viene dirottato all'interno di un metodo di una classe possiamo invocare qualsiasi metodo, istanziare qualsiasi classe, invocare qualsiasi funzione
poichè il controller ci ha dato tutta la strumentazione di cui abbiamo bisogno.
Nel model non vi è neanche un include neanche un require_once ma tutto è accessibile in maniera dinamica grazie al lavoro fatto dal controller.
Il Model recupera i valori passati dal client e si interfaccia con il DB e genera l'aggiornamento e tutte le relative funzioni di intefacciamento.
Nel progetto ci sono anche dei submit, per alcune funzioni di upload o download, ma in generale tutte le chiamate vengono invocate dinamicamente tramite Ajax, il Model effettua le operazioni e risponde al Client Ajax che a sua volta aggiorna o blocca l'azione
inserita dall'utente.
E' incluso Bootstrap e tutte le dialog sono create con la modal di bootstrap in load modal.
Il progetto è facilmente debuggabile, sia il lato client-Ajax-Javascript sia il lato server.
Se ci dovessero essere dei problemi di funzionamento, tramite il nome dell'azione si arriva facilmente ad interfacciarsi con la parte di codice nel Model che si occupa di quella determinata azione.
Questa logica è stata fatta apposta, perchè lavorando in un progetto molto grande mi scontro tutti i giorni con problemi di analisi e debug del codice, non essendoci una struttura fissa e ordinata, mentre in questo
caso, nel MVC-WebLog ho proprio voluto creare una struttura solida e facilmente gestibile e debuggabile, lavorando con classi ed interfacce.
È come se fosse un insieme di piccoli servizi/metodi ed ognuno di questi metodi si occupa di una determinata azione dove la colonna portante è rappresentata dal controller.
PHP è un grande linguaggio e ci permette di fare sviluppi che altri tipi di linguaggio non permetterebbero, ha una versatilità e dinamicità che altri linguaggi non hanno(provare per credere), però ha anche un difetto, e cioè permette di creare codice anche disordinato
se il programmatore non segue delle logiche ben precise e strutturate, FISSE.Il problema è di chi programma e non del linguaggio, se io sono disordinato creerò codice disordinato e non strutturato all'interno di una logica ben definita e questo imporrà a chi
gestirà il progetto dopo di me, una grande fatica di analisi e debug.
Ogni file all'interno del progetto ha la sua estensione, i file HTML sono tutti file con estensione HTML, che viene generato dal PHP con una classe di gestione template e vengono passati i parametri dal PHP all'html tramite array, i file javascript hanno tutti estensione
.JS e sono tutti all'interno dell'html(non amo fare injection di javascript dal PHP, se non altamente necessario, come non amo mettere codice html all'interno del PHP, amo dividere i file per la loro natura e tenere ordinato il progetto).
Nel model tutti gli interfacciamenti con il DB SQL sono creati con prepared statment e ovviamente cercando più sicurezza possibile, sperando che non mi sia sfuggito qualcosa.
Quindi tutti i file PHP che invece si interfacciano con il DB ed hanno logiche di programmazione, if, switch statment, richiamo funzioni e tutto ciò che riguarda il DB e le logiche di sviluppo sono all'interno del MODEL.
Facciamo un esempio pratico con un metodo univoco, nella cartella model ho il file variazioni con la prima classe del file class variazioni e nello specifico questa scrittura :
Adesso vogliamo accedere da Ajax, quindi dopo aver inserito il nome e cognome da delle normalissime input text html, a questo metodo e printare il nome e cognome, scriverò
al click del bottone dall'html, per esempio bottone "invia dati" farò una funzione javascript ed un ajax in questo modo :
oppure usando un normale link da browser(ovviamente usando un $_REQUEST nel costruttore) basterà scrivere:
indice?metodo=scriviNomeVariazione&nome=Andrea&cognome=Rossi
Provare a digitare questo url nel browser e provare a cambiare i valori o mettere un metodo=prova&nome=pippo&cognome=topolino:
https://fractalcosmo.com/MVC/indice?metodo=scriviNomeVariazione&nome=Andrea&cognome=Rossi
Oppure inserire dei valori qui, il quale rispecchia l'esempio scritto sopra :
Ovviamente possiamo lavorare con interfacce, in maniera molto ordinata, l'MvcWebLog non è altro che una struttura
ordinata di lavoro a classi in OOP con PHP dove tutto è studiato per gestire al meglio ed in maniera più dinamica e comoda possibile un sito
di gestione dei dati.
Dal link che ho scritto sopra si può provare ad inserire anche la variabile classe, ovviamente la libreria ci avvertirà di un errore,
perchè inserendo la classe ed il metodo vuol dire che il metodo deve appartenere ad un'interfaccia e la classe deve implementare quell'interfaccia,vengono eseguiti tutti i controlli del caso.
Facciamo adesso un esempio con le interfacce, creiamo un file nominato interfacce, lo mettiamo nella cartella model e lo facciamo includere alla struttura dal controller, in questo file inseriremo solamente le interfacce
che vogliamo usare nelle classi, per il momento scriviamo un'ipotetica interfaccia persona,
interface persona così definita:
Mentre nel file dove è presente la classe variazioni quindi il file variazioni, sotto la classe variazioni, NB possiamo metterlo in qualsiasi file vogliamo, non cambierebbe nulla,
proprio perchè il controller e il model, insieme, scannerizzano la cartella model quindi non c'è un ordine prestabilito, il controller
include in primis i file che non presentano classi(questo è stato fatto apposta per non appesantire lo scan e i file che non presentano classi sono esclusi dallo scan), quali ad esempio
le funzioni(per ordine è meglio creare un file apposito per le interfacce ma si potrebbero anche inserire in un
file dove sono presenti classi, però è meglio includere subito le interfacce perchè quando il parse scannerizza le classi se l'interfaccia viene inclusa dopo che il parse scannerizza la classe non troverebbe l'interfaccia),
qui rientriamo in un ordine di progetto, e cioè, un file dove metteremo tutte le nostre interfacce e lo facciamo includere al controller, a questo punto vengono inclusi subito i file primari dal controller ed in ultimo viene effettuato
lo scan ed ovviamente la classe può implementare l'interfaccia senza problemi poichè è già stata inclusa, quindi dopo aver incluso questi file il parse scannerizza la cartella model ed include in splAutoLoad tutti i file dove sono presenti classi.
NB il controller scannerizza anche le interfacce, il file interfacce lo facciamo includere dal controller ma non lo escludiamo dallo scan.
Inseriamo quindi sotto la classe variazione queste classi, due delle quali implementano l'interface persona così definite:
Abbiamo creato tre classi, la prima estende la classe variazioni(esempio di prima con nome e cognome) ed abbiamo inserito un metodo, le altre due
classi implementano l'interfaccia persona, ovviamente le classi
Student e
lavoratore devono implementare i metodi dell'interfaccia altrimenti
PHP andrebbe in errore, se implementassimo l'interfaccia senza inserire i metodi nella classe PHP andrebbe in fatal error, proprio per definizione di interfaccia nella OOP.
Negli screen ci sono due funzioni chiamate definisciCostruttori, questa funzione permette di definire i costruttori sia se la chiamata viene effettuata dal PHP sia se venisse
effettuata da Ajax, i costruttori per il momento sono due e sono "nome" e "cognome", vediamo che ci sono tre metodi oltre che il metodo magico __construct.
Il primo metodo
getName printa il nome passandogli i costruttori che vengono ridefiniti nel metodo
setName mentre il metodo
invoca è quello completo
che setta i costruttori ed invoca la getName per il print.
Adesso la libreria ci permette di accedere a queste classi e questi metodi sia da Ajax che dal Php ma ovviamente possiamo tranquillamente scriverlo tramite url,
è importante sapere che nella libreria sono presenti anche funzioni che permettono di accedere ad un metodo o solo in chiamata POST o solo in GET o solo in AJAX proprio per
avere maggiore sicurezza, ma non faccio qui l'esempio.
Andiamo ad accedere al metodo invoca tramite url sia nella classe Student che lavoratore, esempio per la classe Student:
https://fractalcosmo.com/MVC/indice?metodo=invoca&classe=Student&nome=Andrea&cognome=Rossi
Esempio per la classe lavoratore :
https://fractalcosmo.com/MVC/indice?metodo=invoca&classe=lavoratore&nome=Luca&cognome=Bianchi
Adesso però proviamo a mettere come classe la classe variazioniAggiornate che estende la class variazioni, ovviamente dobbiamo avere un errore
perchè la classe variazioniAggiornate non implementa interfacce ed inoltre il metodo invoca non è presente nella class variazioniAggiornate.
Ed Infatti, ecco cosa ci dice la libreria.
https://fractalcosmo.com/MVC/indice?metodo=invoca&classe=variazioniAggiornate&nome=Luca&cognome=Bianchi
Oppure :
https://fractalcosmo.com/MVC/indice?metodo=invoca&classe=variazioni&nome=Luca&cognome=Bianchi
Oppure chiamiamo il metodo invoca senza però passare la classe :
https://fractalcosmo.com/MVC/indice?metodo=invoca&nome=Luca&cognome=Bianchi
Ecco l'esempio con l'uso delle interfacce accedente da Ajax dopo la compilazione dei dati, come l'esempio sopra a metodo univoco:
Come ho detto nelle righe sopra possiamo accedere da qualsiasi punto di un file del model alla classe lavoratore(passando sempre ovviamente dal controller) anche se non è la prima classe del file,
nota bene se io scrivessi
new lavoratore(), l'splAutoloadRegister in automatico include lavoratore ma non lo trova poichè lavoratore è l'ultima classe del file variazioni, di conseguenza
se non avessi creato un metodo che mi permettesse l'istanziamento della classe e l'invocazione di un metodo avrei il limite di una classe per singolo file, ma a noi i limiti non piacciono,
ed adesso da qualsiasi file del model mi basterà scrivere
$arrayCostruttori = array('nome'=>'Ciccio','cognome'=>'Pasticcio');
$controller = new controller();
$controller->invocaMetodo('lavoratore', 'invoca', $arrayCostruttori);
Il parse printerà
Nome : Ciccio Cognome : Pasticcio e sei un lavoratore
Proprio grazie all'utilizzo della funzione definisciCostruttori nel metodo magico __contruct, non sto qui a spiegare la logica, accenno solamente che una volta che il parse ha ricavato classe e metodo
ed ha controllato che effettivamente sia tutto nella corretta struttura imposta dall'MVC(cioè in questo caso il metodo "invoca" appartiene ad un'interfaccia e lavoratore implementa l'interfaccia etc..etc..)
istanzia i costruttori usando la
newInstanceArgs della reflectionClass e li passa come $array di costruttori alla classe.
Possiamo accedere a questo punto sia da server che da Ajax.
Facciamo un altro esempio ampliando la classe Student, inserendo un array come indirizzo, al costruttore andiamo quindi a passare un array di array a due dimensioni per la chiave
indirizzo contenente via e citta, come nello screen sottostante:
Se volessimo accedere al metodo
indirizzoStudent da un qualsiasi file php anche esterno dalla cartella model, basterà includere il controller, ci basterà scrivere, screen sottostante :
Come vediamo dal primo screen nel costruttore impostiamo un definisciCostruttori con la chiave dell'array, in questo caso "indirizzo", all'interno del metodo scriviamo
$this->indirizzo['citta'] e $this->indirizzo['via'], ovviamente questa è solamente una metodologia, mi permetto di dire abbastanza comoda ma nessuno ci impedisce di usare altre metodologie.
Ovviamente come si vede nello screen 2 non passiamo la classe Student perchè il metodo indirizzoStudent è univoco non vi sono altri metodi con lo stesso nome.
Per accedere ad esempio da url cliccare il link sottostante impostando sempre l'array indirizzo via e città:
https://fractalcosmo.com/MVC/indice?metodo=indirizzoStudent&nome=Andrea&cognome=Rossi&indirizzo[citta]=Roma&indirizzo[via]=Tiburtina
Possiamo anche farci ritornare i valori e poi maneggiarli successivamente, si rispettano le piene regole della OOP.
Se invece volessimo accedere al metodo variazioneDatiEstesa, degli screen precedenti che è sempre un metodo univoco, basterà digitare o invocare:
https://fractalcosmo.com/MVC/indice?metodo=variazioneDatiEstesa
Oppure usare il meotodo invocaMetodo senza passare la classe, essendo variazioneDatiEstesa un metodo univoco.
Da questi esempi credo che si noti bene che in questa libreria, composta da 3 file,
indice inserito nella root principale del progetto,
controller inserito nella cartella controller del progetto e
model inserito nella cartella model del progetto, permette, innanzi tutto,
di avere sott'occhio tutta la struttura della cartella model, di poter lavorare con le classi le interfacce, rispettando le regole di OOP e poter accedere
a metodi delle classi sia da Ajax che dal PHP, completa dinamicità all'interno della cartella model, piena gestione di tutta la cartella model.
Ordine di struttura e debug immediato.
Inoltre vi è un metodo che ci informa su tutta la struttura della cartella model, quante classi ci sono, se ci sono delle classi che implementano delle interfacce,
quali sono i metodi delle classi, se ci sono dei metodi doppi(devono essere solamente i metodi delle interfacce e il metodo magico construct per avere ordine),
se ci sono delle interfacce, quali sono i metodi delle interfacce etc..etc..etc...
È una libreria creata per avere stabilità e pieno controllo su tutto il progetto lavorando in OOP.
Cliccando il link sottostante veniamo informati proprio sulla struttura del model presente in queste pagine di esempio, ovviamente
ci sono anche i metodi usati per gli esempi di w2ui ai quali si può accedere dal menù in alto.
https://fractalcosmo.com/MVC/indice?metodo=debugStruttura
Fin'ora ho parlato di invocazioni a metodi di Classi, utilizzo delle interfacce e struttura della OOP, ma la libreria permette anche di invocare una funzione
definita di un file, in un qualsiasi file, dopo che questo viene incluso in require_once dal controller.
La funzione può essere invocata da Ajax con una semplice composizione di url utilizzando la scrittura:
indice?funzione=nomeFunzione
Ad oggi il parse, ad esempio in queste pagine di esempio, per dirottare la chiamata all'interno di un metodo della cartella model, quindi eseguire lo scan della struttura,
effettuare i controlli etc... impiega, dal mio computer con una normale connessione, circa 60/80 millisecondi, per esempio se dal menù aprite la modale bootstrap,
Esempi W2ui 1.4->Modal W2Grid V1.4.3 ADV-SEARCH-WINDOW WORK e eseguite un autocompletamento dei comuni, cliccate su un record e digitate ad esempio una lettera,
quell'autocompletamento viene dirottato all'interno del metodo "ricercaComune", quindi all'interno di una classe, la classe
viewEsempi, come si può ben vedere
è molto veloce, proprio perchè ho inserito una classe myRecursiveFilterIterator, nel controller, dove vado ad escludere quei file che non presentano classi, per velocizzare lo scan,
ripeto che lo scan viene effettuato con la
token_get_all di PHP, ovviamente per sapere se abbiamo a che fare con una classe dobbiamo scannerizzare i file della cartella model e recuperare il
token T_CLASS della classe o T_INTERFACE delle interfacce, quindi dato che a noi interessa dirottare dentro un metodo di una classe, non ha senso scannerizzare anche i file
che non presentano classi, questo aumenta di molto le prestazioni, anche se parliamo sempre di millisecondi.
In un progetto dove ad esempio abbiamo tanti file con tante classi potrebbe essere utile eseguire alcune chiamate a delle funzioni piuttosto che a dei metodi, per esempio in un progetto
di grandi dimensioni il parse per dirottare all'interno di un metodo potrebbe impiegare circa 150 millisecondi mentre per dirottare all'interno di una funzione impiegherebbe circa 60/80millisecondi, questo
è logico poichè non deve effettuare i controlli della struttura, ma tramite la funzione di php
function_exists, che ritorna le funzioni definite, cerca la funzione e la invoca dinamicamente.
In alcuni casi potrebbe essere più utile, ad esempio per un autocompletamento, invocare una funzione piuttosto che un metodo di una classe, ovviamente sempre in sicurezza di chiamata.
Quindi per esempio nel file funzioni incluso dal controller vado a scrivere questa funzione :
Alla quale si accede da un normalissimo url di chiamata o inserimento dati da input html, con Ajax, come l'esempio sopra :
https://fractalcosmo.com/MVC/indice?funzione=invocazioneScriviNome&nome=Pippo&cognome=Paperopoli
Per qualsiasi info o per i file non esitate a contattarmi.