CRUD stands for Create, Read, Update, Delete. These are the most basic functions of persisted storage. Having access to a system that performs these functions easily can be very helpful to developers and game operators. In this article, we'll create a system that allows us to dynamically create, read, update and delete objects. For this example, we're going to create a management screen for our game-level objects. To do this, we'll use the metaCollection feature in GameSparks. However, it should be possible to use the Game Data Service to achieve the same outcome.


There are five steps required to complete the example:

  • Screen Creation
  • Snippet Creation
  • Create Level Object
  • Implement List Retrieval Page Logic
  • Implement Create/Edit Page Logic


Step 1: Screen Creation

To start, we'll create a new management screen using the details set out below.


Important! If you are not clear on how to do this, please review the Working with Dynamic Forms page in our main GameSparks Learn site before you attempt to follow this example.


Name: Level Objects

ShortCode: LEVEL_OBJECTS_SCREEN

<gs-row>
    <gs-placeholder id="LEVEL_OBJECTS_SCREEN">
        <gs-snippet snippet="level_object_list"></gs-snippet>
    </gs-placeholder>
</gs-row>


Step 2: Level Object List Snippet Creation

For the Level Object List Snippet, we'll first implement some basic HTML and JavaScript as a basis to work from. In the Snippet below, you'll notice we have a placeholder getLevelObjects function - this will be the function to retrieve the data from the Data Service later in this example.


Name: Level Object List

ShortCode: level_object_list

Spark.setScriptData("form", snippetProcessor(Spark.getData().scriptData));
function snippetProcessor(data) {
    
    return {"levelObjects": getLevelObjects() };
    
    // Placeholder get level objects function
    function getLevelObjects () {
        return [{
            "id": "test1",
            "name": "test1",
            "level": "testLevel"
        }];
    }
}


