Watch, Follow, &
Connect with Us

Adrian Chaves

Locating Bottlenecks

There are several techniques you can use when working with PHP applications to improve performance. These are probably some of the most fundamental ones:

  • Upgrade PHP. Newest PHP versions will include lots of changes, not just additional features, but also performance improvements.
  • Use cache for database results and rendered pages.
  • Disable PHP extensions you are not using when you deploy your application to a web server.

Those tasks, specially caching, will greatly increase the performance of your application. But once you have followed those advises, it is time to have a closer look to the code of your application.

Bottlenecks

Bottlenecks are those parts of an application whose execution takes more time that it needs to.

Once you have a working application, (almost) bug-free, you focus on improving its performance and responsiveness. But in almost any project, reviewing the whole code in the search of room for improvement is just unacceptable.

What to do then? Well, then you have to focus on the parts of the code that take the most part of the execution time. That is what the Profiler is for. The Profiler watches the execution of your application, and generates a detailed performance analysis you can use to find out which parts of the code are subject to be reviewed, since improving them would have a reasonable impact on the overall performance of the application.

Performance Analysis

To generate a performance analysis with RadPHP, follow these steps:

  1. Open your PHP application on RadPHP.
  2. Go to Run > Enable profiler.
  3. Execute your application (Run > Run).
  4. Go to Run > Load Last Profiler Results.

You will then get a window with a treeview that lets you browse all the functions called during the execution of your application, and tells you how much time (in milliseconds or percentage of the overall execution time) each one of them took.

Now you can locate the parts of your application that are slowing you down, and get into the code and try to improve it.

Improve your Code

Improving the speed of your code might not be easy, though, and sometimes it might be just impossible.

First, you should try to think about alternative algorithms to those you are using, that might improve the performance of the operation. During development, a working code is often more important than a clean, efficient code, specially when there are schedules to be met.

If you are not sure, between two or more algorithms, of which one is the fastest, test them against the Profiler, and you will get your answer.

Of course, knowing how to properly use PHP to gain speed during execution is important too. For example, here you are a couple of tips you might want to follow:

  • Be original, do not copy. Avoid copying variables into other variables when it is not necessary.
  • Do not use variables inside strings. Strings are parsed faster that way. If you need to use a variable on a string, just join the string and the variable instead of using the variable directly on the string.
  • Use the identical operator, ===. Apart from being stricter than ==, which is usually what you need, === is way faster.

Lets Check

Time now to put in practice the Profiler. Lets, for example, check if one of these tips does actually work. Here is the code we will use for that:

  function test1()
  {
    for($i = 0; $i < 10000; $i++)
    {
      echo "$i<br />";
    }
  }

  function test2()
  {
    for($i = 0; $i < 10000; $i++)
    {
      echo $i."<br />";
    }
  }

  test1(); // Test with a variable inside the string.
  test2(); // Test with string a variable joined instead.

We define a function that prints a simple string containing a variable a thousand times. Then, we create a sibling for that function that does exactly the same, but separating the variable and the string, joining them with a dot. This is what the Profiler tell us:

Profiler in action.

Profiler in action.

The second way, $i."<br />", is about 33% faster than the former, "$i<br />".

Lets check now the comparison operators:

  function test1()
  {
    for($i = 0; $i < 10000; $i++)
    {
      if($i == 5000)
        print "Got to the middle!";
    }
  }

  function test2()
  {
    for($i = 0; $i < 10000; $i++)
    {
      if($i === 5000)
        print "Got to the middle!";
    }
  }

  test1();
  test2();

Results:

Check your Own Code

Now is time for you to check your code, and try to improve its performance. These popular PHP benchmarks should give you a hint of minor changes that could mean a great deal for the performance of your application, and use the Profiler to check these or other PHP syntax choices, as well as more complex algorithms.

Posted by achaves on March 15th, 2012 under Testing, Tutorials | Comment now »


Breakpoints and Watches

On this post we are going to have a quick look at breakpoints and watches, features we can use during debug to locate code errors.

Breakpoints

You can attach a breakpoint to a line of code to stop script execution at that point. When you go to Run > Run, the application will stop at the first breakpoint, if you go to Run > Run again, it will jump to the second breakpoint, and so on.

With breakpoints, you can take execution flow control through the key parts of your application, those that are relevant to the error you are trying to fix, avoiding unnecessary steps in the middle.

Imagine you have a PHP script with 10,000 lines of code (it happens…), and need to check only when the execution flow goes through two functions, called from different places, at different times. If you have to manage that with basic flow control, you can go crazy. Using two breakpoints instead that would be a piece of cake.

Conditions

But breakpoints are more flexible than that.

You can, for example, define a PHP expression to be evaluated when the execution flow reaches a breakpoint, and the execution flow will then only be stopped if that expression evaluates to true.

For example, if at some point in a long code a variable has an unexpected value, and you know what the correct value would be, you could use a expression like:

$variable !== $expectedValue

That way, the breakpoint would only stop the execution when an unexpected value has been assigned to the variable.

If you just know that at some point in a loop something unexpected happens, you can set a pass count to a breakpoint, so that breakpoint is ignored a certain amount of times before it stops the execution. For example, you could skip the 30 first loops and take on execution control from there.

Watches

The Debugger has views for both local and global variables, but searching for the desired variables on a big project can be a real pain. Watches let you manage list of variables you need to look at during execution flow control.

Also, you can take advantage of groups to associate variables you want to watch, and have them together on different tabs on the Watch List.

That way, if you are jumping between different parts of an application, and there are certain variables you want to watch on each part, you can add those of each part to a group, and just switch between tabs.

After Debugging

We have already seen how to avoid simple errors, and how to use execution flow control and data watching to locate the source of an error. What happens when you locate all errors? Well, then there is always room for improvement.

Next blog post will focus on how to perform one of the most important tasks during the testing of an application: analyzing its performance. We will locate bottlenecks, parts of the code that, while doing their job, are taking a lot of time to do it, or at least more time than they should.

Posted by achaves on March 14th, 2012 under Testing, Tutorials | Comment now »


Basic Debugging

The Debugger is the perfect tool to locate the source of any logic error in your application. You can use it to get control of the execution flow of your scripts while you watch the value of variables and expressions, allowing you to track the data until the point when something unexpected happens.

Now, lets see some code:

<?php

define('YES', 0);                 // What strcmp() returns for a string match.

$pet = $_GET['pet'];
$pet = strtolower($pet);          // strcmp() is case-sensitive.
$isCat = strcmp($pet, 'cat');     // Save the return value of strcmp().

if($isCat = YES) {                // Check if given pet is a cat.
  echo "You have a cat!";
} else {
  echo "Your pet is not a cat.";
}

?>

This is the code we are going to test. You might have already spotted the error, which is a common PHP logic error, but do not tell anyone! ;)

This script expects the ‘pet’ variable to be set as an URL parameter, and it prints a different string depending on whether ‘pet’ is assigned the string ‘cat’ (case-insensitive) or a different string (if any).

