Friday, March 26, 2010

The rest of the NakedPhp walktrough

This post's title is a parody of The Rest of the Robots.

In the previous post we have seen how to manipulate PHP objects directly, and calling methods on them, thanks to NakedPhp's user interface. Today we will see how to interact with the database, essentially how to store object in it and retrieve them via Repositories.
The situation we left the example application into was the following:
There are two Example_Model_City objects in memory (London and Paris), and a Example_Model_Place (Eiffel Tower) which references, with a many-to-one directional association, Paris.
We are ready to save our session. Generally, a subset of the application's object graph is instantiated in memory, modified and put back in the storage. We are not assuming that the storage is a relational database: it is simply a component that take an object graph and persist it someway. In PHP, one of the few library that can do this is Doctrine 2, which will map our objects on a set of relational tables.
We check that there are no objects to remove, and then click Save:
Note that after saving the session, the objects' color changed. In the example application I associated via CSS rules green to transient objects and orange to managed ones. The difference between the two is that a transient object will vanish if we let the session expire, while a managed one is already present in the storage (so it should be actively removed if we want it to vanish.) The save action response tells us three new entities have been persisted, and no managed ones. Moreover, no objects have been removed.
Let's close this session and click Clear.
The session is now empty, and in the last screen we are already gone to the PlaceFactory object. We notice a new findAllCities action available. It has appeared now because there is a hideFindAllCities() method on Example_Model_PlaceFactory, which returns true until there are no cities in the database (Services has access to external infrastructure like storage and whatever we want them to use, while Entities usually have no external references since they have to be serialized.)
The findAllCities action brings us back all the cities stored in the database. This is an "object" - really an array, but every variable saved in the session is wrapped in a NakedObject instance.
The difference between a normal object and an array is that a wrapped array has a Collection Facet, and the views recognize this Facet and treat it differently. Particularly, they list the contained objects and provide access to them.
Finally, note that this Entity is managed and not transient, because it has been retrieved from the storage.
By clicking on the row of an item of the collection, the object is extracted in the session (it is considered transient because the semantic for the extracted objects has not been written yet. This will be fixed in the future.) It still refers to the same instance in the collection, so if we modify one we'll see the changes in the other.
We now see an example of method call with object parameters. The createPlaceFromCity action will take a City and create for us a new Place object with the city property already set. But we don't like the current cities: we want another one.
As it was the case of Entities editing, the context is conserved between different method calls. We have clicked on createCity and we are now creating Rome.
The new object is available for the original method call now.
And finally, the action returns a new Place object which is stored in the session. The city has been set correctly.

That's all for now. I have exposed a practical example of NakedPhp features and of direct manipulation of objects, which has been persisted, retrieved, and moved around.
Now I can go back to add other features, such as semantics for removal and extraction of objects, and a complete method merging feature. What do I mean? It will be useful to have the createPlaceFromCity method also on every City object, so that, given a city, it will present to the user a Factory Method for Places. In this case, we could have put the method on the Example_Model_City class, but such a method may require collaborators which we should not inject on City: imagine a sendByMail(City) or searchSimilar(City) methods which access mailers or the database. With method merging, any service method which has in its parameters an object of class A will be callable from A objects as well, with the particular A parameter automatically passed and hidden.

The method merging feature was already present in NakedPhp but is currently broken (if I had acceptance-TDDed it, it wouldn't have been.)
See you in April for NakedPhp 0.2 and some news!

No comments: