[Helma-user] helma summer camp
Anton Pirker
helma at gmiatlich.net
Wed Jul 19 01:04:25 CEST 2006
Hi List!
Here are my notes from day two and day three of the Helma Summer Camp.
Thank's again to Michi for organizing this workshop!
Hopefully the notes will provide a little help to Helma-Newbies.
x) HobObject get persist when they are added to a persisten collection
of HopObjects. (or with a call of the .persist() method of the object)
x) Changes to an HopObject are stored in the database at the end of an
request.
x) always use forms with method="post"
use req.isPost() to check if a form was submitted. (so you don't need to
know the name of the submit-button)
x) javascript syntax:
param.action || "main"
-> if param.action exists (is not NULL or "") take param.action, else
take "main"
param.action && param.action.toUpperCase()
-> if param.action exists (is not NULL or "") run the
param.action.toUpperCase(), else break
x) very handy: the generic list_macro() in HopObject-Prototype:
function list_macro(param) {
var coll = param.collection ? this[param.collection] : then;
for(var i=0; i<coll.count(); i++) {
var obj = coll.get(i);
obj.renderSkin(param.skin || "defaulSkin");
}
}
so you can make one macro wich displays all of your lists. you can
implement pagination an other list-specific stuff here.
you can call it like: <% this.list collection="users" skin="listView" %>
x) sometimes it is handy to map the Root-Prototype to the DB. this is
useful if you store settings in Root.
x) _parent = organization.bla, root.bla
-> the second parent is a fall back, if there is no organization, than
the prototype is mapped to root.
x) M:N: checking for subscription
The M:N-Setup:
--subscription/type.prototype--
user = object(User)
user.local = user_f_id
user.foreign = user_id
magazine = object(Magazine)
magazine.local = magazine_f_id
magazine.foreign = magazine_id
--end--
--user/type.prototype--
subscriptions = collection(Subscription)
subscriptions.local = user_id
subscriptions.foreign = user_f_id
subscriptions.accessname = magazine_f_id
--end--
--magazine/type.prototype--
subscriptions = collection(Subscription)
subscriptions.local = magazine_id
subscriptions.foreign = magazine_f_id
subscriptions.accessname = user_f_id
--end--
The trick is to use the foreign-key of the other table as the accessname
for the collection.
So you don't need to loop through all subscriptions of a user to see if
she subscribes to a specific magazine.
You just need to call:
theUser.subscriptions.get(theMagazine._id.toString());
The .toString() is importand, because if you call .get(n) where n is an
integer, than the n-th element will be returned.
If you call .get("n") than the element with the accessname "n" will be
returned.
x) Don't put persistent Objects into the Session!
If you really need this just put the Object-ID into the Session and get
the Object with the stored ID
every time you need it.
x) onRequest() function of an HopObject is always executed before the
action.
onRequest() in the HobObject-Prototype is a good place for
security-stuff. For example checking for logged in user like this:
function onRequest() {
if(!session.user && (req.action != "login" || req.action !=
"register")) {
res.redirect(root.href("login"));
}
}
the check for req.action is necessary because if you no user is logged
in than you will be redirected
to the login and before you can login the onRequest ist called again.
x) HobObject-Prototypes have static methods like .getById(). So you
don't need a collection to get a Object.
For example: var invoice = Invoice.getById("20060815")
x) Group by:
A collection with a .group definition is a collection of collections.
Every group is represented through a collection.
Lets assume we have a table with a date (in format yyyy-mm-dd), and we
group it by this date in a collection
called "allElements"
Now we can call
var todaysElements = allElements.get("2006-07-19");
to get all elements of the group with todays date. If there aren't any
elements in that group todaysElements
will be NULL
x) nice separation of controller and model [1] made easy in helma:
(this also describes how nice helma actions could look like)
--actions.js--
function register_action() {
if(req.isPost()) {
try {
var u = this.doRegister(req.data);
session.login(u);
redirect(root.href());
} catch(err) {
res.message = err.message;
}
}
renderSkin("register");
}
--end--
--function.js--
function doRegister(dat) {
//register the user with the infos stored in 'dat'.
if(problem)
throw new Error("there was an quite unexpected error!");
}
--end--
never do the data-manipulation stuff in the action (the controller).
all that stuff belongs in extra functions (to the model).
the session handling should be in the controller. so you can use the
doRegister() function
for bulk-registration without problems.
[1] http://en.wikipedia.org/wiki/Model_View_Controller
http://de.wikipedia.org/wiki/Model_View_Controller
x) Every macro has prefix and suffix.
<% this.questions prefix="<li>" suffix="/li>" %>
x) Javascript tips:
format(str): \n -> <br />
stripTags(str): removes the html-tags from str.
x) macro handler:
one can call also the parent of the current object.
<% parentname.dateOfBirth %>
x) pushing parameters to skins only if it is absolutly neccesary!
for example when using alternate background-colors for lists:
for(var i=0; elements.count(); i++) {
var p = new Object();
p.className = "element" + (i%2);
var obj = elements.get(i);
obj.renderSkin("listElement", p);
}
--the skin--
<div class="<% param.className %>"><% this.name %></div>
--end--
--the css stylesheet--
.element0 {
background-color: black;
}
.element1 {
background-color: red;
}
--end--
x) res.handlers is quite powerful!
lets assume we are in an order. an order has an customer.
to access this customer from the macro we create a handler for it:
res.handlers.customer = this.customer
this.renderSkin("detailView");
--the skin--
<% this.date %> - <% this.orderNum %><br />
<% customer.name %><br />
<% customer.address %>
...
--end--
this makes all this
function customerName_macro() {
return this.customer.name;
}
stuff obsolete!
x) again group-by:
--type.properties--
foo.group = answer
foo.group.prototype = Answer
--end--
Scripted Prototypes! I can add an Answer Prototype and it can have
own actions and functions like every other prototype.
x) Perfomance hint: Indices on foreign keys!
to, that's it!
now you can start over, falling in love with Helma!
Anton
More information about the Helma-user
mailing list