Running Into the Error

With that script open in RadPHP, lets go to Run > Run to get the script rendered on a web browser.

Since we did not pass it ‘pet’ on the URL parameters (we will later see how to do that from RadPHP), so far the script is behaving as expected. Now, lets edit the URL parameters and give ‘pet’ the value ‘cat’ so the script returns the other message. This time we can do this from the web browser itself to save time, later we will do it from RadPHP.

The message is still the same, while the other message should have been returned instead. There is our logic error. Time to locate its source using the Debugger.

Custom URL Parameters

In order to use the Debugger on a script, you must run that script from RadPHP. The Debugger will not work if we call the script from a web browser. Since the error happens when we run the script with ‘pet’ parameter set to ‘cat’, we will need to customize the URL parameters passed to the script when running it from RadPHP.

Go to Run > Parameters… > Debugger, and set the Parameters field to ‘pet=cat’. Then, click OK.

Start the Execution Flow Control

Now it is time to start the execution flow control using the Debugger.

To start execution and pause it right on the first line, you can either go to Run > Step Over or Run > Trace Into, but we should start by examining the code which directly causes the error, which in this case is the expression on the if statement. We can take the execution flow directly to that line by clicking it (so the text cursor is placed on it) and going to Run > Run to Cursor.

Value on Hover

Now, lets have a look at the value that those two expressions (the variable $isCat and the constant YES) have at that point of execution. To do so, you can just hover them, and you will get their value on a tooltip.

They match, they are both 0, as expected. Why did it fail before and is working now? (remember you do not know what is going on). In any case, 0 is obviously equal to 0 so, for whatever reason, this time the if statement will return true.

Continue Execution

Yet I can not believe it suddently works. In fact, I need to see it by myself, see it print the right message this time.

To continue the execution flow, you can use the same commands that let you start its control in the first place. In this case, we need to go to the next step of execution, for which we can use either Run > Trace Into or Run > Step Over. The former, when calling a function or including a file, will take us to the included script or the script where the function is defined. The former will go directly to the next line of execution in the current script.

In this case both would work the same way, but lets go with Run > Step Over.

What the… I’m speechless, I never expected that to happen. You neither, did you? :)

Stop Debugging

Ok, if that if statement can not evaluate that expression right, I will get RadPHP to evaluate it.

First, lets stop debugging. Go to Run > Program Reset.

And lets start the debugger again, get it to that line of code, and check the values are still both 0. They are.

Evaluating Expressions

But this time, instead of continuing with the execution, lets make sure that expression is evaluating to true at that point of execution.

Right-click on the Code Editor, and go to Debug > Evaluate/Modify. Then enter the expression on the Expression field ($isCat = YES), and click Evaluate.

What?!

I don’t understand

Anyone can make a mistake, and using = instead of == or === is one of the most common ones with PHP. It is perfectly possible for someone to go through all the steps above, and still don’t realize what the cause of the error is. In fact, I bet there are a lot of PHP forum threads out there covering this issue.

Just remember, if you ever find youself at a dead end while programming with RadPHP, our forums are always open, and there is a lot of nice people around :)

Advanced Debugging

The techniques we used here to debug our code are perfect for short scripts, and fine on most situations you will run into. Yet, sooner or later you will need more advanced debugging features, which can save you a lot of time: breakpoints and watches.

Posted by achaves on March 13th, 2012 under Testing, Tutorials | 1 Comment »


Avoiding Simple Errors

On this post, we will have a look at two kinds of PHP errors, and learn how we can save time using RadPHP to avoid them.

Syntax Errors

Syntax errors are the most fundamental errors, those produced by a PHP code that cannot be understood by the PHP interpreter, which reports them as “Parse error”.  Common examples of syntax errors are a missing semi-colon at the end of a line of code, not escaping quotes inside a string, forgetting to write an initial or final parenthesis…

To avoid these errors you have Error Insight, a syntax checking tool that reviews the code as you write it, warning you about these errors by underlining them in red on the Code Editor, and listing them on the Structure widget.

Calls to Undefined Elements

Also common are those errors produced by a call to a PHP element that does not exist, either because we misspelled it or because we forgot to include the script defining the element. PHP will exit with an error line, like Call to undefined function or Class ‘[class]’ not found.

To avoid these errors we have the Code Insight, a powerful feature of the Code Editor that will help you write code easier, faster and avoiding the use of unexisting PHP elements.

To take advantage of this feature, just write the beginning of the code element (variable, function, property, method…) and hit the Code Insight shortcut, Ctrl + Space by default. You will then get a list of possible elements, and you can navigate that list, reading for each element its documentation to help you decide which one to use.

For example, if you write str somewhere in your code, and hit the shortcut, the list of possibilities in the current context will be displayed:

You can continue writting after the Code Insight comes in, and the list of possibilities will be filtered as you do so. Following the example above, if we were to type the l character then, the list would get updated displaying only the results still matching our written code:

Sometimes, you do not even have to trigger the Code Insight, it gets triggered automatically. This happens, for example, when you are about to call a method on an object:

Last but not least, Code Insight lets you use Code Templates. Code templates are common code structures that you can print following the same logic you follow with the autocompletion feature, only that you have to hit Ctrl+J instead.

There are a lot of default code templates you can use, but you can also customize them so they match your style, or add new ones to fit your needs.

The Tricky Ones

These errors were easy to avoid, since RadPHP automatically detects syntax errors and warns you about them, and it gives you the tools to avoid calls to undefined elements while working faster on your code.

The tricky ones are the logic errors, those where the PHP interpreter does not complain, but the application does not work as expected. For those, we will use the Debugger.

Posted by achaves on March 12th, 2012 under Testing, Tutorials | Comment now »


Where It Was, Part Four

This article is a follow up of Where It Was, Part Three.

In the previous post, we made it possible to open places on an individual page, and to delete them. It is time now to add the only missing feature for our application: geolocation. We will store the geolocation data of the mobile device when a place is added to the list on the main page, and show it on a map on the individual page of each place.

Database Structure

First thing we need to change is the database structure. Currently, it contains for each place an identifier (id) and a name (name). We will need to add two additional columns to that table: latitude and longitude. These are the decimal values used to indicate a location on the surface of the Earth.

Since we have not as yet released our application to the public, this time we do not need to care about upgrading an existing database of a previous version of our application. Instead, we will delete our application’s data from the device we are testing on (you can just remove the whole application), and let our application create a brand-new (empty) database.

For this first step, we are just going to change the SQL sentence we are using to create the database structure:

database.executeSql('CREATE TABLE IF NOT EXISTS places (id INTEGER PRIMARY KEY ASC, name TEXT)');

So it also adds decimal columns for latitude and longitude:

database.executeSql(
  'CREATE TABLE IF NOT EXISTS places (' +
    'id INTEGER PRIMARY KEY ASC, ' +
    'name TEXT, ' +
    'latitude REAL, ' +
    'longitude REAL' +
  ')'
);

I used different columns to make the code more readable. If your are not familiarized with JavaScript, notice the plus symbol (+), when used with strings, serves to concatenate those strings.

