Part 4: Sencha Touch Email + Facebook Log In System with PHP & MySQL Backend

14 min read

Originally published August 07, 2014

eBook Cover

Hey there! There is also a (very) expanded version of this tutorial series available that includes the Facebook login portion. As well as getting a convenient PDF, ePub and mobi version, you'll also get a premium video tutorial series covering the development from start to finish and a bunch of other goodies! Check it out here.

Welcome to Part 4 of my tutorial series where I walk you through how to create a Sencha Touch application with a email + facebook signup and authentication system. If you haven't seen it yet, make sure to start at Part 1. Here's the rest of the series:

Part 1: Setting up the Database and API

Part 2: Creating the Application Screens

Part 3: Creating the User Model and API

Part 4: Finishing off the Logic

At this point we have our screen flow in place and a completed API communicating with our PHP & MySQL backend. Now to complete the puzzle all that is left to do (besides the Facebook Login alternative that we can add later) is to hook the rest of our application into that API.

Specifically, we want to add the functionality in our application to create a new user, log in a user and log out a user. This is what our application looks like so far:


Currently you can click between all the screens without having to enter in any credentials. By the end of this tutorial a valid account will be required to proceed to the logged in view.

First, we want to hook up the functionality to actually allow a use to create an account. This will store their account credentials in our MySQL database. Before we do this though, we want to add in an extra little bit of functionality.

If the only acknowledgment that a user exists resides on our server, then the user will have to log in every time they refresh the application. We don't want this, so first we are going to add a new model and store called 'LocalUser' and 'LocalUsers' respectively. This will allow us to store the session locally for the user so when they come back they won't need to log in again as long as their session key matches.

LocalUser.js

/app/model/LocalUser.js

Ext.define('SenchaLogin.model.LocalUser', {
  extend: 'Ext.data.Model',
  requires: ['Ext.data.identifier.Uuid'],
  config: {
    identifier: {
      type: 'uuid',
    },

    fields: [
      { name: 'userId', type: 'int' },
      { name: 'email', type: 'string' },
      { name: 'session', type: 'string' },
    ],
  },
});

LocalUsers.js

/app/store/LocalUsers.js

Ext.define('SenchaLogin.store.LocalUsers', {
  extend: 'Ext.data.Store',
  requires: ['SenchaLogin.model.LocalUser'],
  config: {
    model: 'SenchaLogin.model.LocalUser',
    storeId: 'LocalUsers',

    proxy: {
      type: 'localstorage',
    },
  },
});

Don't forget to add these to your app.js file. Your models, views, controllers and stores should look like this by now:

views: [
    'Main',
    'Login',
    'CreateAccount'
],

models: [
    'User',
    'LocalUser'
],

controllers: [
    'Login'
],

stores: [
    'LocalUsers'
],

Now that is set up we can add in the create account functionality. The logic for this will reside in the 'onSubmitAccountCommand' function of the Login controller. Currently we're just calling the sign in success function immediately, but we're going to replace that with the following. There's far too much code here to talk through step by step, so hopefully my comments are clear enough:

Login.js

/app/controller/Login.js

