Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / guides / direct_grid_pt2 / README.js
diff --git a/docs/guides/direct_grid_pt2/README.js b/docs/guides/direct_grid_pt2/README.js
new file mode 100644 (file)
index 0000000..b9462be
--- /dev/null
@@ -0,0 +1 @@
+Ext.data.JsonP.direct_grid_pt2({"guide":"<h1>Mapping a Grid to a MySQL table using Direct and PHP Part 2</h1>\n\n<h2>I. Introduction</h2>\n\n<p>In <a href=\"#!/guide/direct_grid_pt1\">the last tutorial</a> we created a grid that pulled information from a MySQL database using Ext Direct. Through the power and simplicity of grids we created what was essentially a glorified table (albeit 'turbocharged'). To add to the dynamism that grids present to us we'll be adding CRUD (Create, Read, Update, Delete) capabilities as well. A typical scenario that benefits from this is backend interfaces where a client might want to do anything from update someone's address or rename a blog post. We've already got it reading from a database so we're a quarter of the way there!</p>\n\n<p>By the end of this tutorial, we should have something that looks like this.</p>\n\n<p><p><img src=\"guides/direct_grid_pt2/grid-full.png\" alt=\"The final product\"></p></p>\n\n<h2>II. Getting Started</h2>\n\n<h3>2.1 Requirements</h3>\n\n<p>You will need:</p>\n\n<ul>\n<li>A server with PHP (5.3+) and MySQL (4.1.3+) installed</li>\n<li>A browser compatible with Ext JS 4 and debugging tools</li>\n<li>A text editor</li>\n</ul>\n\n\n<p>Personally, I find Firefox with Firebug best for debugging Ext.</p>\n\n<p>It's highly recommended that you complete Part 1 before embarking on this tutorial to fully understand what we're doing. The base files that we'll be following on from <a href=\"resources/start.zip\">can be found here</a>.</p>\n\n<h2>III. Writing the Application</h2>\n\n<h3>3.1 API</h3>\n\n<p>Carrying on from our last tutorial in this series, we wrote a file called grid.js that housed the JavaScript portion of our Ext application. Now, if we're going to move forward to this brave new world of CRUDing we need to make some modifications.</p>\n\n<p>Previously, we defined the proxy in the store but to save records individually, as we'll be doing, it makes more sense to have it defined in our model, <code>PersonalInfo</code>. Remove the <code>proxy</code> configuration inside <code>store</code> and we'll replace the single Direct function (getting the results) with four functions that will create, read, update and delete. To do this, the following is needed:</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nproxy: {\n    type: 'direct',\n    api: {\n        create: QueryDatabase.createRecord,\n        read: QueryDatabase.getResults,\n        update: QueryDatabase.updateRecords,\n        destroy: QueryDatabase.destroyRecord\n    }\n},\n</code></pre>\n\n<p>So there should no longer be a <code>directFn</code>, it's been replaced by a more robust API. Writing this tells it that it's looking for a class called QueryDatabase and the method name (e.g. <code>createRecord</code>).</p>\n\n<p>While we're editing the model, a few of you may have noticed in the last tutorial that the ID field, when sorted, displayed some strange behavior. We didn't set a type to the ID so it assumed that it was a string which caused it to not sort sequentially. This can be fixed by adding a type to the ID field like so:</p>\n\n<pre><code>fields: [{name: 'id', type: 'int'}, 'name', 'address', 'state'],\n</code></pre>\n\n<p>Next, we're going to expose these methods in our config.php file, in the first tutorial we added <code>getResults</code> to our server-side API, now we're going to add the names of our other PHP functions which we'll be creating shortly.</p>\n\n<h3><code>config.php</code></h3>\n\n<pre><code>&lt;?php\n$API = array(\n    'QueryDatabase'=&gt;array(\n        'methods'=&gt;array(\n            'getResults'=&gt;array(\n                'len'=&gt;1\n            ),\n            'createRecord'=&gt;array(\n                'len'=&gt;1\n            ),\n            'updateRecords'=&gt;array(\n                'len'=&gt;1\n            ),\n            'destroyRecord'=&gt;array(\n                'len'=&gt;1\n            )\n        )\n    )\n);\n</code></pre>\n\n<h3>3.2 Plugin</h3>\n\n<p>Back to grid.js, beneath the <code>store</code> variable, we're going to add a plugin for grids called the <a href=\"http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.plugin.RowEditing\">Row Editor</a>. This will give an overlay that makes it really simple for users to change a field's content and looks just like this:</p>\n\n<p><p><img src=\"guides/direct_grid_pt2/row-editor.png\" alt=\"Row Editor in action\"></p></p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nvar rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {\n    clicksToMoveEditor: 1,\n    autoCancel: false\n});\n</code></pre>\n\n<p>By storing the row editor in a variable it'll make it easier to reference later on; as for the configuration options, <code>clicksToMoveEditor</code> is how many times the user clicks to move to a different record in our grid. <code>autoCancel</code> means that they can't discard changes before moving on and will bring up an arrow pointing to the record saying that they need to commit their changes first. Whenever adding a new component it's worth checking out the <a href=\"http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.plugin.RowEditing\">corresponding API documentation on it</a> to find out all the configuration possibilities.</p>\n\n<p>We also need to specify a field type for each of the fields we'll want to edit in the grid, simply append the following to the name, address and state columns:</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\ntext: 'Name',\nfield: {\n    type: 'textfield',\n    allowBlank: false\n}\n</code></pre>\n\n<p>For the state column, we're going to do something a little special and add a validation check as well.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nfield: {\n    type: 'textfield',\n    allowBlank: false\n    vtype: 'alpha'\n}\n</code></pre>\n\n<p><code>vtype</code> stands for validation type and we're asking to only allow characters from the alphabet to be entered into this field as no state has a number in it.</p>\n\n<p>To actually be able to edit our grid with the nice Row Editor interface we need to reference the plugin we created earlier. Still within the grid variable write:</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nplugins: [\n    rowEditing\n],\n</code></pre>\n\n<p>However, if you try to add a record and type a state with a space in it, e.g. New York, you'll find that you're not able to type spaces because we've <em>only</em> allowed characters in the alphabet. To get around this we need to write a custom <code>vtype</code>.</p>\n\n<p>Above our grid variable, create a new variable called <code>alphaSpaceTest</code>, the funny looking string after this is a <a href=\"#!/api/RegExp\">'regular expression'</a> which will only allow hyphens, white space and letters.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nvar alphaSpaceTest = /^[-\\sa-zA-Z]+$/;\n\nExt.apply(Ext.form.field.VTypes, {\n    //  vtype validation function\n    alphaSpace: function(val, field) {\n        return alphaSpaceTest.test(val);\n    },\n    // vtype Text property: The error text to display when the validation function returns false\n    alphaSpaceText: 'Not a valid state, must not contain numbers or special characters.',\n    // vtype Mask property: The keystroke filter mask\n    alphaSpaceMask: /^[-\\sa-zA-Z]+$/\n});\n</code></pre>\n\n<p>Then change the vtype that previously read <code>vtype: alpha</code> to <code>vtype: alphaSpace</code> and we'll have the best of both worlds.</p>\n\n<h3>3.3 UI</h3>\n\n<p>So far our grid looks a tad sparse, we're now going to beef it up a bit and add some extra controls with minimal effort. Within our <code>grid</code> variable we're going to add a property called <code>dockedItems</code> that will hold all of the information for our gorgeous UI.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\ndockedItems: [{\n    xtype: 'toolbar',\n    dock: 'bottom',\n}]\n</code></pre>\n\n<p>Setting the <code>xtype</code> to a toolbar will house the buttons that we'll create later on. You can also try <a href=\"http://docs.sencha.com/ext-js/4-0/#!/api/Ext.panel.Panel-cfg-dockedItems\">experimenting with the dock</a> seeing which position you prefer (possible values are <code>top</code>, <code>left</code>, <code>right</code> and <code>bottom</code>) and you could even add <code>frame: true</code>, it's just a matter of preference.</p>\n\n<h3>3.4 Updating</h3>\n\n<p>We now want to add a function that will update our MySQL table through our QueryDatabase.php file. Because this is a feature that could be used multiple times in one session, we're using the MySQLi prepare statement for it's <a href=\"http://www.linearchat.co.uk/2011/08/why-prepared-statements-in-mysql-are-a-good-thing/\">security and speed improvements</a>.</p>\n\n<h3><code>QueryDatabase.php</code></h3>\n\n<pre><code>...\npublic function updateRecords(stdClass $params)\n{\n    $_db = $this-&gt;__construct();\n\n    if ($stmt = $_db-&gt;prepare(\"UPDATE owners SET name=?, address=?, state=? WHERE id=?\")) {\n        $stmt-&gt;bind_param('sssi', $name, $address, $state, $id);\n\n        $name = $params-&gt;name;\n        $address = $params-&gt;address;\n        $state = $params-&gt;state;\n        //cast id to int\n        $id = (int) $params-&gt;id;\n\n        $stmt-&gt;execute();\n\n        $stmt-&gt;close();\n    }\n\n    return $params;\n}\n</code></pre>\n\n<p>When we say <code>bind_param</code>, we're telling it the order and type that our variables will appear in, so <code>sssi</code> means three strings and an integer. Slightly counter-intuitively, we then say what those variables are and force the <code>$id</code> to be an integer, by default it is a string because of the way it's parsed with Ext Direct. We then execute the statement and close it.</p>\n\n<p>Back to our grid.js file we're going to add the final touch to enable updating. Once we've finished editing a row, the <code>edit</code> event is called. We're going to use this to our advantage to save the record once it's been edited. Underneath our <code>grid</code> variable write the following:</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\ngrid.on('edit', function(e) {\n    e.record.save();\n});\n</code></pre>\n\n<p>The variable <code>e</code> represents an edit event with a <a href=\"http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.plugin.RowEditing-event-edit\">number of properties</a>. We're calling the <a href=\"http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.Model-method-save\"><code>save</code> method</a> which triggers either <code>createRecord</code> or <code>updateRecords</code> in our Ext Direct proxy.</p>\n\n<p>You'll see that when you update a record's value a small, red right triangle will appear on the field that you updated, this means that the record is dirty. A dirty record is one that has a different value from it's <code>originalValue</code> and needs syncing with the store. Once the save function has been successful you should find that the triangle disappears and is no longer dirty. Note to those using Ext JS 4.0.2a, you'll see that the triangle isn't removed, this is fixed in later versions available to Ext JS subscribers.</p>\n\n<h3>3.5 Creating</h3>\n\n<p>In life, creation is the most complicated process but with Ext we'll get through it without too much sweat. To start, within our <code>dockedItems</code> we want to add a button for the user to click to add a record. To do this, we make an array of items.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nitems: [\n{\n    iconCls: 'add',\n    text: 'Add'\n}]\n</code></pre>\n\n<p>The <code>iconCls</code> will be referenced in our own CSS file to give it an appropriate icon. This is just the aesthetics, to make it function properly we'll have to add a <code>handler</code> and a sprinkling of PHP.</p>\n\n<p>A <code>handler</code> can either be a reference to a function variable or contain the function inline, in this case I've done it inline but you <em>could</em> arrange your file with all of the CRUD functions at the top or bottom and reference it like <code>handler: addRecord</code>.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\ntext: 'Add',\nhandler: function() {\n    rowEditing.cancelEdit();\n    // create a blank record from PersonalInfo\n    var record = Ext.create('PersonalInfo');\n    //insert at top\n    store.insert(0, record);\n    //edit at row 0\n    rowEditing.startEdit(0, 0);\n}\n</code></pre>\n\n<p>This anonymous function first cancels editing if another record is being edited, and then creates a new record from our <code>PersonalInfo</code> model, this inserts blank values for our name, address and state fields. We then insert this 'phantom' record into the store using the insert method and then tell the row editor to start editing the top record - where we inserted the record.</p>\n\n<p>As wonderful as Ext is, it can't deal with the server side logic for us so opening our QueryDatabase.php file we're going to create a new method that will insert records into the database.</p>\n\n<h3><code>QueryDatabase.php</code></h3>\n\n<pre><code>...\npublic function createRecord(stdClass $params)\n{\n    $_db = $this-&gt;__construct();\n    if($stmt = $_db-&gt;prepare(\"INSERT INTO owners (name, address, state) VALUES (?, ?, ?)\")) {\n        $stmt-&gt;bind_param('sss', $name, $address, $state);\n\n        $name = $_db-&gt;real_escape_string($params-&gt;name);\n        $address = $_db-&gt;real_escape_string($params-&gt;address);\n        $state = $_db-&gt;real_escape_string($params-&gt;state);\n\n        $stmt-&gt;execute();\n\n        $stmt-&gt;close();\n    }\n\n    return $params;\n}\n</code></pre>\n\n<p>Our method name corresponds to the name we set when declaring our API in grid.js, i.e. <code>createRecord</code>, we then say that 'data that comes from the class stdClass (from router.php) will be assigned to a variable called params, this is for added security so attacks can't be spoofed from another file. The data in question looks like this:</p>\n\n<p><p><img src=\"guides/direct_grid_pt2/creating-record.png\" alt=\"Firebug showing the JSON that contains the data to create a record\"></p></p>\n\n<p>This clearly shows us which class and method is being invoked and includes a JSON data object that we access to get the individual fields for our database. The <code>tid</code> is the transaction ID of POST requests in this session, so this is the second (the first being when it loaded the data).</p>\n\n<p>We then prepare our MySQL statement as we did before. The question marks are linked to the next line, where we bind parameters, the <code>'sss'</code> denotes that there are three variables which are all strings that we then map afterwards, making sure to escape input into our database as one last security measure before executing our query.</p>\n\n<p>At this stage, if you add a new record the ID field will stay at 0. If you'd like to remedy this, add <code>$params-&gt;id = $_db-&gt;insert_id;</code> inside of the <code>createRecord</code> function, before you <code>return</code>. This gets the last inserted ID and sets it as the <code>id</code> in <code>$params</code> which we then return and is used by the grid. The downside of this is that when the record is saved, because the ID changes, Ext interprets the ID field as dirty.</p>\n\n<h3>3.7 Deleting</h3>\n\n<p>Destruction is always easier than creation and this next section will teach you how to be an Ext Shiva. We already have a button to add so now we're going to do the same process to delete, complete with a handler.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\n}, {\n    iconCls: 'delete',\n    text: 'Delete',\n    handler: function() {\n        rowEditing.cancelEdit();\n        var sm = grid.getSelectionModel();\n        store.remove(sm.getSelection());\n        store.sync();\n    }\n}\n</code></pre>\n\n<p>We've seen the first two properties before with adding so I'm going to jump straight to what we're doing with the <code>handler</code>. We get rid of the editing overlay if it's open, get which record is being selected in the grid, and then remove the row from the store (using the selection model) and finally sync everything up so our database is up-to-date with our store.</p>\n\n<p>We then need to write the corresponding snippet of PHP to remove the record from our MySQL database.</p>\n\n<h3><code>QueryDatabase.php</code></h3>\n\n<pre><code>...\npublic function destroyRecord(stdClass $params)\n{\n    $_db = $this-&gt;__construct();\n\n    $id = (int) $params-&gt;id;\n\n    if(is_numeric($id)) {\n        if($stmt = $_db-&gt;prepare(\"DELETE FROM owners WHERE id = ? LIMIT 1\")) {\n            $stmt-&gt;bind_param('i', $id);\n            $stmt-&gt;execute();\n            $stmt-&gt;close();\n        }\n    }\n\n    return $this;\n}\n</code></pre>\n\n<p>We grab the <code>id</code> variable from the <code>$params</code> object and check that it's a number (instead of some nasty SQL injection attack) before passing it to our SQL statement. We're using the same prepare method as we used earlier which works in exactly the same way. Limiting the statement to only one record ensures that only one record will be deleted at a time.</p>\n\n<p>You should now be able to add, update and remove records but we're not quite finished yet.</p>\n\n<h3>3.8 UX: Bare Buttons and Bad Behavior</h3>\n\n<p>Noticed that the buttons are looking a bit bare? To add an icon we're going to write some old school CSS. The classes aren't arbitrary, when coding our buttons we added an <code>iconCls</code> to each one, this adds the class name that we're now using in the CSS. Here are the icons in a <a href=\"resources/icons.zip\">zip file</a>.</p>\n\n<h3><code>style.css</code></h3>\n\n<pre><code>body {\n    background: #ededed;\n}\n\n.add {\n    background: url('images/add.png');\n}\n\n.delete {\n    background: url('images/delete.png');\n}\n</code></pre>\n\n<p>Of course, we also have to link it in our index.html like so:</p>\n\n<h3><code>index.html</code></h3>\n\n<pre><code>...\n&lt;link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\"&gt;\n</code></pre>\n\n<p>As long as it's beneath the Ext CSS file and in the <code>&lt;head&gt;</code> tag it doesn't matter where you place it.</p>\n\n<p>Something else you may have noticed is how easy it is to delete a record forever and how easily this might be done by mistake. To be less rude to our users we're going to add a dialog box to confirm deletions.</p>\n\n<p>Dealing with confirming deletions isn't hard at all. First, we want to find the part of our code that deals with deleting things - our handler for the delete button. Second, we want to split the handler into two parts, destructive and non-destructive behavior. The first two lines are non-destructive so I've left them at the top so they get run as soon as the user clicks Delete, but we only want to remove and sync when they <em>confirm</em> that that's what they want to do.</p>\n\n<p>We invoke <code>Ext.Msg.show</code> with some configuration options. The title and message are self-explanatory, the buttons option dictates what buttons the user will be able to click on, in this case, Yes or No. We've also added an icon to the dialog so that the user knows immediately that an action is needed from them to continue. When the user does decide on one of the options and clicks the corresponding button we can check which one was chosen by supplying a callback function with <code>fn</code>. This is shorthand for function (no surprises) and works the same way as a handler on our Add and Delete buttons where we simply check if they said yes (always in lowercase) and if so, carry out what needs to be done. If we were being <em>really</em> nice we could add an <code>else</code> and resume their editing where they left off.</p>\n\n<h3><code>grid.js</code></h3>\n\n<pre><code>...\nhandler: function() {\n    rowEditing.cancelEdit();\n    var sm = grid.getSelectionModel();\n\n    Ext.Msg.show({\n         title:'Delete Record?',\n         msg: 'You are deleting a record permanently, this cannot be undone. Proceed?',\n         buttons: Ext.Msg.YESNO,\n         icon: Ext.Msg.QUESTION,\n         fn: function(btn){\n             if (btn === 'yes'){\n                 store.remove(sm.getSelection());\n                 store.sync();\n             }\n         }\n    });\n}\n</code></pre>\n\n<p>Voila, now we have a nice, user friendly message that confirms their actions.</p>\n\n<p>And that's it! You should now have a fully functioning grid that looks similar to this:</p>\n\n<p><p><img src=\"guides/direct_grid_pt2/grid-full.png\" alt=\"The final product\"></p></p>\n\n<h2>IV. Conclusion</h2>\n\n<p>In this tutorial we've covered a lot of ground. You should now know how to implement Ext Direct to create, read, update and delete from a database. We've also looked at how easy Ext makes dialogs and alerts that have a direct impact on the application and create a better user experience overall.</p>\n\n<p>If you're going to integrate this into production code, you could look into how to optimize it using <a href=\"http://www.sencha.com/blog/using-ext-loader-for-your-application/\">Ext Loader to only load the classes that we use</a> or process actions in batches with a 'Save changes' button so permanent changes aren't immediate.</p>\n\n<p>Finally, for reference, you can find the <a href=\"guides/direct_grid_pt2/reference-files.zip\">completed source files here</a>.</p>\n","title":"Ext Direct and Grid Part 2"});
\ No newline at end of file