Store Geolocation

Next step will be to include device’s geolocation data when inserting a place on the database. First, lets have a look at the current code to insert a place on the database, and then lets explain the changes we need to perform on it in detail:

// JQuery controls.
var edit = jQuery('#MEdit1');
var list = jQuery('#MList1');

// Input text.
var place = edit.val(); // We get it from the MEdit control.

// Try to insert given place into the database.
function insert() {
  Places.transaction(performQuery, error);
}

// Perform SQL Query to insert the place.
function performQuery(database) {
  database.executeSql('INSERT INTO places (name) VALUES (?)', [place], success);
}

// Function to run if the new place was added to the database.
function success(database, results) {
  list.prepend('<li id="' + results.insertId + '">' + place + '</li>'); // Add item at the beginning of the list.
  list.listview('refresh');               // Refresh the list (updates the style).
  edit.val('').focus();                   // Empty the MEdit and focus it.
}

// Function to run if the new place was not added to the database.
function error(reason) {

  // Construct message.
  var message = 'Could not save ' + place + ': ' + reason.message + '. Do you want to retry?';

  // Print notification.
  navigator.notification.confirm(
    message,                // Message.
    userAnswer,             // Callback function.
    'Saving Place Failed',  // Title.
    'Retry,Cancel'          // Button labels.
  );

}

// Manage user answer when asked whether to retry or not.
function userAnswer(button) {
  if(button == 1) {
    insert();               // Retry place insertion.
  } else {
    edit.focus().select();  // Focus place name box, and preselect its content.
  }
}

// Try to insert the new place into the database.
if(place != '') { insert(); }

Now, lets start with the changes. First things first, we will need two variables, for which we have not yet their values:

var place = edit.val(); // We get it from the MEdit control.
var latitude = null;
var longitude = null;

Currently, on the insert() function, we are starting a database transaction. This was because once user clicked on the “Remember” button, data to be inserted on the database was already available. However, now we will need to try to get geolocation data first. If it is available, then we will start the database transaction. Else, we will let the user know geolocation data cannot be retrieved, and it will be the user who decides whether to save the place anyway or not. That way, if the user forgot to enable geolocation, he or she can enable it and try again.

To do so, we first replace the transaction call with a call to a function we will later define ourselves, getGeolocation():

function insert() {
  getGeolocation();
}

Then we need to define getGeolocation(). This function will call navigator.geolocation.getCurrentPosition(), a PhoneGap method that will try to retrieve geolocation data. We will pass it two callback functions, one in case data is retrieved, and another in case an error occurs.

function getGeolocation() {
  navigator.geolocation.getCurrentPosition(geoSuccess, geoError);
}

Next thing we are going to do is to define the first of these two methods. geoSuccess() should store the retrieved values on the variables we defined above, and then trigger the database transaction we used to call from insert().

function geoSuccess(position) {
  // Save coordinates.
  latitude = position.coords.latitude;
  longitude = position.coords.longitude;

  // Proceed to add place to the database.
  Places.transaction(performQuery, error);
}

Of couse, we will need to change performQuery() as well so it stores geolocation data on the database. Since we will let the user save places even when no geolocation data is available, we will check if either latitude or longitude are null, and based on that store or not geolocation data on the database.

function performQuery(database) {
  // If any geolocation data is missing:
  if(latitude == null || longitude == null) {
    database.executeSql('INSERT INTO places (name) VALUES (?)', [place], success);
  } else { // If geolocation data is available:
    database.executeSql(
      'INSERT INTO places (name, latitude, longitude) VALUES (?, ?, ?)',
      [place, latitude, longitude],
      success
    );
  }
}

Now, it is time to define geoError(). First we notify the user about what happened, and ask him or her to decide whether or not to save the place anyway. To manage the answer, we will use a callback function, geoAnswer().

function geoError(error) {

  // Construct message.
  var message = 'Could not get current location data: ' + error.message + '. Do you want to continue saving anyway?';

  // Print notification.
  navigator.notification.confirm(
    message,                        // Message.
    geoAnswer,                      // Callback function.
    'Geolocation Retrieval Failed', // Title.
    'Continue,Cancel'               // Button labels.
  );
}

function geoAnswer(button) {
  if(button == 1) {
    // Proceed to add place to the database (without geolocation).
    Places.transaction(performQuery, error);
  }
}

Map IFrame

From the Designer, on the second page of our application, we will add an MIFrame component. We can call it iMap (Name property). If you are planning to deploy to iOS devices, please read MIFrame documentation carefully. We will later use iMap to load a map from the Internet using OpenStreetMap.

MIFrame component on the second page

MIFrame component on the second page.

No Map Notification

Also from the Designer, although this time on the main page, we will add an MNotification component we will use in those situations where geolocation data for a place is available but no map can be displayed on the page because user has no Internet connection.

I called the component nNoMap (Name property), and set its Title property to “No Map Available” and its Message property to “Place includes geolocation data, but map cannot be retrieved from the Internet.”.

As with the MIFrame, we will use this component later on this post.

MNotification component on the main page.

MNotification component on the main page.

Load the Map

Finally, when loading place data on the second page, we will include a map if there is Internet connection and place has geolocation data, display no map if there is no geolocation data, and if there is geolocation data but no Internet connection we will tell the user and then hide the map.

Lets start by having a look at our current code:

// Perform SQL Query to get place name.
function performQuery(database) {
  database.executeSql('SELECT name FROM places WHERE id=?', [placeId], success);
}

// Function to run if query goes as expected.
function success(database, results) {
  if(results.rows.length) {
    lPlaceName.text(results.rows.item(0).name);
  } else {
    var reason;
    reason.message = 'no place found with id ' + placeId + '.'
    error(reason);
  }
}

First thing to do is to retrieve latitude and longitude from the SQL query:

function performQuery(database) {
  database.executeSql(
    'SELECT name, latitude, longitude FROM places WHERE id=?',
    [placeId],
    success
  );
}

Now, on the success() function, we are going to implement the logic we talked about above:

function success(database, results) {
  if(results.rows.length) {

    // Store results on readable variables.
    var name = results.rows.item(0).name;
    var latitude = results.rows.item(0).latitude;
    var longitude = results.rows.item(0).longitude;

    // Load labels.
    lPlaceName.text(name);

    // Remove iframe if no geolocation data is available.
    if(latitude == null || longitude == null) {
      jQuery('#iMap_outer').remove();
    } else {
      // If there is no Internet connection, remove iframe and launch notification.
      if(navigator.network.connection.type == Connection.NONE) {
        jQuery('#iMap_outer').remove();
        nNoMapNotification();
      } else { // With Internet connection and geolocation data, set iframe src attribute:
        jQuery('#iMap').attr('src', getOSM(latitude, longitude)); // Setup iframe.
      }
    }

  } else { // If no result is returned.
    var reason;
    reason.message = 'no place found with id ' + placeId + '.'
    error(reason);
  }
}

