Posts Tagged ‘DAO’

A very quick and simple fxDao client example

// August 3rd, 2009 // No Comments » // fxDao

About one month ago I released fxDao v 1.0.0, a simple PHP gateway used to execute queries on the server specifying them directly in your ActionScript code.

fxdao_example

fxDao Example Application

The main difference between the fxDao approach and others like the one of AS3FlexDB is that the main application code is loosely coupled with it. Using fxDao your main application calls static methods from DAO classes. Encapsulating the data access logic into DAO classes brings the advantage that in case data access logic changes, you have to rewrite only the DAO classes, leaving the rest of the application as is.

In this article I’ll show a very simple ActionScript DAO class example.

The main application code is listed below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    pageTitle="fxDao DAO Example"
    layout="vertical" horizontalAlign="left"
    paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10" 
    width="100%" 
    backgroundColor="#ffffff"
    initialize="onInitialize(event);"
    applicationComplete="onComplete(event);" viewSourceURL="srcview/index.html">
 
  <mx:Script>
  <![CDATA[
    import model.Contact;
    import utils.FxDaoUtils;
    import dao.ContactsDao;
    import mx.collections.ArrayCollection;
    import mx.events.FlexEvent;
 
    [Bindable]
    private var _data:ArrayCollection;
 
    private function onInitialize(event:FlexEvent):void
    {
      FxDaoUtils.loadProperties();
    }
 
    private function onComplete(event:FlexEvent):void
    {
      doReadAll();
    }
 
    private function doReadAll():void
    {
      ContactsDao.readAll(
        function(list:ArrayCollection):void {
          _data = list;
        }
      );
    }
 
    private function doSaveNew():void
    {
      var obj:Contact = getObj();
      ContactsDao.create(
        obj, 
        function(id:Number):void {
          obj.id = id;
          _data.addItem(obj);
          datagrid.selectedIndex = _data.length - 1;
          datagrid.scrollToIndex(datagrid.selectedIndex);
        }
      );
    }
 
    private function doUpdate():void
    {
      var obj:Contact = getObj();
      ContactsDao.update(
        obj,
        function():void {
          setObj(obj);
        }
      );
    }
 
    private function doDelete():void
    {
      var obj:Contact = getObj();
      ContactsDao.deleteById(
        obj.id,
        function():void {
          unsetObj(obj);  
        }
      );
    }
 
    /**
     * Return a Contact instance from form data.
     */
    private function getObj():Contact
    {
      var obj:Contact = new Contact();
      obj.id = Number(inId.text); 
      obj.firstName = inFirstName.text;
      obj.lastName = inLastName.text;
      obj.email = inEmail.text;
      return obj;      
    }
 
    /**
     * Update Contact object in data set.
     */
    private function setObj(obj:Contact):void
    {
      for each (var item:Contact in _data)
      {
        if (item.id == obj.id)
        {
          item.firstName = obj.firstName;
          item.lastName = obj.lastName;
          item.email = obj.email;
          break;
        }
      }
    }
 
    /**
     * Remove object from data set.
     */
    private function unsetObj(obj:Contact):void
    {
      var i:Number = 0;
      for each (var item:Contact in _data)
      {
        if (item.id == obj.id)
        {
          _data.removeItemAt(i);
          datagrid.selectedIndex = -1;
          break;
        }
        i ++;
      }
    }
  ]]>
  </mx:Script>
 
  <mx:HBox verticalAlign="middle">
    <mx:Image source="@Embed('../assets/fxDaoTiny.png')" />
    <mx:Label text="DAO Class Example" fontSize="18" color="#666666">
      <mx:filters>
        <flash.filters:DropShadowFilter 
            xmlns:flash.filters="flash.filters.*" 
            angle="45" blurX="4" blurY="4" 
            distance="2" 
            alpha=".4" color="0x000000" />
      </mx:filters>
    </mx:Label>  
  </mx:HBox>
 
  <mx:Panel
      width="100%" height="100%"
      title="Datagrid" 
      fontFamily="Arial" fontSize="12">
    <mx:DataGrid id="datagrid"
          width="100%" height="100%"
          dataProvider="{_data}">
      <mx:columns>
        <mx:DataGridColumn headerText="ID" dataField="id" />
        <mx:DataGridColumn headerText="First name" dataField="firstName" />
        <mx:DataGridColumn headerText="Last name" dataField="lastName" />
        <mx:DataGridColumn headerText="E-mail" dataField="email" />
      </mx:columns>
    </mx:DataGrid>
  </mx:Panel>
 
  <mx:Panel
      width="100%"
      title="Detail"
      fontFamily="Arial" fontSize="12">
    <mx:Form id="form"
          width="50%">
      <mx:FormItem label="ID">
        <mx:TextInput  id="inId"
            text="{datagrid.selectedItem.id}" 
            enabled="false"/>
      </mx:FormItem>
 
      <mx:FormItem label="First name">
        <mx:TextInput id="inFirstName"
            text="{datagrid.selectedItem.firstName}" />
      </mx:FormItem>
 
      <mx:FormItem label="Last name">
        <mx:TextInput id="inLastName"
            text="{datagrid.selectedItem.lastName}" />
      </mx:FormItem>
 
      <mx:FormItem label="E-mail">
        <mx:TextInput id="inEmail"
            text="{datagrid.selectedItem.email}" />
      </mx:FormItem>
    </mx:Form>
  </mx:Panel>  
 
  <mx:HBox width="100%">
    <mx:Button label="Save as new" click="doSaveNew();" />
    <mx:Button label="Update" click="doUpdate();" enabled="{datagrid.selectedItem != null}" />
    <mx:Button label="Delete" click="doDelete();" enabled="{datagrid.selectedItem != null}" />
    <mx:Spacer width="100%" />
    <mx:Button label="Read all" click="doReadAll();" />
  </mx:HBox>