onSubmitAccountCommand: function(){

     var me = this,
          createView = me.getCreateAccount();

     //Get references to the form fields
     var emailField = createView.down('#email'),
          passwordField = createView.down('#password'),
          passwordConfirmField = createView.down('#passwordConfirm');

     //Grab their values
     var email = emailField.getValue(),
          password = passwordField.getValue()
          passwordConfirm = passwordConfirmField.getValue();

     //If either of the fields are blank, immediately stop executing and
     //trigger the error message function on the create account view
     if(email.length === 0 || password.length === 0){
          createView.showFailedMessage('Please enter a username and password.');
          return;
     }

     //If the passwords do not match, again display the error message
     if(passwordConfirm != password){
          createView.showFailedMessage('Please enter matching passwords');
          return;
     }

     //Looks like we're all good to get underway! Processing on the server
     //can take a little while and we don't want our user waiting around
     //wondering if anything is happening. A load masked will stop the
     //user from interacting with anything and display a spinner and message
     createView.setMasked({
          xtype: 'loadmask',
          message: 'Creating Account...'
     });

     //Create a new instance of the user model
     var newUser = Ext.create('SenchaLogin.model.User', {
          email: email,
          password: password
     });

     var errors = newUser.validate();

     if(!errors.isValid()){
          errors.each(function(error){
               console.log(error);
          });
     }
     else
     {
          //Now that we're happy no errors have occured, we will
          //save the user. This will trigger the proxy defined
          //in the model to be invoked, specifically 'create'
          //which wills end the data to our PHP API
          newUser.save({
               success: function(record, operation){

                    console.log("User successfully created");

                    //Grab the JSON response from the server
                    var responseText = operation.getResponse().responseText;
                    var response = Ext.JSON.decode(responseText);
                    var user = response.users[0];

                    //Let's see what we got back
                    console.log(user);

                    //Now let's store that information locally so the user does
                    //not have to relog everytime

                    var id = user.id;
                    var email = user.email;
                    var session = user.session;

                    var localUsersStore = Ext.getStore("LocalUsers");

                    if(localUsersStore.getCount() > 0){

                     //Someone has already created an account on this device
                     //so let's replace the local user rather than creating
                     //a new one.
                    var currentUser = localUsersStore.getAt(0);

                    currentUser.set('userId', id);
                    currentUser.set('email', email);
                    currentUser.set('session', session);
                    localUsersStore.sync();

                }
                else
                {
                         //No accounts have been created on this device before so let's
                         //create a new instance
                         var newLocalUser = Ext.create('SenchaLogin.model.LocalUser', {
                              userId: id,
                              email: email,
                              session: session
                         });

                         localUsersStore.add(newLocalUser);
                         localUsersStore.sync();
                }

                //We're all done now so we have to remove the loading screen
                //for the user
                    createView.setMasked(false);

                    //Now let's take them to the logged in view
                    me.signInSuccess();
               },
               failure: function(record, operation){

                    console.log("Failed to create user");

                    //Hopefully this shouldn't happen, but if something went wrong
                    //(like the email address already existing) this function
                    //will be triggered and we can display the error to the user
                    var responseText = operation.getResponse().responseText;
                    var response = Ext.JSON.decode(responseText);
                    var error = response.error;

                    createView.showFailedMessage(error);
                    createView.setMasked(false);
               }
          })
     }
}

Now that we've created an account and are (hopefully) logged in, I want you to refresh your browser. You should be kicked back out to the login screen again. There's a couple of things we need to do now. One being that we need to allow the automatic logging in of users from local storage as we discussed before, and the other the ability to log in with their username and password.

Let's get the login form working first. We've already got a handler set up to fire an event when the login button is tapped, so let's head over to our 'Login' controller again and take a look at the 'onSignInCommand'. Again, we don't have much there right now and we're going to replace it with the following code. I'll once again place comments within the code as it is quite long.

Login.js

/app/controller/Login.js

onSignInCommand: function(view, email, password){
     var me = this,
          loginView = me.getLogin();

     //Similar to before, check if the fields are empty and display an error if they
     //are
     if(email.length === 0 || password.length === 0){
          loginView.showSignInFailedMessage('Please enter your username and password.');
          return;
     }

     //Again we want to mask the users view whilst we're performing stuff on the server
     loginView.setMasked({
          xtype: 'loadmask',
          message: 'Signing In...'
     });

     //This is kind of like what we did before when we saved the user and invoked the proxy
     //But this Ajax request is triggered manually by us, and we can supply whatever parameters
     //we want, do some stuff on the server with it, and return data to our application
     Ext.Ajax.request({

          url: 'http://www.joshmorony.com/demos/SenchaLogin/api/users.php?action=login',
          method: 'post',
          params: {
               email: email,
               password: password
          },

          //If a successful response was returned we run this function
          success: function(response){

               var loginResponse = Ext.JSON.decode(response.responseText);

               if(loginResponse.success){
                    //Now we want to retrieve the generated session token
                    var localUsersStore = Ext.getStore('LocalUsers');
                    var session = loginResponse.details[0].session;

                if(localUsersStore.getCount() > 0){
                    //If a local user already exists, update the session key
                    var currentUser = localUsersStore.getAt(0);

                    currentUser.set('session', session);
                    localUsersStore.sync();
                }
                else
                {
                     //If this is the first login then create a new local user
                         var newLocalUser = Ext.create('SenchaLogin.model.LocalUser', {
                              email: email,
                              session: session
                         });

                         localUsersStore.add(newLocalUser);
                         localUsersStore.sync();
                }

                    me.signInSuccess();
               }
               else
               {
                    console.log(loginResponse);
                    me.signInFailure(loginResponse.error);
               }
          },

          failure: function(response){
               me.sessionToken = null;
               me.signInFailure('Login failed. Please try again later.');
          }
     });
},