getOSM() is a function I developed to return an URL to display a given location on an OSM map. You can add its definition right before success()’s:

function getOSM(latitude, longitude) {
  var url = 'http://www.openstreetmap.org/export/embed.html?bbox=' +
    (longitude - 0.0001) + ',' +
    (latitude  - 0.0001) + ',' +
    (longitude + 0.0001) + ',' +
    (latitude  + 0.0001) + ',' +
    '&amp;layer=mapnik&amp;marker=' +
    latitude + ',' +
    longitude;
  return url;
}

Basic Theming

With Mobile Theming, you can combine the different color variations included on the default jQuery Mobile theme, develop a theme of your own, or use common CSS instead. For this application, we will just use different color variations.

On the main page, we have three mobile controls: the MEdit, the MButton and the MList. So we will add three MobileTheme components to the page, and point the Theme property of each of those controls to a theme. We could use a MobileTheme for several components, but this time I want to use a different color variation for each.

Then, we can change the ColorVariation property of those themes to the one we prefer. For example, I used cvMedium2 for the MEdit, cvMedium for the MButton and cvHigh for the MList.

You can do the same to change the aspect of the two MButton components on the second page.

Color variation applied to Where It Was pages.

End of Part Four

Our application is now able to store a place’s name and location, and display the later on a mapWhere It Was initial goals were achieved, so we can call this its 1.0 version and release it to the public.

This is the last part of the tutorial. We have learned how to create a mobile application with RadPHP, how to use some of the many mobile components available, as well as programming with JavaScript, jQuery, jQuery Mobile and PhoneGap.

But who knows, maybe someday we will see Where It Was 2.0 :)

Posted by achaves on February 10th, 2012 under Mobile, Tutorials | Comment now »


Where It Was, Part Three

This article is a follow up of Where It Was, Part Two.

In the last post, we got our application to keep a database of places, and made it possible to add new places to the list. Now we will make it possible to open places on a second page, and to delete them from there.

Second Page Design

Currently, our database is only storing two fields for each place: an identifier, just for logical purposes, and a name, which is the way users can identify the different places on the list. But we plan to add geolocation to the database later, and we have to be prepared to add even more data in the future: images, videos…

We cannot include any of those on the list, it would be too cluttered, so we will need to let the user tap a place on the list, and then open a page with all the information available for that place. We will begin by designing that second page.

With our project open on RadPHP, go to File > New > Other… > RadPHP Project > PHP Files, select iPhone Page and click OK. A second page should be added to your project, and it should be displayed on the Designer. I suggest you change its name. Filenames will not be visible for the user, but short and meaningful names can make your code more readable, and we will use filenames when moving between pages from code. I decided to name the main page main and this new page place-viewer.

In this part of the tutorial we are not yet adding additional data to places database, so we will only be able to display the name of the place on the second page. For that, we will use a Label component. Then, we will also need a couple of MButton components, one to get back to the list of places, and another to delete the current place.

Place the components as you wish on the Designer, and give them descriptive names (use their Name property). For example, I named them lPlaceName, bBack and bDelete. You should also customize a bit the Font property for the label, given its content will be the title of the page. As for the buttons, you should give a value to their Caption property (for example: Back and Delete), and also make sure you set their ButtonType property to btNormal, since their behaviour will be controlled through JavaScript.

Second page of our application on the Designer.

Second page of our application on the Designer.

From the Main Page to the Second Page

Time to make the items on the list of places react to tapping.

jQuery Mobile page navigation does not work the usual way, loading the new page on the browser replacing the previous one. Instead, it loads every page on the document object model (DOM) of the main page on demand. Some Android devices have trouble loading JavaScript files referenced from pages loaded through AJAX, so only the JavaScript files loaded from the initial page will be executed. Since ours is a simple application, we will concentrate the logic on the first page, hence supporting those devices. In the future, however, we should probably refactore the code, and drop support for those devices as they become outdated.

On the main page of our application, at the end of the PageStateJSDeviceReady() function where we already load the database into the list of places, we will add a listener for the tap event on any li element (list items), and we will use that event to open the second page:

jQuery('li').live('tap', function() {
  jQuery.mobile.changePage('place-viewer.html');
});

You might notice we are passing the second page filename with the extension modified, from .php to .html. This is because that is the way all pages are named after deploying the project with the Wizard for PhoneGap.

Pass Place Data Between Pages

Changing to the second page will not be enough, though. We need a way to transfer the information about the tapped place from the first page to the second, so we can properly populate the later with the available data for the place. We will do so by storing place identifier in a JavaScript global variable, and reading the database on the second page. The whole process will be the following:

  1. When adding li elements to the listof places, we will also attach the place identifier to them, in a way that it is not visible.
  2. Then, on the tap event, we will get the identifier from the li element and store it in a JavaScript global variable.
  3. We will then add a listener to an event that triggers right before page changes, and when that change is from the main page to the second page, we will use the event to: (1) get the place identifier from the global variable, (2) get place data from the database using the identifier, and (3) populate the second page with that data.

Attach Place Identifiers to Each List Item

In order to attach the place identifier to the list items, we will have to modify our previous code where we print those list items, both when loading the database and when inserting new items on it. Where we were just printing a place name surrounded by list item tags, we will also set the id property of the list item to the identifier of the place.

When loading the database, right at the beginning of the application, we were using the code below:

function success(database, results) {
  for (var i=0; i<results.rows.length; i++){
		  list.prepend('<li>' + results.rows.item(i).name + '</li>');
  }
		list.listview('refresh'); // Refresh the list (updates the style).
}

We will get the place identifier from the same JavaScript object from where we got the place name, it is just a matter of replacing name with id. So the resulting code, after the new modifications, should look like this:

function success(database, results) {
  for (var i=0; i<results.rows.length; i++){
    list.prepend('<li id="' + results.rows.item(i).id + '">' + results.rows.item(i).name + '</li>');
  }
  list.listview('refresh'); // Refresh the list (updates the style).
}

As for the function to print a list item right after inserting it on the database, this is the current code:

function success() {
  list.prepend('<li>' + place + '</li>'); // Add item at the beginning of the list.
		list.listview('refresh');               // Refresh the list (updates the style).
		edit.val('').focus();                   // Empty the MEdit and focus it.
}

This time, we have no obvious way to retrieve the identifier of the place we have just entered. When we were writting this function, we only needed the name of the place, which we knew because we had just inserted it into the database.

If we have a look at PhoneGap Storage documentation, we will see that there is no possible way we can retrieve the identifier of the item we inserted from a success callback method on the transaction() method. But it turns out that if we call our success() function from the executeSql() method instead, it will receive two parameters: the Database object and an SQLResultSet. The later has the following property:

  • insertId: the row ID of the row that the SQLResultSet object’s SQL statement inserted into the database.

This is exactly what we are looking for.

So lets look again at our current code from a wider persepctive, since we will be changing three of its functions:

// Try to insert given place into the database.
function insert() {
  Places.transaction(performQuery, error, success);
}

