doc/book/en/development/entityclasses/application-logic.rst
branchstable
changeset 5144 5a09bea07302
child 5152 35e6878e2fd0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/entityclasses/application-logic.rst	Fri Apr 02 17:27:53 2010 +0200
@@ -0,0 +1,158 @@
+How to use entities objects
+---------------------------
+
+The previous chapters detailed the classes and methods available to
+the developper at the so-called `ORM`_ level. However they say little
+about the common patterns of usage of these objects.
+
+.. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping
+
+Entities objects are used in the repository and web sides of
+CubicWeb. In the repository side of things, one should manipulate them
+in Hooks and Operations.
+
+Hooks and Operations provide support for the implementation of rules
+such as computed attributes, coherency invariants, etc (they play the
+same role as database triggers, but in a way that is independant of
+the actual data sources).
+
+So a lot of an application's business rules will be written in Hooks
+(or Operations).
+
+In the web side, views also typically operate using entity
+objects. Obvious entity methods for use in views are the dublin code
+method like dc_title, etc. For separation of concerns reasons, one
+should ensure no ui logic pervades the entities level, and also no
+business logic should creep into the views.
+
+In the duration of a transaction, entities objects can be instantiated
+many times, in views and hooks, even for the same database entity. For
+instance, in a classic CubicWeb deployment setup, the repository and
+the web frontend are separated process communicating over the
+wire. There is no way state can be shared between these processes
+(there is a specific API for that). Hence, it is not possible to use
+entity objects as messengers between these components of an
+application. It means that an attribute set as in `obj.x = 42`,
+whether or not x is actually an entity schema attribute, has a short
+life span, limited to the hook, operation or view within the object
+was built.
+
+Setting an attribute value should always be done in the context of a
+Hook/Operation, using the obj.set_attributes(x=42) notation or a plain
+RQL SET expression.
+
+That still leaves for entity objects an essential role: it's where an
+important part of the application logic lie (the other part being
+located in the Hook/Operations).
+
+Anatomy of an entity class
+--------------------------
+
+We can look now at a real life example coming from the `tracker`_
+cube. Let us begin to study the entities/project.py content.
+
+.. sourcecode:: python
+
+    class Project(TreeMixIn, AnyEntity):
+        __regid__ = 'Project'
+        __implements__ = AnyEntity.__implements__ + (ITree,)
+        fetch_attrs, fetch_order = fetch_config(('name', 'description',
+                                                 'description_format', 'summary'))
+
+        TICKET_DEFAULT_STATE_RESTR = 'S name IN ("created","identified","released","scheduled")'
+
+        tree_attribute = 'subproject_of'
+        parent_target = 'subject'
+        children_target = 'object'
+
+        def dc_title(self):
+            return self.name
+
+First we see that it uses an ITree interface and the TreeMixIn default
+implementation. The attributes `tree_attribute`, `parent_target` and
+`children_target` are used by the TreeMixIn code. This is typically
+used in views concerned with the representation of tree-like
+structures (CubicWeb provides several such views). It is important
+that the views themselves try not to implement this logic, not only
+because such views would be hardly applyable to other tree-like
+relations, but also because it is perfectly fine and useful to use
+such an interface in Hooks. In fact, Tree nature is a property of the
+data model that cannot be fully and portably expressed at the level of
+database entities (think about the transitive closure of the child
+relation).
+
+The `dc_title` method provides a (unicode string) value likely to be
+consummed by views, but note that here we do not care about output
+encodings. We care about providing data in the most universal format
+possible, because the data could be used by a web view (which would be
+responsible of ensuring XHTML compliance), or a console or file
+oriented output (which would have the necessary context about the
+needed byte stream encoding).
+
+The fetch_attrs, fetch_order class attributes are parameters of the
+`ORM`_ layer. They tell which attributes should be loaded at once on
+entity object instantiation (by default, only the eid is known, other
+attributes are loaded on demand), and which attribute is to be used
+when SORTing in an RQL expression concerned with many such entities.
+
+Finally, we can observe the big TICKET_DEFAULT_STATE_RESTR is a pure
+application domain piece of data. There is, of course, no limitation
+to the amount of class attributes of this kind.
+
+Let us now dig into more substantial pieces of code.
+
+.. sourcecode:: python
+
+    def latest_version(self, states=('published',), reverse=None):
+        """returns the latest version(s) for the project in one of the given
+        states.
+
+        when no states specified, returns the latest published version.
+        """
+        order = 'DESC'
+        if reverse is not None:
+            warn('reverse argument is deprecated',
+                 DeprecationWarning, stacklevel=1)
+            if reverse:
+                order = 'ASC'
+        rset = self.versions_in_state(states, order, True)
+        if rset:
+            return rset.get_entity(0, 0)
+        return None
+
+    def versions_in_state(self, states, order='ASC', limit=False):
+        """returns version(s) for the project in one of the given states, sorted
+        by version number.
+
+        If limit is true, limit result to one version.
+        If reverse, versions are returned from the smallest to the greatest.
+        """
+        if limit:
+            order += ' LIMIT 1'
+        rql = 'Any V,N ORDERBY version_sort_value(N) %s ' \
+              'WHERE V num N, V in_state S, S name IN (%s), ' \
+              'V version_of P, P eid %%(p)s' % (order, ','.join(repr(s) for s in states))
+        return self._cw.execute(rql, {'p': self.eid})
+
+.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker/
+
+These few lines exhibit the important properties we want to outline:
+
+* entity code is concerned with the application domain
+
+* it is NOT concerned with database coherency (this is the realm of
+  Hooks/Operations); in other words, it assumes a coherent world
+
+* it is NOT concerned with end-user interfaces
+
+* however it can be used in both contexts
+
+* it does not create or manipulate the internal object's state
+
+* it plays freely with RQL expression as needed
+
+* it is not concerned with internationalization
+
+* it does not raise exceptions
+
+