Tutorial Step 3 - Adding Admin Functionalities
This tutorial step should show us how to create basic administrator functionalities with adding new items, editing the existing ones and deleting them.
Contents
Deleting items
Having the adding items functionality ready we should feel that other features, such as deleting and updating should not be very hard to implement.
Let's start from the dispatcher rules. Add the following entry to the dispatch.conf file:
1 {dynamic, "^/item/delete/(?<id>[0-9]+)$", {item_admin, delete}}.
And then edit the item_admin.erl created in the previous step:
1 -export([delete/1]).
2
3 ...
4
5 delete(Args) ->
6 Id = list_to_integer(proplists:get_value(id, Args)),
7 wtype_item:delete(Id),
8
9 {redirect, "/item/all"}.
Great! That's all - the service should be ready for handling the delete requests.
Assume we had three items in our shop:
The last item (Form generated one) had an ID = 3. We went to http://localhost:8080/item/delete/3, controller deleted the item and redirected us to /item/all:
Editing items
Our shop is getting better and better, but we still do not have the possibility to edit the particular items. If we made a mistake during the description of an item (or the item became unavailable) we have to remove it from the service and enter the same (but corrected) data once again. Unfortunately, its ID will be different since it is automatically assigned during the save phase (it is true only when we are passing the record with its first field set to undefined atom - otherwise the ID set by user will remain the same).
To change it, at first edit the dispatcher configuration file:
1 {dynamic, "^/item/edit/(?<id>[0-9]+)$", {item_admin, edit}}.
2 {dynamic, "^/item/do_edit$", {item_admin, do_edit}}.
Then add the following functions to the item_admin.erl controller:
1 -export([edit/1, do_edit/1]).
2
3 ...
4
5 edit(Args) ->
6 Id = list_to_integer(proplists:get_value(id, Args)),
7 wpart:fset("__edit", wpart_db:build_record_structure(item, wtype_item:read(Id))),
8 wpart:fset("__primary_key", Id),
9
10 {template, "item/edit.html"}.
11
12 do_edit(_Args) ->
13 case validate_tool:validate_cu(item, create) of
14 {ok, Item} ->
15 wtype_item:update(Item),
16 {redirect, "/item/all"};
17 {error, _Reason} ->
18 %% Make sure to set the primary key of this item
19 wpart:fset("__primary_key", list_to_integer(wpart:fget("post:__primary_key"))),
20 wpart:fset("__edit", wtype_item:prepare_validated()),
21 {template, "item/edit.html"}
22 end.
And at the end we should prepare item/edit.html template file:
<html> <head> <title>Editing an item</title> <link rel="stylesheet" type="text/css" href="/style.css" /> </head> <body> <h1>Editing an item</h1> <wpart:form type="item" action="/item/do_edit" /> </body> </html>
As you have probably noticed, both do_add}} - {{{do_edit and add.html - edit.html looks very but very similar - they differs only in the actions they call - the structure remains the same.
The editing functionality has been split in the two parts: in the first one, when we enter the address /item/edit/N we have to load the entry from the database and initialize the form with its fields' values. In the second step we save the modified content.
The result of our work could be seen when we enter e.g. http://localhost:8080/item/edit/2:
Let's make this item available and change its price to 12.5. After successful validation we should have been redirected to the all items page and notice there is no longer "NOT AVAILABLE" marker next to the second item. When we enter to its address we will see the modified entry:
Gathering all admin functionalities in one place
So far we have written three functionalities that modifies the content of the database: create, edit and delete item functionalities. In order to avoid the mess in our project we should separate the admin features from the normal, ordinary services, such as displaying an item description or listing all items.
As a first step we should separate the admin dispatcher entries from the normal user ones. Dispatcher configuration file allows us to delegate some kind of entries to the other files (see dispatcher wiki page - delegate entry type). We should then create an additional folder inside the config directory - let it be config/dispatcher and add there a new file: admin.conf:
1 {static, "add$", "item/add.html"}.
2 {dynamic, "do_add$", {item_admin, do_add}}.
3 {dynamic, "delete/(?<id>[0-9]+)$", {item_admin, delete}}.
4 {dynamic, "edit/(?<id>[0-9]+)$", {item_admin, edit}}.
5 {dynamic, "do_edit$", {item_admin, do_edit}}.
All of the corresponding entries in the dispatch.conf file should be shrink into one delegate entry:
1 {dynamic, delegate, "^/item/admin/", "config/dispatcher/admin.conf"}.
From now, all the URLs that start from /item/admin/ will be matched against the rules held in config/dispatcher/admin.conf file.
There will be then five classes of admin URLs:
/item/admin/add - that will generate the HTML form for adding a new item
/item/admin/do_add - that will do the actual insertion of the item to the database
/item/admin/delete/N - where N is an integer - that will remove the item from our system
/item/admin/edit/N where N is an integer - that will generate filled HTML form for editing the existing item
/item/admin/do_edit - that will do the actual item's update
We should then change the URLs that are bound to the actions in the HTML files (from /item/do_add to /item/admin/do_add in add.html and from /item/do_edit to /item/admin/do_edit in edit.html).
Download
This tutorial step is over, the package containing the sources of the application can be downloaded from here.