// Perform SQL Query to insert the place.
function performQuery(database) {
  database.executeSql('INSERT INTO places (name) VALUES (?)', [place]);
}

// Function to run if the new place was added to the database.
function success() {
  list.prepend('<li>' + place + '</li>'); // Add item at the beginning of the list.
		list.listview('refresh');               // Refresh the list (updates the style).
		edit.val('').focus();                   // Empty the MEdit and focus it.
}

Now, on the first function we are going to remove the reference to our success() function, since we are not going to call it for the overall transaction success. We can keep the error callback though:

// Try to insert given place into the database.
function insert() {
  Places.transaction(performQuery, error); // Removed the third parameter (success).
}

On the second function, we will add our success() function callback function to the executeSql method:

// Perform SQL Query to insert the place.
function performQuery(database) {
  database.executeSql('INSERT INTO places (name) VALUES (?)', [place], success);
}

Finally, we will modify our success() function so it prints the place identifier on the id property of the list item:

function success(database, results) {
  list.prepend('<li id="' + results.insertId + '">' + place + '</li>'); // Add item at the beginning of the list.
		list.listview('refresh');               // Refresh the list (updates the style).
		edit.val('').focus();                   // Empty the MEdit and focus it.
}

Store the Identifier of the Tapped Place on a Global Variable

Now that we ensured every item on the list will contain its place identifier, we will modify their tap event, so when user taps a place name on the list, the identifier of that place is stored on a variable that will survive the page change.

At the beginning of this part of the tutorial, we wrote the code to change the page when a place on the list was tapped:

jQuery('li').live('tap', function() {
  jQuery.mobile.changePage('place-viewer.html');
});

We are associating the tap event to the list items (li), hence we will be able to access tapped li element from inside the event function with jQuery(this). So we just need to get its id attribute and store it in a global variable (creating a new property on the window object) right before changing to the second page of our application:

jQuery('li').live('tap', function() {
  window.placeId = jQuery(this).attr('id');
  jQuery.mobile.changePage('place-viewer.html');
});

Populate the Second Page with Place Data

We have now made sure we can get tapped place identifier from anywhere right before changing page. Now we must populate the second page with the data of the place right before the page gets loaded. We will do this with an event, pagebeforeshow, which triggers before a page is displayed when calling changePage() method.

So lets start by adding a listener to this event right after we added the one for the tap event, at the end of the PageStateJSDeviceReady() function:

jQuery('div').live('pagebeforeshow', function(event, ui) {
  // We will write our code for this event here.
});

We attached the event to div elements because there will be always at least one when changing between pages.

Since only the JavaScript code from the main page will be parsed, this event will be triggered everytime we change page. That means our function will be called both when user opens the second page and when he or she gets back to the main page.

We are going to populate the second page only when entering it, and not when entering the main page, so we must check which page we are at when the event gets triggered. To do so, we will check for the existance of a component from the second page. If it exists, we have just changed to the second page, else we changed to the main one.

var lPlaceName = jQuery('#lPlaceName',jQuery.mobile.activePage);
if(lPlaceName.length) // Check if the control exists.
{
  // Here will go the code to be run before displaying the second page.
}

On the first line, we store the lPlaceName Label component on a variable for easy access to it.

You might notice we are passing jQuery() function a second parameter. As I said before, jQuery Mobile loads all the pages on the current DOM. That means that, once the second page gets loaded for the first time, every time we look for the lPlaceName component on the DOM, it will be there, even if we are back to the main page. jQuery.mobile.activePage contains only the DOM of the currently active page, and by passing it to jQuery() we ensure the lPlaceName component is only looked for on that DOM.

Now we can write the code we want to run on the second page right before it gets displayed. We will get the data from the places database for the place with the identifier we stored on the window object, and we will use it to populate the page.

We will start by retrieving the place identifier:

// Place data.
var placeId = window.placeId;

Now we will define a function that runs a transaction on the database:

// Function to load the place data from the database.
function loadData() {
  Places.transaction(performQuery, error);
}

As you see we are only using a error callback function, and no success function. As we did to retrieve the place names from the database on the main page, we must set a success callback function directly on the executeSql() method. So we will define performQuery() function like this:

// Perform SQL Query to get place name.
function performQuery(database) {
  database.executeSql('SELECT name FROM places WHERE id=?', [placeId], success);
}

If you wonder why we are in such a trouble to get the name of the selected place, why we did not store it directly on the window object instead, avoiding this SQL transaction now, remember that we are planning to add more information to places, namely geolocation, that will not be loaded on the main page. We are preparing for the future.

Our success() function will look something like this:

function success(database, results) {
  if(results.rows.length) {
    lPlaceName.text(results.rows.item(0).name);
  } else {
    var reason;
    reason.message = 'no place found with id ' + placeId + '.'
    error(reason);
  }
}

We make sure the query returned results, and in that case we use the name retrieved on the first row of results (there should only be a row) to populate the lPlaceName label display text. If no result is retrieved, we call the error() function with an error message included. Now, lets define our error() function:

// Function to run if there is any issue with the database query.
function error(reason) {

  // Construct message.
  var message = 'Could not load place: ' + reason.message + '.';

  // Print notification.
  navigator.notification.alert(
    message,                      // Message.
    navigator.app.backHistory(),  // Callback function.
    'Loading Place Failed',       // Title.
    'OK'                          // Button labels.
  );

}

It simply displays a dialog with the error message, and gets back to the main page of our application.

navigator.app.backHistory() is an undocumented PhoneGap method to go to a previous page of your application. You should also know of the navigator.app.exitApp() method, which lets you exit your application.

Finally, we proceed to call our initial function:

loadData();

Setup the Back Button

The label is now properly setup before the page is displayed. It is time to continue with the other two controls. First one is the Back button.

Setting the Back button up is quite easy. Use the following code at the end of the PageStateJSDeviceReady() function:

jQuery('#bBack').live('tap', function(event) {
  navigator.app.backHistory()
});

That’s it.

Setup the Delete Button

For the Delete button, we will need to remove the place from the database, go back to the main page and remove the item from the list there before the page is displayed.

Lets start by adding another tap event listener at the end of thePageStateJSDeviceReady() function:

jQuery('#bDelete').live('tap', function(event) {
  // Our code for the event will go here.
}

We will get the place identifier from the global variable as we did to populate the page:

// Get place identifier.
var placeId = window.placeId;

Next, we will define a function to run the transaction, and another to perform the actual query:

// Function to load the place data from the database.
function deletePlace() {
  Places.transaction(performQuery, error);
}

// Perform SQL Query to get place name.
function performQuery(database) {
  database.executeSql('DELETE FROM places WHERE id=?', [placeId], success);
}

Now, if the transaction goes as expected, we will store a global variable to indicate a place deletion took place, and we will get back to the main page, where we will later read that variable.

// Function to run if query goes as expected.
function success() {
  window.itemDeletion = true;
  navigator.app.backHistory()
}

Finally we will define the error() function (just notify the user) and call the first function:

// Function to run if there is any issue with the database query.
function error(reason) {

  // Construct message.
  var message = 'Could not delete place: ' + reason.message + '.';

  // Print notification.
  navigator.notification.alert(
    message,                // Message.
    function(){},           // Callback function.
    'Deleting Place Failed', // Title.
    'OK'                    // Button labels.
  );

}

deletePlace();

Update Main Page List after Place Deletion

When the user gets back to the main page, the place he or she just deleted should not be listed. So lets go to the beforepageshow event we previously defined, and lets append an else statement to the if statement where we check if active page is the second page.

if(lPlaceName.length) // Check if the control exists.
{
  // …
} else {   // Main Page
  // Here we will write code now.
}

So, before the main page is displayed, we will check if an item was deleted, and in that case, remove it from the list:

if (window.itemDeletion === true) {
  var placeId = window.placeId;                // Get the identifier of the place.
  jQuery('li[id="' + placeId + '"]').remove(); // Remove the place from the list.
  window.itemDeletion = false;                 // Unset the flag.
}

End of Part Three

We accomplished our goals for this part of the tutorial. We can now open places on a separate page, and delete them from there. We learned how to deal with communication between different pages on a mobile application, and we practiced more with PhoneGap Storage API.

On the upcoming chapter of this tutorial, we are going to include the only remaining feature of our application: geolocation. We will store geolocation data when saving a place to the database, and will load it on a map displayed on the second page.

Posted by achaves on February 9th, 2012 under Mobile, Tutorials | Comment now »


Where It Was, Part Two

This article is a follow up of Where It Was, Part One.

In the previous post, we got a basic user interface to work on. Now it is time to make it actually do something. We will get the “Remember” button to add whatever we write on the MEdit field to the MList, and the list should persist between sessions, that is, it should be still there when user closes and reopens our application.

JavaScript

In this part of the tutorial, we are going to use some RadPHP mobile components, but we will also start writing code.

When programming for mobile devices, using PHP is possible, but it has its cons. Whenever we can, we will rely on JavaScript programming language, and its jQuery library toguether with jQuery Mobile. Also, we will use PhoneGap library to interact with devices — it can be used for things such as storage, notifications, geolocation…

JavaScript should be easy to understand for any developer, especially for those with some knowledge of a programming language following the C style syntax, including PHP. There are guides to get started all over the Internet.

As for the libraries, they all have great official documentation (follow links above) and are, in general, really intuitive.

Mobile Storage System

When developing mobile applications with RadPHP, you can access device storage as a Web SQL Database, using Hardware Components and PhoneGap’s Storage API. It does not matter which device we are talking about, it works the same way for all of them.

Setup a Database

First, we will need to create a database to work on. To do so, from the Designer, go to the Tool Palette, and under Mobile Hardware find a component called MDB. Now drag it and drop it on our application. With the MDB component selected, go to the Object Inspector and fill in the following properties:

  • DBDisplayName: Places. This is the name of the JavaScript variable for our database. In our application, we will use it to perform transactions on the database.
  • DBName: places. This is the logic name of the database, the one it will be saved with on mobile devices running our application.
  • DBSize: 1000000. This is the maximum size of the database, in bytes. 1000000 should be more than enough.
  • DBVersion: 1. This is the version number of our database. We can choose this value freely, like the ones before. It will be used to control changes in the database that might happen in the future as we release new versions of our application with new features.
  • Name: Places. This is the name of the component itself. This property is used to tell components apart on the Designer, since the name is displayed along with component’s icon.
Added an MDB component to the application, and filled its properties.

Added an MDB component to the application, and filled its properties.

With this our database is defined.

Get an Event for When Device Is Ready

Now that we have defined the information of our database, Places, we will get the database initialized when our application is run on a mobile device for the first time. That is, we will create its structure in case it does not exist yet on the device.

This step must be performed as soon as the device loads our application, so first we need an event that gets triggered at that point. We will use an MPageEvents component for that, and we will call it “PageState”, for example. This component has a JavaScript event, OnDeviceReady, which is exactly what we are looking for here.

So, time to drag the component from the Tool Palette (again under Mobile Hardware), drop it on our page, and change its Name property to PageState.

Adding an MPageEvents component to the application, and naming it “PageState”.

Added an MPageEvents to our main page, and named it “PageState”.

Setup a Transaction

Now, we will prepare a database transaction, which will be responsible for creating the database structure if it does not exist already on the mobile device. To do that, we will use an MDBTransaction component. It is a Mobile Hardware component too, check the Tool Palette, and drag it and drop in on the application. Then it is time to fill its properties:

  • DB: Places. This is the database the transaction will work on. It can be chosen from a drop-down list where Places, our MDB component, should be already listed. In fact, you can just double-click the field and Places will be chosen.
  • Name: PlacesInitialization. This is the name of the component itself.
Adding an MDBTransaction component to the application, and filling its properties.

Added an MDBTransaction to our main page, and filled in its properties.

Make the Event Trigger the Transaction

It is time to associate the event to the transaction. Select PageState (MPageEvents component) on the Desinger, and from the Object Inspector go to its JavaScript tab. and double-click its OnDeviceReady event. That will open the Code Editor with the text cursor inside the following PHP code, that has been just generated for us:

function PageStateJSDeviceReady($sender, $params)
{
  ?>
  //begin js

  //end
  <?php
}

If you have been paying attention, you might wonder: “Weren’t we supposed not to use PHP on this tutorial?”. You are right. In fact, we are not using it. RadPHP mobile projects use the RPCL PHP library as base, each page is an MPage container, any mobile component is written in PHP, same for their events. But there will be no trace of PHP on the final application. Once we export our mobile project, it will only be JavaScript. This way, we do not have to renounce to the benefits of the RPCL library and RAD development.

Between the PHP comments above we will place the JavaScript code we want to run when the application is ready. In our case, we want the transaction, PlacesInitialization, to be run. The JavaScript code to run the transaction is this:

PlacesInitializationTransaction();

So we just add this code between the comments. Now, PlacesInitialization transaction will be run whenever our application has been completely loaded on a device. In fact, this is what will happen:

  1. Application starts, completely.
  2. OnDeviceReady event is called.
  3. We call <MDBTransaction>Transaction from the function associated to the OnDeviceReady event.
  4. <MDBTransaction>’s OnTransaction event is called from <MDBTransaction>Transaction.

You just need to replace <MDBTransaction> with the name of the MDBTransaction component, the one we called PlacesInitialization.

Many mobile hardware components include JavaScript functions like this one you can call. For more information, check the documentation on Hardware Components (there are separated pages for each component which has some of these functions).

Define the Transaction

Now that the transaction gets triggered at the right time, we only need to define what it does. To start with, we will select PlacesInitialization on the Designer, and from the Object Inspector, JavaScript tab, we will double-click OnTransaction event. We will gets this code on the Code Editor then:

function PlacesInitializationJSTransaction($sender, $params)
{
    ?>
    //begin js

    //end
    <?php
}

Transactions get a database object (which would be our previously defined MDB component) in a variable called event, and we can run queries on it through its executeSql() method. Since for now we will only be storing a text string for each row (the name or short description of the place), we can do like this:

var database = event;
database.executeSql('CREATE TABLE IF NOT EXISTS places (id INTEGER PRIMARY KEY ASC, name TEXT)');

This code is enough to create our initial database schema in case there is none yet.

Manage Transaction Errors

Finally, in case for some reason our database can not be created, we want to tell our users about it, so they can either fix the problem (for example, if it is a setting on their mobile device), or report the error to us (be it a bug on our application). Select PlacesInitialization on the Designer again, and from the Object Inspector, JavaScript tab, we will double-click OnTransactionError event. We will get this code on the Code Editor then:

function PlacesInitializationJSTransactionError($sender, $params)
{
    ?>
    //begin js

    //end
    <?php
}

Now, between the PHP comments, we can use the PhoneGap notification system:

// Define the message:
var message = 'There was an error creating places database: ' + event.message + '.';

// Call the notification system:
navigator.notification.alert(
  message,                // Message.
  function(){},           // Callback function, we are not using it here.
  'Cannot Store Places',  // Title.
  'OK'                    // Button label.
);

Note you can still use JavaScript’s alert() function if you want to, but you will find PhoneGap notification system quite more powerful while easy to use. Also, there is a mobile component to manage notifications, MNotification, which we will use later. But for this case, since we need to include the content of event.message on the notification, we will not use the component.

Save Items on the List

Now we have both the graphical representation of the list of places, the MList component, and the logical representation, the database, which is created as soon as the applications starts in case it did not exist already.

It is time to fill the list of places for real. When users press the MButton, ‘Remember’, an item should be added to both the database and the list, containing the text entered on the MEdit control.

First, we select the button from the Designer, and we set its ButtonType property to btNormal. We don’t want to submit any form when using this button — or any other button when working with JavaScript and mobile applications, for that matter. It will be just a normal button, and we will control its behavior through JavaScript.

Then we go to its JavaScript events tab on the Object Inspector, and double-click its OnClick method, getting the following code generated for us:

function MButton1JSClick($sender, $params)
{
    ?>
    //begin js

    //end
    <?php
}

Between the PHP comments we will write our JavaScript code. This is what we want it to do:

  1. Get the text from the MEdit control.
  2. Try to save it to the database (logical list of places).
  3. In case it works, add it to the MList control (graphical list of places).
  4. Empty the MEdit and set focus on it.

At the beginning, we should define some variables we will need all across the code: the controls and the name of the place to be saved. We will use jQuery for that. jQuery selectors will help us to easily reference our controls and get the content of the MEdit:

// jQuery controls.
var edit = jQuery('#MEdit1');
var list = jQuery('#MList1');

// Input text.
var place = edit.val(); // We get it from the MEdit control.

Note: When using jQuery selectors on RadPHP mobile applications, use jQuery() syntax instead of $(), since the later will not work.

Then, we’ll define the functions we are going to need.

  • A function from which we will start the transaction on the database. We do it calling the transaction() method of our database, which gets three functions as parameters:
    • A function to execute the SQL query.
    • A function in case the query fails. We will make it print a notification, and give the user the choice to retry.
    • A function in case the query succeeds. We will make it write the entry to the MList.
  • A function to control the answer of the user when given the choice to retry (when the transaction fails).

Lets start with the first function:

// Try to insert given place into the database.
function insert() {
  Places.transaction(performQuery, error, success);
}

Places is the name we gave to our database — the DBDisplayName of our MDB component. We use transaction() method on it, and pass it the names of the three functions we are going to define below:

// Perform SQL Query to insert the place.
function performQuery(database) {
  database.executeSql('INSERT INTO places (name) VALUES (?)', [place]);
}

Notice we use ? on the query string, and then include the name or short description of the place in the array we pass as the second argument. This is the safer aproach. In case you have multiple variables to use in the query, use multiple interrogation symbols and use the following syntax on the second argument: [variable1, variable2, variableN].

// Function to run if the new place was added to the database.
function success() {
  list.prepend('<li>' + place + '</li>'); // Add the place at the beginning of the list.
		list.listview('refresh');               // Refresh the list (updates the style).
		edit.val('').focus();                   // Empty the MEdit and focus it.
}

// Function to run if the new place was not added to the database.
function error(reason) {

  // Construct message.
  var message = 'Could not save ' + place + '. ' + reason.message + '. Do you want to retry?';

  // Print notification.
  navigator.notification.confirm(
    message,                // Message.
    userAnswer,             // Callback function.
    'Saving Place Failed',  // Title.
    'Retry,Cancel'          // Button labels.
  );

}

The function to manage the case where the transaction fails recives an error object (called ‘reason’ above) with two properties:

  • code: error code.
  • message: error description.

The callback function for the notification, userAnswer, will get the index of whatever button user presses (1 or 2 in our case). We will define that function below so it calls insert() in case user pressed ‘Retry’ button on the notification.

// Manage user answer when asked whether to retry or not.
function userAnswer(button) {
  if(button == 1) {
    insert();               // Retry place insertion.
  } else {
    edit.focus().select();  // Focus MEdit field, and preselect its content.
  }
}

Finally, after defining all the functions we need, we will call the first one, although only if MEdit1 field is not empty:

// Try to insert the new place into the database.
if(place != '') { insert(); }

Load any Existing Database upon Start

When our application runs on a mobile device for the first time, it will create an empty database (database structure), and user will be able to add items to it. But if our application runs on the same device again, it will not load the existing database, and the list will be empty, no matter if user entered places before. We will take care of that now.

To do so, we will add additional JavaScript code after database initialization code. That additional code will be quite close to the code to add an item to the list. We will have the same methods, just with slightly different content.

So go to these lines on your code:

function PageStateJSDeviceReady($sender, $params)
{
?>
//begin js
  PlacesInitializationTransaction();
//end
<?php
}

And lets start adding code right after PlacesInitializationTransaction() call:

// JQuery controls.
var list = jQuery('#MList1');

We will also use the MList this time, although not the MEdit.

// Function to load database places into the list.
function loadListFromDatabase() {
  Places.transaction(performQuery, error);
}

This time we did not call any function when the transaction as a whole succeeds. We will control transaction success on a per-query basis. This is because the only way to get the results of a SELECT Web SQL query is from the success method of that same query (third argument on the call).

// Perform SQL Query to insert the place.
function performQuery(database) {
  database.executeSql('SELECT name FROM places', [], success);
}

Here we called the success() function for the SELECT query. As you will see below, on the success() function we can get the results of the query and work with them.

// Function to run if query goes smooth.
function success(database, results) {
  for (var i=0; i<results.rows.length; i++){
    list.prepend('<li>' + results.rows.item(i).name + '</li>');
  }
		list.listview('refresh'); // Refresh the list (updates the style).
}

Now our query selects all the names from the places database. Then, on success(), we use the second parameter to retrieve the results, add them to the list and refresh the list. The rest of the code is even closer to the code we used before to save individual places on the database:

// Function to run if there is any issue with the database query.
function error(reason) {

  // Construct message.
  var message = 'Could not load places list: ' + reason.message + '. Do you want to retry?';

  // Print notification.
  navigator.notification.confirm(
    message,                // Message.
    userAnswer,             // Callback function.
    'Loading Places Failed',  // Title.
    'Retry,Cancel'          // Button labels.
  );

}

// Manage user answer when asked whether to retry or not.
function userAnswer(button) {
  if(button == 1) {
    loadListFromDatabase(); // Retry place insertion.
  }
}

// Load database places into the list.
loadListFromDatabase();

Run on the Android Emulator

Our application should be functional now, even if not at all feature-complete. We should be able to add items to the list, close the application, open it again and those items we entered before should be there.

But better safe than sorry, so run your application on the Android emulator to be sure. You can do so from the Wizard for Phonegap, from the Tools menu. If you have any doubt, check the documentation.

Where It Was running on an Android emulator.

Where It Was running on an Android emulator.

End of Part Two

As you can check on your Android emulator, or mobile project has gone from a useless interface to a working application. We have taken full advantage of PhoneGap Storage and Notification APIs, learned how to create, modify and load a database on mobile devices, and we have also got in touch with JavaScript, jQuery and jQuery Mobile.

On the next post, we are going to let user open places on a separated page, where for now only the name of the place will be displayed — later we will add a map. From that second page, we will also let the user delete places from the list (and database).

Posted by achaves on February 8th, 2012 under Mobile, Tutorials | 4 Comments »


Where It Was, Part One

In this tutorial, we are going to develop a simple mobile application from scratch using RadPHP.

An Idea Is Born

I am new in the city, I’ve just moved, and everything is new to me. I don’t know where things are, and most times I just follow my workmates to wherever they go for lunch. The other day I found a great place to order home-made food, but I was in a hurry so I just got the name. Now I can’t remember it, or where the place was.

So I decided I would need an application for my phone so I could just write down the name or something else about a place where I am, like “Nice Restaurant” or “Baby Stuff”, and my phone should be able to tell me later where I was when I wrote that down.

I want my phone to tell me Where It Was!

If you are going to tell me that there is already an application that does that, or that this is useless since I could just write down the name of the place and search for it later, please remember this is just an excuse to create a sample application step by step so you can follow the process closely :)