</mx:Application>

The application loads a configuration file on startup at line 25. The configuration file contains the fxDao entry point URL and the hash key used to calculate a checksum string which is passed to the gateway on each request. The checksum is used to build up a very basic level of security. Security in fxDao is certainly something we have to focus better on in the next future.

Once the application initialization is completed, a first call to the DAO class populates the DataGrid at line35. The ContactsDao.readAll method expects a callback Function as parameter. That function will be invoked once the result will be available: after all we are playing with asynchronous service calls.

The callback function at line 36 takes an ArrayCollection as parameter which means that the DAO class will serialize and deserialize data without the need for the main application to know anything about the data structures exchanged underneath. This is the reason why the main application can be considered loosely coupled with fxDao. The Value Object used in this example is Contact and its very simple structure is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package model
{
[Bindable]
public class Contact
{
  // -------------------------------------------------------------------------
  //
  // Properties
  //
  // -------------------------------------------------------------------------
 
  public var id:Number = -1;
  public var firstName:String = "";
  public var lastName:String = "";
  public var email:String = "";
 
  // -------------------------------------------------------------------------
  //
  // Constructor
  //
  // -------------------------------------------------------------------------
 
  /**
   * Constructor
   * 
   * Assumes an XML structure in the form:
   * 
   * <data>
   *   <c1>123</c1>
   *   <c2>first name</c2>
   *   <c3>last name</c3>
   *   <c4>email</c4>
   * </data>
   * 
   */
  public function Contact(data:XML = null)
  {
    if (data != null)
    {
      id = Number(data.c1);
      firstName = data.c2;
      lastName = data.c3;
      email = data.c4;
    }
  }
 
}
}

All other create, delete and update methods work more or less the same way. As you can see, this example application is very simple and let you remotely read, insert, update and delete records.

The ContactsDao class encapsulates the SQL queries used to read and write records into static methods invoked by the main application. Queries are executed through the fxDao gateway which means that an HTTP service is used to send commands to the server. This is why method calls are asynchronous.

