Building Web Applications Using Parse Rest API

108  Download (0)

Full text


Building Web Applications Using Parse REST


Using Laravel V4.x to build a simple blog

Mhd Zaher Ghaibeh

This book is for sale at This version was published on 2015-01-10

This is aLeanpubbook. Leanpub empowers authors and publishers with the Lean Publishing process.Lean Publishingis the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do.

This work is licensed under aCreative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License


Tweet This Book!

Please help Mhd Zaher Ghaibeh by spreading the word about this book onTwitter! The suggested tweet for this book is:

I just bought the book Building Web Applications Using Parse REST API

The suggested hashtag for this book is#laravelandparseit_english.

Find out what other people are saying about the book by clicking on this link to search for this hashtag on Twitter:



Foreword . . . . 1

Introduction . . . . 2

What Is Laravel . . . 2

What Is Parse Data? . . . 3

Now What? . . . 7

Creating Data Classes . . . . 9

Data Types: . . . 9

Creating Your Classes: . . . 9

User Authentication . . . 14

Setting Up The Database . . . 14

Creating The Migration File . . . 14

Creating The Seed File . . . 17

Setting up the Layout template . . . 19

Creating The Authentication Controller . . . 19

Creating Posts Controller: . . . 23

Configure Library: . . . 23

Testing Our Configuration: . . . 24

Creating Admin Posts Controller: . . . 24

Creating The Comments Controller . . . 41

Creating The Comments Controller . . . 41

Building General Actions: . . . 42

Putting everything together . . . 54

Posts Missing functionality . . . 54

Comments Missing functionality . . . 56

Front-end Functionality . . . 64

getPost Function . . . 67

Refactoring the posts controller, to use Models . . . 70

Create the Posts Model Class . . . 70

Create getPosts Function . . . 72

Create getPost Function . . . 74

Creating deleteItem Function . . . 77



Refactoring the comments controller, to use Models . . . 83

The Comments Model Class . . . 83

getComments Function . . . 83

getComment Function . . . 85

getPostComments Function . . . 86

deleteItem Function . . . 87

handleItem Function . . . 87

Mastering Parse Query Class . . . 91

How to use parse query class . . . 91

Learning Resources . . . 98

More about Parse Products . . . 98

More about Laravel . . . 98

Preparing your production server . . . 99

Creating Your Laravel & nginx Server . . . 99



by Mhd Zaher Ghaibeh

First of all I have to say thanks for everyone who encourage me to start this small journey and type this beginners book, about how to use and benefit from Parse on your next web project, and I should also say thank forBoydlee Pollentine¹who encourage me to read about Parse, and know how to use it with web applications, not only for mobiles, and also to my oldest friend

Hala Deeb²who always encourage me to not limit my imagination.

Who Am I?

I am the Co-founder ofCreative Web Group Syria³, a web development startup that specializes in developing modern web applications and utilizing the latest web development technologies and methodologies. I have 8 years of web development experience and holds a Bachelor Of Information Technology from the Syrian University, Damascus. I am currently working with

Tipsy & Tumbler Limited⁴as Lead developer.

Thank You

I want also to thank you, for supporting this small project by purchasing the book, and I would like to encourage you contacting me via email if you find any errors, or you have any question don’t hesitate to contact me and start a discussion about it, or to say Hi.

Contact Info

Name : Mhd Zaher Ghaibeh Email :z@zah.me⁵

Blog :http://www.zah.me⁶/ Arabic Blog ¹ ² ³ ⁴ ⁵ ⁶



What Is Laravel

Quoting fromLaravel Documentation⁷: Laravel Philosophy

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, and caching. Laravel aims to make the development process a pleasing one for the developer without sacrificing application functionality. Happy developers make the best code. To this end, we’ve attempted to combine the very best of what we have seen in other web frameworks, including frameworks implemented in other languages, such as Ruby on Rails, ASP.NET MVC, and Sinatra.

Laravel is accessible, yet powerful, providing powerful tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the tools you need to build any application with which you are tasked.

To simplify the idea, Laravel is a powerful web application framework, which will help you to create your next web application in an elegant powerful way.

Laravel Features

• RESTful Routing.

• Powerful Template Engine (called Blade).

• Proven Foundation, since it has been built on top of manySymfony2⁸Components. • Great ORM and Migration System, to deal with the database.

• Supporting many Databases including : MySQL, SQLite, MSSQL, and Postgresql.

• Composer powered, so that you can use Composer to install third party libraries which you can search for onPackagist⁹.

• Built with testing in mind. • Great Community.

⁷ ⁸ ⁹


Introduction 3

Please Note:

This book is not intended to teach you Laravel, this book is going to demonstrate how you can use and interact withParse Data¹⁰ from Parse.com¹¹. If your looking for a resources to learn Laravel 4, you can checkDayle Rees¹²bookCode Bright¹³.

Installing Laravel 4

There are so many ways to install Laravel, the one which I really like is to go toLaravel website¹⁴, download the latest version and extract it on your computer, Open up terminal if you’re on a Mac or Command Prompt if you’re using Windows, navigate to the directory where you have extracted your files and then issue the command:

composer install

to install all the required libraries, including Laravel itself. But also you can do it like this :

• Download and install composer fromhttp://www.getcomopser.org¹⁵. • From the terminal issue the command :

composer create-project laravel/laravel jasmine --prefer-dist

• Configure your Apache virtual host to handle the domains. If you don’t know how to do that,click here¹⁶(I will usejasmine.devhere to reference to the blog app).

• Change the permission for storage directory to be 777 and ensure that you choose to recursively give all directories within it the same permissions.

What Is Parse Data?

Quoting fromParse.com¹⁷website:

Save flexible data objects to the cloud with SDKs for every platform. No servers necessary.

So in easy words, you can use Parse Data as your database server, which you can interact with to save and retrieve your data whenever you need it.

¹⁰ ¹¹ ¹² ¹³ ¹⁴ ¹⁵ ¹⁶ ¹⁷


Introduction 4

Why Using

There is not just one reason to use, there are many of them, a small introduction can’t describe the full potential use ofParse Data¹⁸, but i can say that your limitation is your imagination. So I will list some of the features which you can get when usingParse products¹⁹.

• We can host our static image files (up to 10MB per file) on Parse Hosting²⁰Service, this way you will be using Parse CDN to serve the images of your site.

• We can useParse Analytics²¹ Service to track our API real-time usage, and to track our custom events via the dashboard.

• Offload User management, Quickly add user accounts to your app without having to code a full authentication system yourself.

• Easy Scalability and extendability by simply adding new feature for your application, for example you can useParse Social²²to easily integrate your application with Facebook and Twitter, which

You can take a look atParse Customers²³page to get around and see what other developers has been usingParse Products²⁴for. Data Features

• Full Stack Of SDK, which you can use to interact with your data.

• Powerful Data Management, which you can use to manage, search, and update your content without writing a single line of code.

Data Browser

• Advanced filtering directly from within the data browser. ¹⁸ ¹⁹ ²⁰ ²¹ ²² ²³ ²⁴


Introduction 5

Data Filtering

• Ability to interact with otherParse.com²⁵products.

Signup With

Since we are going to useParse Data²⁶ as our database backend, we need to have an account with them, and create an app to have your own key which you will use for interacting with securely. To do this, you need to:

1. Go to Parse.comSignup page²⁷.

2. After you finish you will be redirected to your Dashboard and you will be greeting with this nice box, which asks you to create your first app:

Create a new app box

1. Now lets create our new app and let’s call jasmine. ²⁵

²⁶ ²⁷


Introduction 6 2. After you click the create button, you will have a small window, which will contain all

your keys which you will use to interact with

Your application API keys

as you can see you have many keys, and lets be honest, all you want to have is only : 1. Application ID.

2. Client Key. 3. REST API Key. 4. Master Key.

and now we are ready to go, just remember to save them for you only, and not publish them on the web, otherwise people will be able to access your data.


Introduction 7

Now What?

Let’s see, we have installed Laravel 4, we have created an account on, what else do we need?

Actually we are missing just one component, which is the ‘PHP Library. Sadly, does not have such library, but that hasn’t stoppedapotropaic²⁸ from creating one, Considering this is the only library recommended by website²⁹, this is what we are going to use, and we might modify it if we find anything we need to change or enhance.

How to install

The easiest way to install the library is by downloading it to our app directory, and adding it to composer auto load so lets do that:

Why not clone it via git? It would be nice to do so, but what if you have modified the

code of the library? Next time the library was updated, you will lose your modifications.

• Download the library fromapotropaic³⁰github repo. • Extract the library inapp/libraries/parsedirectory. • Editcomposer.jsonso its look like :

"autoload": { "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/database/seeds", "app/libraries/parse", "app/tests/TestCase.php" ], },

• Last thing you have to issue is the composer command :

²⁸ ²⁹


Introduction 8 composer dump-autoload

and that’s it, we are done.

In the next chapter, we are going to create the Classes which will be used by our blog to store our posts & comments on it .


Creating Data Classes

Data Types:

To create the class, we need first to know the types of data which is available in Data. Here is a list with all of the data types within Data:

1. Date: The Date type contains a field iso which contains a UTC timestamp stored in ISO 8601 format with millisecond precision: YYYY-MM-DDTHH:MM:SS.MMMZ.

2. Bytes: The Bytes type contains a field base64 which contains a base64 encoding of binary data. The specific base64 encoding is the one used by MIME, and does not contain whitespace.

3. Pointer: The Pointer type is used when we set a ParseObject as the value of another object. It contains the className and objectId of the referred-to value.

4. Relation: The Relation type is used for many-to-many relations when we use ParseRela-tion as a value. It has a className that is the class name of the target objects.

5. String. 6. Number.

7. Boolean: Simple, true/false value.

8. File: You can upload files and add use the hosting service which is offered by Hosting, in the free account you will have 1GB to upload your files to, but keep in mind that the file should not exceed 10MB in size.

9. GeoPoint: The GeoPoint type is used with maps and geolocation data, so it will contain the latitude and longitude values for the point.

10. Array.

11. Object: A hashed array as JSON format.

Creating Your Classes:

Posts Class:

Now that we have know all the types of data which we can use within our class, let’s create our Posts class, this class will hold all of our blog posts.

Please Note,

Before we start with the operation, lets try to simplify the idea of the classes for those who never worked with NOSQL before, and to be honest the simplest way for me to describe it, is that the Class represent the Table in any RDMS, and the row record in RDMS is simply the equivalent for Object in Data. So whenever we say lets

get the object id, or lets get the object, make sure that you translate it in your mind like lets get the row id, or lets get the row record.


Creating Data Classes 10 First of all, lets go toParse.com³¹and login to the dashboard, because we are going to use the data browser to create our first class. Dashboard - Data Browser

To create our first class, we simply click on the top left blue button which say New Class, and that will prompt us to enter the name of the class which you want to create.

New Class Prompt

To name our class, we should make sure to use only numbers, letters, and underscore, and to only begin with a letter.

Now that we have created our class, we should create the tables which will be contained within the class.

Let’s have a few minutes to think, what should the class have as a columns ?

Now that we have taken few minutes to think, We have found that we will need those columns: 1. Post title, and it will be of the type String.

2. Post body, and it will be of the type String. 3. Post date, and it will be of the type Date. 4. Post update, and it will be of the type Date.

5. Post active, and it will be of the type Boolean, and it will indicate if this post is published or not.


Creating Data Classes 11 So after we have defined what we want, let’s see how we can create each columns.

First we click on the class name, then we click on the + Col button from the buttons bar.

The buttons bar

Once we click it we will be prompt with a nice modal to type the name of the column and select the type of the column from the data types which we have talked about earlier.

Create new column

In the column name we type title since this is the first column which we will use to hold the title of our post, then we select the type String, because it will holds only string.

We will create the same thing for each of the other fields. But wait a minute, did I mention that data already have some default columns which we can use? Whenever you create a new class automatically adds the following column to the class:

• objectId: This will hold the id of the object (the id of the record) which we will use, and it will be generated for you automatically so don’t worry about it.

• createdAt: This is going to be date data type column which represent the date/time which you create your record.

• updatedAt: This is going to be date data type column which represent the date/time which you update your record, but by default when you create a record, it will have the same value as createdAt.


Creating Data Classes 12 So after this small info, you know that you only need to create the body and the active field.

Name of the column

The name of the column, Must only contain alphanumeric or underscore characters, and must begin with a letter or number.

After you finish, you should see something like the image below:

Posts Class

To make things bit interesting, why don’t you try to click on the + Row button and try to add the value of each column directly using the Data Browser, you will notice how much easy it is to edit/add new value using the data browser.

After you finish, you will have something like the image below:

Posts class with some data

By default, the default value for the boolean when you add it is true, for now let’s choose to set it to false.

Undefined Value

By default, you can have one or all of your custom columns to have value, but this is not a mandatory thing, I mean for example here, you can have a body for the post, but your not required to have a title and when you retrieve the data, you will not get any

title field and thats what they mean by undefined.

Remember - you don’t have control over the default fields such as objectId, createdAt and updatedAt.

Comments Class:

Now that we’ve become familiar with the Data browser, I don’t think we need to get more into how to create the Comments Class, since its the same old story.

Lets define what is the fields which we are going to use:

• Author name: a String which will hold the name of the comment author. • Author email: a String also and it will hold the email of the comment author.


Creating Data Classes 13 • Approved: boolean type, which will indicate that the comment is approved by the blog

author or not.

• Post: this one is a new one, we didn’t use any like it in the Posts class, this is going to be a Pointer, which will point to the the Post which the comment belong to (you can think of it as the Foreign Key which point to the post).

Creating a pointer field

Creating a pointer field

Please don’t stop here, go ahead and add some default data to the comment class, but make sure that you choose false for the approved field for now.

Adding a pointer via Data Browser:

Just so that you know, when you add a pointer you will not be able to select it. Instead, you can simple copy the objectId of the record which we want to use, and just paste it in the field.


User Authentication

Setting Up The Database

To setup the database in Laravel 4, all you have to do it to edit the file database.php which located at:


As you have read about Laravel currently it support : • SQLite.

• MySQL. • Postgresql. • SQL Server.

So when you open it you will have to change the default connection tosqlite, to match the following code:

return array(

'fetch' => PDO::FETCH_CLASS, 'default' => 'sqlite',

'connections' => array( 'sqlite' => array(

'driver' => 'sqlite',

'database' => __DIR__.'/../database/production.sqlite', 'prefix' => '',

), ),


I have removed all the comments from the code, so all you have to change is thedefaultvalue to becomesqlite.

Creating The Migration File

Now we need to create the migration file which will be used to track our database schema. Open your terminal and navigate to your larval directory and type the following command:


User Authentication 15 php artisan migrate:make --table=users CreateUsersTable

so the result will be something like :

Created Migration: 2013_09_14_154114_CreateUsersTable Generating optimized class loader

Compiling common classes

Note: The name of the file 2013-09-14-154114-CreateUsersTable.php should be different than the one you will have cause it will use the current date and time for the execution time, and as you can see am executing the command on 14/09/2013 at


Now lets open that file which you will find under the migrations directory under the database directory :



use Illuminate\Database\Schema\Blueprint;

use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration { /**

* Run the migrations. *

* @return void */

public function up() {

Schema::table('users', function(Blueprint $table) {


}); }


* Reverse the migrations. *

* @return void */

public function down() {

Schema::table('users', function(Blueprint $table) {


User Authentication 16 });

} }

In simple words, theupfunction executed when you run the command: php artisan migrate

And yes you guessed it rightdownfunction executed whenever you run one of the commands: php artisan migrate:rollback

php artisan migrate:reset php artisan migrate:refresh

Usually thedownfunction is used to drop the table, so we are going to change it to be like this :

public function down() {

Schema::drop('users'); }

Now theuserstable should have some information which can be used to authenticate the user, so we will use only: * email. * password. Thats it, nothing more, but surely Laravel will be nice to add thecreated_atandupdated_atfields for us, lets see how its going to be, first we need to modify theupfunction to do the creation job for us.

public function up() {

Schema::table('users', function(Blueprint $table) {

// adding the email field


// adding the password field

$table->string('password')->nullable()->default(null); $table->timestamps();

}); }

Laravel Schema: Since we are not learning Laravel here, I will point you to theLaravel Schema³²documentation to read more about it.

Now that we have created the migration file we can test it, and from the terminal lets execute the command:


User Authentication 17 php artisan migrate

If you got no error, then everything has went as it should and now we can continue seeding the usersdatabase with some dummy data.

Users Table Fields

Creating The Seed File

Now that we have created our table using the migrate command, we need to add some data, and this can be done via the artisan command db:seed. First lets create a file and call it UsersTableSeeder.php


class UsersTableSeeder extends Seeder {

public function run() {


'email' => '',

'password' => Hash::create('123456789') ));

} }

Second we need to edit a file calledDatabaseSeeder.phpwhich can be found underdatabase\seeds directory, and edit it so that it has the following code:


User Authentication 18