Create a Mobile Project

It’s time to open RadPHP and start a mobile project. Just go to Project > Add New Project… > RadPHP Projects, select Mobile Application, and click OK. We will then get the first file of our project opened in the Designer:

RadPHP Mobile Application Design View

RadPHP Mobile Application on the Designer.

As you can see, the Designer for mobile applications includes the possibility to preview different devices and includes backgrounds for them. You can also customize available devices and include your own, just check out the documentation about it.

Now, before anything else, we should set a meaningful name for our project and our first file, unit1.php.e can do so from the Project Manager pane, located by default on the top-right corner of RadPHP. We should also change from the Object Inspector the Caption property of our MPage container to the name of our application:

Change Mobile Page caption with the Object Inspector.

Change Mobile Page caption with the Object Inspector.

Design a Basic Interface

Right now I only care about the main screen, it should be a simple application, as simple as possible, and I want to use it quickly, the less I have to tap the better.

What I will do is to add to the MPage all the things I expect the application interface to have at first. And that is a short list: a field to enter some text (just a line, for place name or short description), a button to save it, and a list of saved places.

When working with mobile applications, we should be careful when we choose components. My rule is: use a mobile component whenever you can, use other components whenever you have to. This way we both avoid incompatibility issues and leave the door open to use Mobile Theming later (can only be used with mobile components).