The ContactsDao class source code is listed below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package dao
{
  import model.Contact;
 
  import mx.collections.ArrayCollection;
  import mx.managers.CursorManager;
  import mx.rpc.AsyncToken;
  import mx.rpc.events.ResultEvent;
 
  import utils.FxDaoUtils;
 
public class ContactsDao
{
  // -------------------------------------------------------------------------
  //
  // CRUD methods
  //
  // -------------------------------------------------------------------------
 
  // -----------------------------------
  // Create
  // -----------------------------------  
 
  public static function create(obj:Contact, 
                                userHandler:Function):void
  {
    var query:String = "INSERT INTO contacts SET "
      + "first_name='" + dquote(obj.firstName) + "', "
        + "last_name='" + dquote(obj.lastName) + "', "
        + "email='" + dquote(obj.email) + "'";
    doExecute(query, onCreate, userHandler);
  }
 
  private static function onCreate(event:ResultEvent,
                                   userHandler:Function):void
  {
    var id:Number = Number(event.result.rs.@insert_id);
    userHandler(id);
  }
 
  // -----------------------------------
  // Read
  // -----------------------------------
 
  public static function readAll(userHandler:Function):void
  {
    var query:String = "SELECT * FROM contacts";
    doExecute(query, onReadAll, userHandler);
  }
 
  private static function onReadAll(event:ResultEvent, 
                                    userHandler:Function):void
  {
    var list:ArrayCollection = new ArrayCollection();
    for each (var row:XML in event.result.rs.r) {
      list.addItem(new Contact(row));
    }
    if (userHandler != null) { 
      userHandler(list);
    }
  }
 
  // -----------------------------------
  // Update
  // -----------------------------------
 
  public static function update(obj:Contact,
                                userHandler:Function):void
  {
    var query:String = "UPDATE contacts SET "
      + "first_name='" + dquote(obj.firstName) + "', "
        + "last_name='" + dquote(obj.lastName) + "', "
        + "email='" + dquote(obj.email) + "' "
        + "WHERE id=" + obj.id;
    doExecute(query, onUpdate, userHandler);
  }
 
  private static function onUpdate(event:ResultEvent,
                            userHandler:Function):void
  {
    if (userHandler != null) {
      userHandler();
    }
  }
 
  // -----------------------------------
  // Delete
  // -----------------------------------  
 
  public static function deleteById(id:Number,
                                userHandler:Function):void
  {
    var query:String = "DELETE FROM contacts WHERE id=" + id;
    doExecute(query, onDeleteById, userHandler);
  }
 
  private static function onDeleteById(event:ResultEvent,
                                       userHandler:Function):void
  {
    if (userHandler != null) {
      userHandler();
    }
  }
 
  // -------------------------------------------------------------------------
  //
  // Private methods
  //
  // -------------------------------------------------------------------------
 
  /**
   * Execute given query.
   */
  private static function doExecute(query:String, 
                                    resultHandler:Function, 
                                    userHandler:Function = null):void
  {
    CursorManager.setBusyCursor();
    var params:Object = new Object();
    params.query = query;
    params.checksum = FxDaoUtils.calculateChecksum(query);
    var token:AsyncToken = FxDaoUtils.service.send(params);
    token.myData = {resultHandler: resultHandler, userHandler:userHandler};
  }
 
  /**
   * Double quotes in given string.
   */
  private static function dquote(value:String):String
  {
    return value.replace("'", "''");
  }
 
}
}

You can download the full source code of the example on the fxDao Google Code project home.

If you like fxDao and want to contribute in any way feel free to submit your request! Any kind of help will be appreciated.

References

Rapid client/server Flex application prototyping with fxDao

// July 1st, 2009 // 2 Comments » // fxDao

fxdaosmallAdobe Flex is a well known and widespread technology with a great and appreciated attitude to make development of software prototypes almost a joke (if you ever developed with Java Swing you perfectly know what I mean).

I refer to prototypes because when things become serious and you have to put code on the server side, basically to wipe out fake data and access real one, the development velocity begins to decrease.

Independently from the server side technologies and protocols you have chosen, you’ll have to write the code which will expose web services and data models. Generally this means that a boring, repetitive and, depending on the complexity of the application, not a really fast coding session is about to begin. Not to forget unit tests.

As a result of these considerations I developed fxDao.

The idea behind it is that Flex prototyped apps could use real data by defining ActionScript DAO classes which encapsulate data access logic, which means, SQL queries.

fxdaoclientOnce your prototyped (but really working) application will pass the POC phase, you can decide to rewrite ActionScript DAO classes in order to access server side web services. Since DAO classes communicate within the application by exchanging Value Objects (VO) and lists of VOs (the so called model), you don’t have to change a single line of code in the rest of the application.

Put in other and few words, fxDao is a simple PHP gateway used to execute queries on the server specifying them directly in your ActionScript code.

Query result is XML data packed with a REST approach but nobody prevent you to implement any other format. The format should possibly be the same of your web services, in orded to keep as much code reusable because VOs instantiation takes this data as its input.

I’m still in the POC phase of the project itself, which means that:

  • there is still some work to do on the server side, in order to make the gateway sound and secure and enable it to the use of transactions;
  • there is still some work to do on the client side, in order to provide ActionScript helper classes to facilitate the creation of query statements (think to the Active Record or Prepared Statement approach);
  • fxDao is open source and comes with a GNU General Puplic License so, if you like, you can contribute to its development. Any help will be very appreciated.

That said, you can download the first version of fxDao from the following URL: http://code.google.com/p/fxdao/

In this version fxDao is bundled with a simple Flex client which has the only purpose of testing installation. In the very next post I will show more details about how to write a very quick and simple Flex client which interacts with fxDao exploiting DAO classes.

See you soon.