class DatabaseSeeder extends Seeder {

public function run() {


$this->call('UsersTableSeeder'); }


Final step will be to issue the command: php artisan db:seed

User data inside the table

Now that we have created our Authentication table, we can carry on and check the file app\filter.phpwhich already has the required filter to authenticate our website visitor, and those filters are:

//this will help to authenticate the admins

Route::filter('auth', function() {

if (Auth::guest()) return Redirect::guest('login'); });

//this will help to authenticate the guests/normal visitors.

Route::filter('guest', function() {

if (Auth::check()) return Redirect::to('/'); });


User Authentication 19

Setting up the Layout template

We are going to use the latest version of Twitter Bootstrap which you can get fromhttp://www.getbootstrap.com³³, download it and put the content inside theassetsfolder under thepublicfolder.

Now we need to create ourlayout.blade.phpfile, under theadminfolder inside ourviewsfolder, and we just add the following code to it:

<!DOCTYPE html> <html lang="en-US"> <head>

<meta charset="utf-8">

{{HTML::style(asset('assets/css/bootstrap.min.css'))}} {{HTML::style(asset('assets/css/main.css'))}}


{{HTML::script('')}} {{HTML::script(asset('assets/js/bootstrap.min.js'))}}



</head> <body>

<div class="container"> @yield('body')

</div> </body> </html>

As you can see I have added a small comments which is the url for the snippets which I have used to create the forms, elements .. etc, We always should give the credits to the original creators of the codes.

Creating The Authentication Controller

Since this book is not going to teach you Laravel 4³⁴, we are going to create a simple Authentication Controller to handle the login/logout actions only. First lets create the Controller file under theapp\controllersdirectory and add the following code:

³³ ³⁴


User Authentication 20


class AuthController extends BaseController{

public function getLogin() {

return View::make('login'); }

public function postLogin() {

if(Auth::attempt(array('email' => Input::get('email'), 'password' => \ Input::get('password')))){

return Redirect::intended('/admin/dashboard'); }else{

return Redirect::to('/login')

->with('error','You dont have access permission, sorry.'); }


public function getLogout() {


return Redirect::to('/login'); }


The first function is going to show a simple view which only have the login form, which we will use to authenticate our admin, the second function is used to validate the credential of the user. If the user authenticates successfully then they will be redirected to the dashboard page, if not they will be redirected back to the login page, and the last function is used to logout the user from the system. Now in ourroutes.phpwe should add the following code:

Route::get('/login',array('uses' => 'AuthController@getLogin')); Route::post('/login',array('uses'=>'AuthController@postLogin'));

Route::get('/logout', array('as'=>'logout','uses'=>'AuthController@getLogout'\ ));

As we can read each Route method is connected to one of the functions in ourAuthController. Here is the code for our simple login form, I am not a good designer, so I hope you can accept this for as simple as it is:


User Authentication 21



<div class="row">


<div class="alert alert-danger">{{Session::get('error')}}</div> @endif

<div class="col-sm-6 col-md-4 col-md-offset-4">

<h1 class="text-center login-title">Sign in to continue</h1> <div class="account-wall">

<img class="profile-img" src="\ b0-k99FZlyE/AAAAAAAAAAI/AAAAAAAAAAA/eu7opA4byxI/photo.jpg?sz=120"



>'loginForm', 'role'=>'form', 'class'=>'form-signin'))}}



>'form-control','required'=>'required') )}}





{{Form::submit('Login',array('class'=>'btn btn-lg btn-primary\ btn-block'))}}

<span class="clearfix"></span>


</div> </div> </div>


User Authentication 22

The Login Form

In the next few chapters, we will talk about how to communicate with Parse Data, so that we can add/edit/delete our data. The next chapter will cover the Posts Class in Parse Data, which is the core of our main topic.


Creating Posts Controller:

Configure Library:

Since we have downloaded the recommended library fromapotropaic³⁵repo, we need now to configure it so that we can make it work for us.

As mentioned in the repo page we need to create the parseConfig.php file and put it in the same directory as our library, so for our case we need to create it underapp/libraries/parse directory.

The content of this file should be the constant variables which he use to authenticate any action with RESTApi server, and it look like:


class parseConfig {

const APPID = 'D8s479IYdHR6uSFBYXsedjPANYNk8tZvfeOkji50';

const MASTERKEY = '4sFcF2UV9126M9x1X5Jh5px6NvmZHSqdTDMVNuki';

const RESTKEY = 'zCTQp4h2wLrjahIUVERkTsEi2156hKlHtCfYOE5P';

const PARSEURL = ''; }

Use your own keys,

I just added my keys here, so that you can have something to start with, but these keys will not be valid for your own development.

Now if you opened the fileapp/libraries/parse/parse.php, you will notice that the creator of the library has some include statements in the header of the file which is :

<?php include 'parseConfig.php'; include 'parseObject.php'; include 'parseQuery.php'; include 'parseUser.php'; include 'parseFile.php'; include 'parsePush.php'; include 'parseGeoPoint.php'; include 'parseACL.php'; include 'parseCloud.php'; ³⁵


Creating Posts Controller: 24 Since we are usingcomposerto autoload our classes we will not need to include any of these files, so we can comment them out, or if you like you can delete them, I like to keep them just in case I have something to check later.

Testing Our Configuration:

Now we need to check if everything is working as it should or not, so we need to edit our routes.phpfile and add this test route:


$test = new parseQuery('posts');

return Response::json($test->find()); });

If everything was good, and there was no errors at all, you should have a JSON result like : {




"body":"this is the first post which we will have here", "title":"first post",

"createdAt":"2013-09-06T17:49:11.938Z", "updatedAt":"2013-09-06T17:49:27.495Z",

"objectId":"J9mLUW0heO" }]


To be more familiar with theRESTApi³⁶in, it recommended that you read thedocumentation³⁷.

Creating Admin Posts Controller:

We are going to build our admin controllers, but we would like to make the urls to be like, and its really a simple things to accomplish using Laravel routing mechanism, so lets edit ourroutes.phpand just add the following to it:

³⁶ ³⁷


Creating Posts Controller: 25 Route::group(array('before'=>'auth','prefix'=>'admin'),function(){

//here we will add each controller which belongs to the admin area.

Route::controller('posts','AdminPostsController'); Route::controller('comments','AdminCommentsController'); Route::controller('dashboard','AdminDashboardController'); });

After that we need to create two classes in our controllers directory, and we will name them: • AdminPostsController (app\controllers\AdminPostsController.php).

• AdminCommentsController (app\controllers\AdminCommentsController.php). • AdminDashboardController (app\controllers\AdminDashboardController.php).

AdminPostsController Functions:

Now, We have our controller ready to be edited, so lets see what do we need? we need : • View Action.

• List Action. • Delete Action. • Store Action. • Edit Action.

Those are the general actions, lets start coding and lets see how we can do it, and am going to start with the easiest one, the View Action.

View Action: Its really a matter of querying for a specific record, using the objectId value, which we can get from Data Browser, In my case it’sJ9mLUW0heO.

In the php library, there is a class calledparseQuery, this class is the one which we should use when querying for anything we want, and mainly they are two types:

• Querying for the records/record information from a class. • Getting the counts of the records in a class.

In our case here, we want to get a record from the Posts Class, using the objectId (the id) of the record, so lets see how the function should look like:


Creating Posts Controller: 26

public function getRecord($objectId = null) {


return Redirect::to('/admin/posts')->with('error','You must select a \ record to view');



$recordInfo = new parseQuery('posts'); $recordInfo->where('objectId',$objectId); $result = $recordInfo->find();

$data = array('item'=>$result->results[0]);

return View::make('admin.posts.record')->with($data); }catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


Nice, now we have a short function which does exactly what we want, but lets have a small review of what we have just wrote:

$recordInfo = new parseQuery('posts');

We have just created an object of the class parseQuery and we have sent the name of which class we want to query as a parameter.


Then we told the object of the class, that we want to have only the records (since we don’t know how many records it will return) which meets the required condition where theobjectIdis equal to the given value. Sowhereis used here to create this condition before send it to $result = $recordInfo->find();

The find function within our parseQuery object, is used to send the data to RESTApi servers and give us back the results. The results which we get is a matter of JSON array with only one element, all the other things is just a matter of sending the data back to our view to display it, and the code for this view is (admin/posts/record.blade.php):


Creating Posts Controller: 27



<div class="row">


<div class="alert alert-success">{{Session::get('success')}}</div> @endif


<div class="alert alert-danger">{{Session::get('error')}}</div> @endif

<ol class="breadcrumb">

<li><a href="{{URL::action('AdminDashboardController@getIndex')}}">Ho\ me</a></li>

<li><a href="{{URL::action('AdminPostsController@getIndex')}}">ALL Po\ sts</a></li>

<li class="active">{{$item->title}}</li> <li class="pull-right no-before">

<a href="{{URL::route('logout')}}">


</a> </li> </ol>

<div class="panel panel-default widget"> <div class="panel-heading">

<span class="glyphicon glyphicon-list-alt"></span> <h3 class="panel-title">

{{$item->title}}</h3> </div>

<div class="panel-body"> <ul class="list-group">

<li class="list-group-item"> <div class="row">

<div class="col-xs-2 col-md-1">

<img src="" class="img-circ\ le img-responsive" alt="" /></div>

<div class="col-xs-10 col-md-11"> <div>

<div class="mic-info">

on {{date('d-M-Y',strtotime($item->create\ dAt))}}

</div> </div>


Creating Posts Controller: 28 {{$item->body}}


<div class="action">

<a class="btn btn-primary btn-xs" title="Edit\ " href="#">

<span class="glyphicon glyphicon-pencil">\




<a class="btn btn-success btn-xs" title="Publ\ ished" href="#">

<span class="glyphicon glyphicon-ok"></sp\ an>

</a> @else

<a class="btn btn-danger btn-xs" title="Hidde\ n" href="#">

<span class="glyphicon glyphicon-remove">\


</a> @endif

<a class="btn btn-danger btn-xs" title="Delet\ e" href="#">

<span class="glyphicon glyphicon-trash"><\



<a class="btn btn-primary btn-xs" title="View\ Post Comments" href="#">

<span class="glyphicon glyphicon-comment"\

></span> </a> </div> </div> </div> </li> </ul> </div> </div> </div> @stop


Creating Posts Controller: 29

The result of the query

Before we continue, there is a small bug in the library which prevent us from adding the limit,skip,order and include parameter if there was no conditions to our query.

Solving a bug in library:

You should open theparseQuery.phpfile and edit the file to comment the lines 27 till 39 like this:

27 public function find(){ 28 //if(empty($this->_query)){ 29 // $request = $this->request(array( 30 // 'method' => 'GET', 31 // 'requestUrl' => $this->_requestUrl 32 // )); 33 // return $request; 34 //} 35 //else{ 36 // $urlParams = array(

37 // 'where' => json_encode( $this->_query )

38 // );

And add this code after it: 40 if(!empty($this->_query)){

41 $urlParams['where'] = json_encode( $this->_query ); 42 }

Last thing remember to delete the close curly bracket “}” at line 70, since this one belong to the elsewhich we have commented out.

List Action: Now that we have become familiar with parseQuery, lets do the List action, which will bring to us all the records we have in the Posts Class.

First we need to do a small calculation to know how many records we have to skip, since we don’t going to list all the records at one time, to do so we made this simple calculation, and we add it to our constructor function:


Creating Posts Controller: 30

