Thursday, March 25, 2010

A NakedPhp walktrough

Today I'll present a walktrough in the NakedPhp example application, to show the direct manipulation of objects it provides, with features like calling methods and editing of Entity objects.
The example application manages a Domain Model that contains places (shops, sightseeings, and so on) and cities. Places have a directional many-to-one relationships with cities.
I will upload a screenshot for every step.The graphic is very basic, but it is not a responsibility of the framework. Every application can write its own layout, complete with style sheets, and assemble the content pieces differently.

Supposing we had set up correctly the example application, to start working we have to load the naked-php controller default action. In my Apache configuration I set up the public/ directory as the DocumentRoot of the example virtual host. There are really no differences with others Zend Framework applications.
The starting page shows in the header the two declared services (managed as singleton-like instances), an empty session and a context bar, which we can ignore for now. Classes of a Domain Model are divided in two parts: Services (always available and never serialized) and Entities (managed in the session bar and passed around). Factories and Repositories go under the Services umbrella, while ValueObjects for now are not supported because the underlying object-relational mapper does not support them yet.
Clicking on the PlaceFactory service in the header will send us to the object Example_Model_PlaceFactory, with a list of the available actions (exposed methods, not filtered for now):
If we click on createPlace, a new Example_Model_Place object will be created by this factory method. This methods has no parameters and creates an instance with default values. We are redirected to that instance, which now is in the session bar:
If we click again on PlaceFactory and choose the createCity action, a form will be shown since this method requires one parameter (the name of the city):
We insert London as the name and submit the form. A new object is put in the session bar and we are redirected to it as it is the result returned by the method. The session bar keeps object in the PHP session: we are not touching the database. This means that entities should be serializable, and this leaves us free to use Plain Old PHP Objects that do not extend any framework class. The only requirement is that the phpdoc annotations and a few other ones are present to determine the type of parameters and method return values.
After the creation and the redirect, a list of the properties of the Example_Model_City object is shown:
Note that the object different specifications (classes) are distinguished by different icons.
If we go back to the Default Name Place by clicking on it and follow the link on the pencil, an editing form is generated basing on the setters available on the object.
Note that the context bar now contains one more link (other than Index). When a form for selecting method parameters or objects fields is shown, the context is conserved. If we don't like the objects we have in the session, we can go around looking for better ones, or create them. In the example, we want to create the Eiffel Tower Place and since it is not in London, we need a Paris City object. So even if we are on the form, we simply go on the PlaceFactory service and select the createCity action again:
The context has grown again (it can be reset by going to the index if someone screws up.) Then we submit the form and we are redirected to the last action we were calling, the editing of the entity 1:
We can now select Paris for the city field and change the name to Eiffel Tower, then submit the form. Unfortunately collections are not supported and we will get an error, but the process works well until it encounter the events collection (not yet implemented). If we simply reload the index page and click on the newly renamed Eiffel Tower object, we'll see it has been correctly edited.
We have worked only in-memory and not had any interaction with the database yet. This post is getting long so I will show you tomorrow the second part of the walktrough, where we save these entities and retrieve them with a method that is hidden or shown automatically basing on the current state.


bonndan said...

A very interesting concept, and a nice implementation in PHP. I have three remarks you might want to respond to:

1) Security
If you are working with ACLs and have to santize user input, how easy can these be regarded by NakedPHP?

2) As far as I understand, methods like hideXYZ() are used to hide or expose functions (formerly known as actions). I would prefer to use phpdoc tags to explictly expose methods, since a whitelist is safer and the tags are used by doctrine anyway.

3) Design. Is design/appearance completely decoupled from logic? How limited is it, apart from styling with CSS? In many projects designers have to do the UI, and customers have clear demands to it. It were great if nakedPHP could be used in commercial RAD.

Tanti saluti


Giorgio said...

For all three points I'd remind you this is alpha/unstable software. :) However the intention for 1) and 2) is to port the design choices made by Naked Objects, so:
1) ACL on the single methods, plus a optional Facet over methods where the developer wants a finer control; e.g. boolean allowMyMethod($someArguments) to check if the current user can call myMethod().
2) There is also the possibility of hiding members via @Hidden annotations, and the Facet methods (such as hideXXX(), choicesXXX(), ...) will be obviously hidden. Don't know if there is finer control in the original Naked Objects framework.
For 3), now the controller generates only markup for different segments of content: the three segments for services/session/context, the segment with action list and the current object view (showing the fields). In the layout, which is application specific, the developer can move these segments where he wants and style them with css. The user interaction is *by design* limited to act on objects of the domain model.