So, on the Tool Palette, expand the Mobile section and find out there are mobile versions of every component I need for this basic interface, so I will use them: MEdit instead of Edit, MButton instead of Button and MList instead of ListBox.

We drag and drop those components, resize them and place them so they look nice enough, and change MButton’s Caption property to “Remember”.

Three components, and my interface is done.

Three components, and the interface is ready.

Run on a Web Browser

I confess I am a bit impatient, so whenever something new is done to an user interface, I like to try the result right away on a device. Same for the application logic.

To deploy our application to a device or emulator would take some time, but we can run mobile application developed with RadPHP on a web browser. Of course, some things might look slightly different from what they will look like on a device, and on the web browser we will not have access to mobile hardware features, so the application logic will not work. But it is the fastest way to check out our changes to the user interface.

To run on the web browser, you just need to go to Run > Run. RadPHP will load the current mobile page on your default web browser:

There It Was initial interface in Firefox.

Our application running on a web browser.

End of Part One

That’s all for now. We have seen how to create a mobile application project, how to add mobile controls to it, and how to run it on a web browser.

On the next post, we are going to see how to access mobile devices’ storage, and use it to keep a database of places and interact with it.

Posted by achaves on February 7th, 2012 under Mobile, Tutorials | Comment now »


PHP Programming, New Frontiers

Next Thursday, November 24th, a technical seminar will be held in Madrid to talk about the new challenges of programmers, namely mobile devices, the web and social networks.

Through practical lessons, attendees will learn how to take advantage of RadPHP to face these challenges. We will have Antonio Alonso with us, who is responsible of mobile application components integration with RadPHP.

First session will cover Facebook applications development with RadPHP, including topics like debugging, database management, and some advanced components. After a coffee, Mobile session will begin, covering mobile applications development, including controls and components to access hardware features, AJAX for remote databases, and deployment for both iOS and Android.

Entrance is free, but class size is limited, so if you plan to assist please register from here. For additional information, check the official announcement (in Spanish).

Posted by achaves on November 17th, 2011 under Events | 2 Comments »


Hello World!

I am sorry I could not come with an original name for my first blog post. And I’ll pretend this apology was enough to break the ice *wink wink*.

My name is Adrián Chaves Fernández (“Adrian” is OK). I am Documentation Writer for RadPHP.

In short, I am responsible for making possible for RadPHP users to spend less time to figure out how to spend less time to do things. (I bet you had to read that twice at least!)

There will always be something you don’t know. I’ll do everything in my hands for you to find your answer as soon as possible. To achieve this goal, I am going to:

  • Update and complete RadPHP Wiki. Users should be able to find there anything they are looking for related to RadPHP, either as a page in the wiki itself or as a link to an external resource, be it a video tutorial, the RPCL Reference website, a blog post like this one you are reading, or anything else.
  • Record video tutorials and write blog posts about different aspects of RadPHP. I will try to ease RadPHP learning curve, and make it possible, for anyone willing to learn, to find their way in the magic streets of RadPHP.
  • Improve RPCL documentation, so RPCL Reference Guide is as complete as possible.

I will also be around RadPHP Forums, reading about what users miss in the documentation so I can add it, and answering their questions or solving their issues whenever I can.

So, I’m wondering… Is there something you don’t know yet?

Posted by achaves on October 6th, 2011 under Other | 1 Comment »


Server Response from: BLOGS2