public function __construct() {

$this->perPage = Config::get('application.perPage');

$pageNo = Input::get('page');

$this->skip = (is_null($pageNo['page'])) ? 0 : ( $this->perPage * ( $page\ No['page'] - 1)) ;


So, we get the page variable, and we check if its null, since theInputclass will returnnull if the variable is not defined, if so we don’t have to skip anything, if not we get the value of page variable minus one, and we multiply it by 10, since we need to show 10 records in each page. By doing that we get the value of the skipped records.

Second, we need to query to get the data which we need, by providing it with the amount of records which it needs to skip, and another parameter which will help us to get the count of all records, you will see why we need that when we create the pagination :

$records = new parseQuery('posts');

// this is to return the count of all records.


// this is to limit the returned number of records.


// this is the skip parameter which we calculate.


// this is to send our request to the server, and get the returned data.

$result = $records->find();

Now that we got our data, lets create the pagination, using Laravel Paginator class we can create a nice pagination, and in the simplest form ever, using the data we have:

$paginator = Paginator::make($result->results,$result->count,$this->perPage);

So, the full code of the function will be :

public function getIndex() {


$records = new parseQuery('posts'); $records->setCount(true);

$records->setLimit($this->perPage); $records->setSkip($this->skip); $result = $records->find();


Creating Posts Controller: 31 erPage);

$data = array(

'items'=> $result->results, 'paginator' => $paginator, 'total' => $result->count


return View::make('admin.posts.list')->with($data); } catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


And the view (admin/posts/list.blade.php) which will give use the data is :



<div class="row">


<div class="alert alert-success">{{Session::get('success')}}</div> @endif


<div class="alert alert-danger">{{Session::get('error')}}</div> @endif

<ol class="breadcrumb">

<li><a href="{{URL::action('AdminDashboardController@getIndex')}}">Ho\ me</a></li>

<li class="active">ALL Posts</li> <li class="pull-right no-before">

<a href="{{URL::route('logout')}}">


</a> </li> </ol>

<div class="add-new center-block">

<a class="btn btn-default btn-sm" title="Add new Post" href="#"> <span class="glyphicon glyphicon-plus-sign"></span> Add new P\ ost

</a> </div>


Creating Posts Controller: 32

<div class="clearfix"></div> <br />

<div class="panel panel-default widget"> <div class="panel-heading">

<span class="glyphicon glyphicon-list-alt"></span> <h3 class="panel-title">

All Posts</h3>

<span class="label label-info">

{{$total}}</span> </div>

<div class="panel-body"> <ul class="list-group">

@foreach($items as $item)

<li class="list-group-item"> <div class="row">

<div class="col-xs-2 col-md-1">

<img src="" class="img-circ\ le img-responsive" alt="" /></div>

<div class="col-xs-10 col-md-11"> <div>

<a href="{{URL::action('AdminPostsController@\ getRecord',$item->objectId)}}">

{{$item->title}}</a> <div class="mic-info">

on {{date('d-M-Y',strtotime($item->create\ dAt))}}

</div> </div>

<div class="comment-text">

{{Str::limit($item->body, 50)}}


<div class="action">

<a class="btn btn-primary btn-xs" title="View\ " href="{{URL::action('AdminPostsController@getRecord',$item->objectId)}}">

<span class="glyphicon glyphicon-eye-open\ "></span>



<a class="btn btn-success btn-xs" title="Publ\ ished" href="#">

<span class="glyphicon glyphicon-ok"></sp\ an>

</a> @else

<a class="btn btn-danger btn-xs" title="hidde\ n" href="#">


Creating Posts Controller: 33

<span class="glyphicon glyphicon-remove">\

</span> </a> @endif </div> </div> </div> </li> @endforeach </ul> </div> </div> {{$paginator->links()}} </div> @stop

List Of All Posts

Delete Action: Deleting objects is one of those actions which you will find easy to use and easy to understand, all you have to do is to create an object of typeparseObjectand just execute the delete function after providing it with the value of the object id which we want to delete. The full code for this function is:

public function getDelete($objectId = null){


return Redirect::to('/admin/posts')->with('error','You must select a record\ to delete');



$recordInfo = new parseObject('posts'); $recordInfo->delete($objectId);

return Redirect::action('AdminPostsController@getIndex')

->with('success','Your Post Has been deleted'); }catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


Creating Posts Controller: 34 We just need to add a link to the delete action to our read record view, so it will look like : 1 <a class="btn btn-danger btn-xs" title="Delete" href="{{URL::action('AdminPos\ 2 tsController@getDelete',$item->objectId)}}">

3 <span class="glyphicon glyphicon-trash"></span>

4 </a>

Store Action: Since we have learned that delete post can be done by simply creating an object of typeparseObject, so does the store function, the idea is simple:

1. We will need to create an object of typeparseObject. 2. Then add the data to it as attributes

But first, we need to create the form, and the code for doing so.

public function getAdd(){

return View::make('admin.posts.add'); }

And the html code for this view (admin/posts/add.blade.php) is: 1 @extends('admin.layout')


3 @section('body') 4 <div class="row">


6 @if(Session::has('success'))

7 <div class="alert alert-success">{{Session::get('success')}}</div>

8 @endif


10 @if(Session::has('error'))

11 <div class="alert alert-danger">{{Session::get('error')}}</div>

12 @endif


14 <ol class="breadcrumb">

15 <li><a href="{{URL::action('AdminDashboardController@getIndex')}}">Ho\ 16 me</a></li>

17 <li><a href="{{URL::action('AdminPostsController@getIndex')}}">ALL Po\ 18 sts</a></li>

19 <li class="active">Add New Post</li>

20 <li class="pull-right no-before">

21 <a href="{{URL::route('logout')}}">

22 Logout


Creating Posts Controller: 35 24 </li>

25 </ol>

26 <div class="panel panel-default widget">

27 <div class="panel-heading">

28 <span class="glyphicon glyphicon-list-alt"></span>

29 <h3 class="panel-title">

30 Add New Post</h3>

31 </div>

32 <div class="panel-body">

33 {{Form::open(array('action'=>'AdminPostsController@postAdd', 'rol\ 34 e'=>'form'))}}

35 <div class="form-group">

36 {{Form::label('title','Title :')}}

37 {{Form::text('title',null,array('id'=>'title','placeholder'=>\ 38 'Post Title', 'class'=>'form-control', 'required'=>'required'))}}

39 </div>

40 <div class="form-group">

41 {{Form::label('body','Body :')}}

42 {{Form::textarea('body',null,array('id'=>'body','placeholder'\ 43 =>'Post Body', 'class'=>'form-control', 'required'=>'required'))}}

44 </div>

45 {{Form::submit('Create',array('class'=>'btn btn-primary'))}}

46 {{Form::close()}}

47 </div>

48 </div>

49 </div>

50 @stop

Add New Post Form


Creating Posts Controller: 36

<div class="add-new center-block"

<a class="btn btn-default btn-sm" title="Add new Post" href="{{URL::action('A\ dminPostsController@getAdd')}}"

<span class="glyphicon glyphicon-plus-sign"></span>

Add new Post

</a> </div>

Now come the part where the real work starts, lets read the function, and i will explain it, trust me :

public function postAdd(){

// i will trust that you have send the data, and that you need to store it // as we said before we are here to talk about not laravel 4


// create parse object

$postData = new parseObject('posts');

// add the title to the object

$postData->title = Input::get('title');

// add the body to the object

$postData->body = Input::get('body');

// and make sure that the active is true

$postData->active = true;

// now send the object to parse and return the object id

$result = $postData->save();

return Redirect::action('AdminPostsController@getRecord', $result->ob\ jectId)->with('success','Your Post Has been added');

}catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


As you can see we created the class object, then we added the data to it, and then send it to, and when everything goes as we plan, we simply redirect it to the post view page to see the result.

Edit Action: Now the edit action is some how a combination of both, add and retrieve, because you will need to have the data to fill the form which you will use to edit the old data, so we have two functions, the first one to get the data and fill the form, the second one to send the updates to


Creating Posts Controller: 37

public function getEdit($objectId = null) {


return Redirect::to('/admin/posts')->with('error','You must select a \ record to edit');



$recordInfo = new parseQuery('posts'); $recordInfo->where('objectId',$objectId); $result = $recordInfo->find();

$data = array('item'=>$result->results[0]);

return View::make('admin.posts.edit')->with($data); }catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


We need to add a link to the Edit action into our read view (admin/posts/record.blade.php):

<a class="btn btn-primary btn-xs" title="Edit" href="{{URL::action('AdminPost\ sController@getEdit',$item->objectId)}}">

<span class="glyphicon glyphicon-pencil"></span> </a>

And the html code for the Edit View (admin/posts/edit.blade.php) is :



<div class="row">


<div class="alert alert-success">{{Session::get('success')}}</div> @endif


<div class="alert alert-danger">{{Session::get('error')}}</div> @endif

<ol class="breadcrumb">

<li><a href="{{URL::action('AdminDashboardController@getIndex')}}">Ho\ me</a></li>


Creating Posts Controller: 38 sts</a></li>

<li class="active">Edit {{$item->title}}</li> <li class="pull-right no-before">

<a href="{{URL::route('logout')}}">


</a> </li> </ol>

<div class="panel panel-default widget"> <div class="panel-heading">

<span class="glyphicon glyphicon-list-alt"></span> <h3 class="panel-title">

Edit : {{$item->title}}</h3> </div>

<div class="panel-body">

{{Form::open(array('action'=>'AdminPostsController@postEdit', 'ro\ le'=>'form'))}}

<div class="form-group">

{{Form::label('title','Title :')}}

{{Form::text('title',$item->title,array('id'=>'title','placeh\ older'=>'Post Title', 'class'=>'form-control', 'required'=>'required'))}}


<div class="form-group">

{{Form::label('body','Body :')}}

{{Form::textarea('body',$item->body,array('id'=>'body','place\ holder'=>'Post Body', 'class'=>'form-control', 'required'=>'required'))}}



{{Form::submit('Update',array('class'=>'btn btn-primary'))}} {{Form::close()}}

</div> </div> </div> @stop


Creating Posts Controller: 39

Edit Post Form

So when you hit the update button the data will be send to our controller action, which will send it to The function is similar to the add function, lets see it:

public function postEdit() {


$postData = new parseObject('posts'); $postData->title = Input::get('title'); $postData->body = Input::get('body'); $postData->active = true;

$result = $postData->update(Input::get('objectId'));

return Redirect::action('AdminPostsController@getRecord', Input::get(\ 'objectId'))

->with('success','Your Post Has been updated');

}catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


Our previous function was the last function of our Post Controller, and as you have see its so easy to work with RESTApi, its just a matter of reading the documentation and implementing a few tricks of your own.

The Parse PHP library contains so much contain so much information, and the best way to learn it is by reading the documentation first, then reading the code again and again till you figure out what each code does, and how you can improve it.


Creating Posts Controller: 40 In our next chapter, we are going to build the comment controller class, which is similar to the Posts controller, except that we need to have it connected with our posts.


Creating The Comments Controller

This chapter is going to be shorter than the old one, since most of what we are going to talk about has been mentioned in the last chapter, so i will try my best to make it short.

Creating The Comments Controller

As we have created our Posts controller, and we have created an empty controller and we have called itAdminCommentsController.php so now we need to extend the BaseController, as the following (just a note we need to add the same constructor code which we used in out posts controller):


class AdminCommentsController extends BaseController{

public function __construct() {

$this->perPage = Config::get('application.perPage');

$pageNo = (is_null(Input::only('page'))) ? 0 : Input::only('page'); $this->skip = (($this->perPage * ($pageNo['page'] - 1)) < 0) ? 0 : \ ($this->perPage * ($pageNo['page'] - 1));

} }

and we add it to our route file as in the following:

Route::group(array('before'=>'auth','prefix'=>'admin'),function(){ Route::controller('posts','AdminPostsController');

Route::controller('comments','AdminCommentsController'); Route::controller('dashboard','AdminDashboardController'); });

This is something we have already done in the last chapter, but its always nice to remember it.


Creating The Comments Controller 42

Building General Actions:

We are going to build the same functionality which we have built in our posts controller, which is:

• List action.

• Store action (will be coded in the next chapter). • Edit action.

• View record action. • Delete action.

You must have all the functions written in the controller, when you include them in your view file.

List Action:

As we have done before the list action consist of querying for our data, but we have just one simple difference here is that we have a pointer field, and we can get the content of this pointer field by just telling to include it in the returned results, like :

public function getIndex() {


$fullComments = new parseQuery('comments'); $fullComments->setCount(true);

//this is the important field, which also get the post data

$fullComments->whereInclude('post'); $fullComments->setLimit($this->perPage); $fullComments->setSkip($this->skip);

$fullComments->orderByDescending('createdAt'); $comments = $fullComments->find();

$paginator = Paginator::make($comments->results, $comments->count, $t\ his->perPage);

$data = array(

'items'=> $comments->results, 'paginator' => $paginator, 'total' => $comments->count


Creating The Comments Controller 43

return View::make('admin.comments.list')->with($data); }catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


And the Html code for this view is (admin/comments/list.blade.php):



<div class="row">


<div class="alert alert-success">{{Session::get('success')}}</div> @endif


<div class="alert alert-danger">{{Session::get('error')}}</div> @endif

<ol class="breadcrumb">

<li><a href="{{URL::action('AdminDashboardController@getIndex')}}\ ">Home</a></li>

<li class="active">Posts Comments</li> <li class="pull-right no-before">

<a href="{{URL::route('logout')}}">


</a> </li> </ol>

<div class="panel panel-default widget"> <div class="panel-heading">

<span class="glyphicon glyphicon-comment"></span> <h3 class="panel-title">

Posts Comments</h3>

<span class="label label-info">

{{$total}}</span> </div>

<div class="panel-body"> <ul class="list-group">

@foreach($items as $item)

<li class="list-group-item"> <div class="row">


Creating The Comments Controller 44

<div class="col-xs-2 col-md-1">

<img src="" class="img-\ circle img-responsive" alt="" /></div>

<div class="col-xs-10 col-md-11"> <div>

<a href="#">

Comment on {{ $item->post->title }}</\ a>

<div class="mic-info">

By: <a href="#">{{$item->authorName}}\


on {{date('d-M-Y',strtotime($item->cr\ eatedAt))}}

</div> </div>

<div class="comment-text">



<div class="action">

<a class="btn btn-primary btn-xs" title="\ View" href="#">

<span class="glyphicon glyphicon-eye-\ open"></span>



<a class="btn btn-success btn-xs" title="\ Approved" href="#">

<span class="glyphicon glyphicon-ok">\


</a> @else

<a class="btn btn-danger btn-xs" title="U\ n Approve" href="#">

<span class="glyphicon glyphicon-remo\ ve"></span> </a> @endif </div> </div> </div> </li> @endforeach </ul> </div> </div> {{$paginator->links()}}


Creating The Comments Controller 45

</div> @stop

Listing All Comments

Edit Action:

The edit action is what you do when you have some information you need to change, like changing a word or deleting some information, but the most important is how you add the value for the pointer ?

You have two choices:

• Never change the value, and so it will not change.

• Just handle it (and this is the case when adding or when changing the value like linking the comment to another post).

I have choose to handle it even though its not going to change, so that you can see how its going to be coded.

public function getEdit($objectId = null) {



return Redirect::action('AdminCommentsController@getIndex')->with\ ('error','Choose a comment to edit');


$commentRecord = new parseQuery('comments'); $commentRecord->where('objectId', $objectId); $commentRecord->whereInclude('post');

$commentRecord->setLimit(1); $result = $commentRecord->find();

$result->results[0]->approved = ($result->results[0]->approved) ? 1 :\



Creating The Comments Controller 46 'item'=> $result->results[0],


return View::make('admin.comments.edit')->with($data);

}catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


public function postEdit() {


$oldComment = new parseObject('comments');

$oldComment->authorName = Input::get('authorName'); $oldComment->authorEmail = Input::get('authorEmail'); $oldComment->commentBody = Input::get('commentBody');

$oldComment->approved = (Input::get('approved') == 1) ? true : false; $oldComment->post = $oldComment->dataType('pointer',array('posts', In\ put::get('postId') ));

$result = $oldComment->update(Input::get('objectId'));

return Redirect::action('AdminPostsController@getRecord', Input::get(\ 'objectId'))

->with('success','The comment has been updated');

}catch(ParseLibraryException $e){

throw new Exception($e->getMessage(), $e->getCode()); }


Most importantly is how we add the pointer to the post, the library provide us with a simple functiondataTypewhich accept the type of the field as its first argument, and an array with the data, as its second argument. and the function is look like this :


Creating The Comments Controller 47

public function dataType($type,$params){

if($type != ''){


case 'date':

$return = array(

"__type" => "Date",

"iso" => date("c", strtotime($params)) );


case 'bytes':

$return = array(

"__type" => "Bytes",

"base64" => base64_encode($params) ); break; case 'pointer': $return = array( "__type" => "Pointer", "className" => $params[0], "objectId" => $params[1] ); break; default: $return = false; break; } return $return; } }

I have removed some of the types to make the function short to read for now, since the only thing which matter for us is thepointerdata type which connect our comment with the posts class, and the retuned data will look something like :


"__type": "Pointer", "className": "Posts", "objectId": "Ed1nuqPvc" }


