Chi lavora con Liferay molto probabilmente conosce il Service Builder e tutto lo strato di persistenza che esso genera.
Però forse non tutti sanno che Liferay è in grado di generare e di mettere a disposizione il medesimo strato di persistenza direttamente in pagina, con Javascript, effettuando in maniera del tutto trasparente le chiamate Ajax necessarie.
Vediamo come fare... e poi mi ringrazierete!
Innanzitutto create in Eclipse un progetto portlet; per comodità chiamatelo DirectWebRemoteTest (così coincidono gli esempi di codice sotto). Aggiungete anche il suo file XML del Service Builder; dopodichè inserite questa entità di prova:
<entity name="Car" local-service="true" remote-service="true" json-enabled="true">
<column name="carId" type="long" primary="true" />
<column name="model" type="String" />
<column name="cabrio" type="boolean" />
<column name="hp" type="int" />
<order by="asc">
<order-column name="model" />
<order-column name="cabrio" />
<order-column name="hp" />
</order>
</entity>
Non entro nel dettaglio della entità, che dovrebbe risultare ovvio, ma segnalo solamente i 2 attributi in grassetto che devono essere impostati a true.
A questo punto lanciamo uno dopo l'altro i seguenti 2 target dello script Ant del progetto:
- build-service;
- build-wsdd.
Una volta terminata l'esecuzione dei 2 target, è già stata creata gran parte di quello che ci serve; prima di procedere dobbiamo decidere quali metodi devono essere accessibili via Javascript in pagina. Supponiamo quindi di voler esporre i metodi:
- getCar(long carId), che restituisce la singola entità;
- getCars(), che restituisce tutte le entità.
Apriamo quindi la classe CarServiceImplche, essendo appena stata generata, sarà vuota.
Attenzione: la classe da usare è CarServiceImpl e non CarLocalServiceImpl.
Una volta aperta la classe, aggiungiamo i 2 metodi da esporre come di seguito:
public CarSoap getCar(long carId) throws SystemException {
return CarSoap.toSoapModel(carPersistence.fetchByPrimaryKey(carId));
}
public CarSoap[] getCars() throws SystemException {
return CarSoap.toSoapModels(carPersistence.findAll());
}
Come sono strutturati questi metodi?
Innanzitutto notiamo l'utilizzo della classe CarSoap, generata dal Service Builder, che di fatto è un wrapper del model Car. Dopodichè il contenuto di ciascun metodo non è altro che una normale chiamata allo strato di persistenza, wrappata però dalla classe CarSoap che converte gli oggetti restituiti,in modo da avere in pagina qualcosa di utilizzabile.
A questo punto lanciamo nuovamente uno dopo l'altro i 2 target dello script Ant del progetto:
- build-service;
- build-wsdd.
Una volta terminata l'esecuzione dei 2 target, apriamo il file docroot/js/service.js; dovreste avere una cosa del genere:
Liferay.Service.register("Liferay.Service.dwr", "it.dvel.dwr.portlet.service",
"DirectWebRemoteTest-portlet");
Liferay.Service.registerClass(
// Liferay.Service.dwr rappresenta una sorta di namespace
// "Car" è il nome dell'oggetto Javascript
Liferay.Service.dwr, "Car",
{
getCar: true, // questo è il nome del primo metodo
getCars: true // questo è il nome del secondo metodo
}
);
La parte cruciale è tutto quanto segue la prima riga, ossia la registrazione della classe Car ed il censimento dei metodi che abbiamo esposto in precedenza. Questo file non deve essere manipolato dallo sviluppatore, vi ho solo mostrato come era stato modificato dal Service Builder.
A questo punto, chiamate un vostro amico e fategli inserire qualche record nel db...
Una volta inserito qualche record in tabella, possiamo procedere con l'ultimo step: il codice da inserire in pagina.
<script type="text/javascript" src="/DirectWebRemoteTest-portlet/js/service.js"></script>
<aui:fieldset label="Single car">
<div id="car"></div>
</aui:fieldset>
<aui:fieldset label="All cars">
<div id="cars"></div>
</aui:fieldset>
<aui:script>
AUI().ready('aui-base', function(A) {
var carId = /* mettete un qualche valore di chiave primaria */;
Liferay.Service.dwr.Car.getCar({
carId: carId
},
function(car) {
A.one("#car").html(car.model);
});
Liferay.Service.dwr.Car.getCars({
},
function(cars) {
var desc = '';
for(i=0; i < cars.length; i++)
desc += cars[i].model + "<br />";
A.one("#cars").html(desc);
});
});
</aui:script>
La prima cosa fondamentale è inserire il riferimento al file service.js che abbiamo visto prima; Liferay non lo inserisce da solo, quindi occhio a non dimenticarlo.
Dopodichè ho inserito un pò di codice HTML per posizionare alcuni <div> che verranno riempiti mediante Alloy; niente di che, fate solo caso al valore di id usato.
Infine, il codice vero e proprio, al caricamento della pagina invoco prima un metodo (che mi restituisce un unico record) poi l'altro (che li restituisce tutti); oh è solo un esempio!
Qual'è la sintassi esatta dell'invocazione? E' tutto quanto contenuto nel blocco Liferay.Service.registerClass del file service.js, ossia il namespace ed il nome dell'oggetto Javascript, seguito dal nome del metodo:
Liferay.Service.dwr.Car.getCar(<A>, <B>);
Ogni metodo generato da Liferay ed utilizzabile in pagina ha 2 parametri.
Il primo parametro rappresenta un oggetto JSON contenente tutti i parametri; nel caso non ci siano parametri va comunque passato un oggetto JSON vuoto (ossia {}).
Il secondo parametro è la funzione di callback che viene invocata al termine dell'esecuzione del metodo dello strato di persistenza (ossia quello implementato nella classeCarServiceImpl); il parametro della funzione di callback (il nome è a scelta dello sviluppatore) è l'oggetto restituito dal metodonella classeCarServiceImpl. Quindi per il metodo getCar il parametro è di tipo CarSoap; mentre per il metodo getCars il parametro è di tipo CarSoap[].
A questo punto potete farne quello che volete.
Enjoy!