Now if you refresh your browser and enter in the credentials of the user you just created, you should once again be logged in. Head over to your 'app.js' file now and we're going to add in the logic for automatically signing the user in when the app is refreshed. If you take a look at your 'launch()' function you will notice it is currently pretty simple – that's about to change of course. Replace your current launch() function with the one below:

app.js

app.js

launch: function(){

    // Destroy the #appLoadingIndicator element
    Ext.fly('appLoadingIndicator').destroy();

    // Check if the user is already logged in

    var localUsersStore = Ext.getStore('LocalUsers');

    localUsersStore.load({
        callback: function(records, operation, success){
            if(success){
                if(localUsersStore.getCount() > 0){

                    //A user already exists so let's grab their
                    //information. This grabs the first and only
                    //record in the local users store
                    var currentUser = localUsersStore.getAt(0);

                    //Grab the email and session key so we can
                    //check it against our database
                    var sessionKey = currentUser.get('session'),
                        email = currentUser.get('email');

                    //Similar to the manual login request we made earlier, we are
                    //manually triggering a request to check the session key
                    Ext.Ajax.request({

                        url: 'http://www.joshmorony.com/demos/SenchaLogin/api/users.php?action=checksession',
                        method: 'post',
                        params: {
                            email: email,
                            sessionKey: sessionKey
                        },

                        success: function(response){

                            var checkResponse = Ext.JSON.decode(response.responseText);

                            if(checkResponse.success){

                                var newSession = checkResponse.session;

                                //The session key matched, let's give them
                                //a new one now
                                currentUser.set('session', newSession);
                                localUsersStore.sync();

                                //Now display logged in view first
                                console.log("show main");
                                Ext.Viewport.add([
                                        {xtype: 'main'},
                                        {xtype: 'login'},
                                        {xtype: 'createaccount'}
                                ]);

                            }
                            else
                            {
                                //Session key did not match, so show the login view first
                                console.log("Session key did not match, show login");
                                 Ext.Viewport.add([
                                        {xtype: 'login'},
                                        {xtype: 'createaccount'},
                                        {xtype: 'main'}
                                ]);
                            }
                        },
                        failure: function(response){
                            console.log("Could not sign in");
                        }
                    });
                }
                else
                {
                    //No local user exists so go straight to the
                    //login screen
                    Ext.Viewport.add([
                            {xtype: 'login'},
                            {xtype: 'createaccount'},
                            {xtype: 'main'}
                    ]);
                }
            }
            else
            {
                //Hopefully this doesn't happen!
                console.log("error");
            }
        }
    });

},

If you try to refresh your browser once more, you should find that you will still be in the logged in view! If not, check over your code and what I posted above to see what's gone wrong.

We're almost there! The final thing we need to do is get the log off button working. To make this simple, we're just going to kick the user back to the main screen and destroy their session key. To do this we're going to make a slight modification to our 'onSignOffCommand' function in the Login controller so that it looks like this:

Login.js

/app/controller/Login.js

onSignOffCommand: function(){

    if(!Ext.Viewport.getMenus().left.isHidden())
    {
        Ext.Viewport.hideMenu('left');
    }

    var localUsersStore = Ext.getStore('LocalUsers');
    var currentUser = localUsersStore.getAt(0);

    currentUser.set('session', '');
    localUsersStore.sync();

     Ext.Viewport.animateActiveItem(this.getLogin(), this.getSlideRightTransition());
},

And that's it. With any luck you should now have a completely functioning log in system. You can now create accounts, log in, log out and display different views based on the logged in status of the user.

You may wish to stop the tutorial there but if you've made it this far I highly recommend taking it a step further and checking out Build a Sencha Touch Login System. This eBook package expands on this tutorial series a great deal and takes it a step further by adding a Facebook authentication option with PhoneGap Build. You'll also receive a ton of other content including a video tutorial series of me creating the app from scratch.

As always, if you had any difficulties please share them below – even if you managed to solve the problem, other people may be struggling with the same thing and will benefit from your comment.