Creating The Comments Controller 48



<div class="row">


<div class="alert alert-success">{{Session::get('success')}}</div> @endif


<div class="alert alert-danger">{{Session::get('error')}}</div> @endif

<ol class="breadcrumb">

<li><a href="{{URL::action('AdminDashboardController@getIndex')}}">Ho\ me</a></li>

<li><a href="{{URL::action('AdminCommentsController@getIndex')}}">ALL\ Comments</a></li>

<li class="active">Edit Comment</li> <li class="pull-right no-before">

<a href="{{URL::route('logout')}}">


</a> </li> </ol>

<div class="panel panel-default widget"> <div class="panel-heading">

<span class="glyphicon glyphicon-list-alt"></span> <h3 class="panel-title">

Edit Comment</h3> </div>

<div class="panel-body">

{{Form::open(array('action'=>'AdminCommentsController@postEdit', \ 'role'=>'form'))}}

<div class="form-group">

{{Form::label('authorName','Your Name :')}}

{{Form::text('authorName',$item->authorName,array('id'=>'auth\ orName','placeholder'=>'Your Name', 'class'=>'form-control', 'required'=>'req\ uired'))}}


<div class="form-group">

{{Form::label('authorEmail','Your Email :')}}

{{Form::text('authorEmail',$item->authorEmail,array('id'=>'au\ thorEmail','placeholder'=>'Your Email', 'class'=>'form-control', 'required'=>\ 'required'))}}


Creating The Comments Controller 49

<div class="form-group">

{{Form::label('commentBody','Comment :')}}


>'commentBody','placeholder'=>'Comment Body', 'class'=>'form-control', 'requi\ red'=>'required'))}}


<div class="form-group">

{{Form::label('approved','Approved :')}}

{{Form::select('approved',array('1'=>'Approved','0'=>'Waiting\ Approval'),$item->approved,array('id'=>'approved','class'=>'form-control'))}\ }


{{Form::hidden('postsId',$item->post->objectId)}} {{Form::hidden('objectId',$item->objectId)}}

{{Form::submit('Reply',array('class'=>'btn btn-primary'))}} {{Form::close()}}

</div> </div> </div> @stop

Edit Comment Form

View Record Action:

Viewing the record consists of just querying to get the data, but we are going to include the posts record with it like:



Outline : Parse Query class