<gs-title-block title="Level Objects" padding="20">
    <table class="table js-table-sorter">
        <thead>
            <tr>
                <th>Object Id</th>
                <th>Object Name</th>
                <th>Level Name</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            {{#each form.levelObjects}}
                <tr>
                    <td>{{id}}</td>
                    <td>{{name}}</td>
                    <td>{{level}}</td>
                    <td>
                        <gs-link snippet="level_object_edit?action=edit&id={{id}}" target="LEVEL_OBJECTS_SCREEN">
                            <button>Edit</button>
                        </gs-link>
                        <gs-link snippet="level_object_list?action=delete&id={{id}}" target="LEVEL_OBJECTS_SCREEN">
                            <button>Delete</button>
                        </gs-link>
                    </td>
                </tr>
            {{/each}}
        </tbody>
    </table>
</gs-title-block>


Step 3: Create the Level Object Add/Edit Snippet

Now that we have our list page for these game level objects setup, next we'll need to implement the basic HTML and some placeholder logic for our Add/Edit Snippet. One thing you might notice here is that we are using the same Snippet for both of these functions. These operations could be split over two Snippets. However, it's worth considering that if the use case of these objects changed - for example an extra parameter was introduced - this would now mean having to update to separate Snippets.


Name: Level Object Edit

ShortCode: level_object_edit


Spark.setScriptData("form", snippetProcessor(Spark.getData().scriptData));
function snippetProcessor(data) {
    
    return {"levelObject": getLevelObject() };
    
    // Placeholder get level object function
    function getLevelObject () {
        return {
            "id": "test1",
            "name": "test1",
            "level": "level1"
        };
    }
}


<style>select {width:100%}</style>
<gs-row>
    <gs-title-block title="{{#if form.levelObject}}Edit {{form.levelObject.name}}{{else}}Create Level Object{{/if}}" padding="20">
        
        <!-- If the Level Object Exists -->
        {{#if form.levelObject}}
            <gs-row>
                <gs-col width="12" align="left">
                    <gs-link snippet="level_object_list" target="LEVEL_OBJECTS_SCREEN">
                        <button>Back to List</button>
                    </gs-link>
                </gs-col>
            </gs-row>
        {{/if}}
        
        <gs-form snippet="level_object_edit?action=save" target="LEVEL_OBJECTS_SCREEN">
            <gs-row><!-- Name -->
                <gs-col width="3" align="right">
                    Name:
                </gs-col>
                <gs-col width="9">
                    <input type="text" name="name" value="{{form.levelObject.name}}" required="required" />
                </gs-col>
            </gs-row>
            
            <gs-row><!-- Level -->
                <gs-col width="3" align="right">
                    Level:
                </gs-col>
                <gs-col width="9">
                    <select name="level">
                        <option value="level1" {{#compare form.levelObject.level "==" level1}}checked="checked"{{/compare}}>Level One</option>
                    </select>
                </gs-col>
            </gs-row>
            
            <!-- Extra Fields -->
            
            <hr /><br />
            <gs-row><!-- Submit / Cancel -->
                <gs-col width="12" align="right">
                    
                    <!-- Level Object Id -->
                    <input type="hidden" name="id" value="{{form.levelObject.id}}" />
                    
                    <!-- If the Level Object Exists -->
                    {{#if form.levelObject}}
                        <gs-submit>Update</gs-submit>
                    {{else}}
                        <gs-submit>Create</gs-submit>
                    {{/if}}
                    
                    <gs-link snippet="level_object_list" target="LEVEL_OBJECTS_SCREEN">
                        <button>Cancel</button>
                    </gs-link>
                </gs-col>
            </gs-row>
        </gs-form>
    </gs-title-block>
</gs-row>


Step 4: Implementing the List Page Retrieval Logic

When we have created the basic HTML templates, we can now begin to implement the custom logic in the Javascript section of the Snippet editor. The first section in which we'll implement this custom logic is the List page. As you can see from the code below, we can now begin to remove our placeholder functions from previous sections and replace these with our actual implementation.

Spark.setScriptData("form", snippetProcessor(Spark.getData().scriptData));
function snippetProcessor(data) {
    
    // Level Object Collection
    var levelObjects = Spark.metaCollection("levelObject");
    
    switch (data.action) {
        case "delete": return deleteObject(data);
        default: return view(data);
    }
    
    function view (data) {
        return {"levelObjects": getLevelObjects() };
    }
    
    function deleteObject (data) {
        if (data.id) {
            levelObjects.remove({"_id": {"$oid": data.id}});
        }
        return view(data);
    }
    
    function getLevelObjects () {
        var r = [];
        var m = levelObjects.find({});
        while (m.hasNext()) r.push(formatObject(m.next()));
        return r;
        
        function formatObject (o) {
            var doc = {};
            if (o._id.$oid) doc.id = o._id.$oid;
            if (o.name) doc.name = o.name;
            if (o.level) doc.level = o.level;
            return doc;
        }
    }
}


Step 5: Implementing the Create / Edit Page Logic

As we did in Step 4 for the List page retrieval logic, we can now begin to implement our custom logic for the Level Object Creation and Edit page. One thing you might notice is that we are using the same Snippet for both the functions of editing and creating the Level Object

Spark.setScriptData("form", snippetProcessor(Spark.getData().scriptData));
function snippetProcessor(data) {
    
    var levelObject = Spark.metaCollection("levelObject");
    
    switch (data.action) {
        case "edit": return editObject(data);
        case "save": return saveObject(data);
        default: return {};
    }
    
    function editObject (data) {
        return {"levelObject": getLevelObject(data.id) };
    }
    
    function saveObject (data) {
        var id = '';
        if (data.id) {
            id = data.id;
            update(id, data);
        } else id = create(data);
        
        return getReturnData(id, data);
        
        function create (data) {
            return levelObject.insert(getObjectData(data));
        }
        
        function update (id, data) {
            levelObject.update({"_id": {"$oid": id}}, {"$set": getObjectData(data)});
        }
        
        function getObjectData(data) {
            return {
                "name": data.name,
                "level": data.level
            };
        }
        
        function getReturnData(id, data) {
            return {"levelObject": {
                "id": id,
                "name": data.name,
                "level": data.level
            }};
        }
    }
    
    function getLevelObject (id) {
        
        return formatObject(levelObject.findOne({"_id": {"$oid": id}}));
        
        function formatObject (o) {
            var doc = {};
            if (o._id.$oid) doc.id = o._id.$oid;
            if (o.name) doc.name = o.name;
            if (o.level) doc.level = o.level;
            return doc;
        }
    }
}