108

Building Web Applications Using Parse Rest API

Embed Size (px)

DESCRIPTION

Laravel Framework

Citation preview

Page 1: Building Web Applications Using Parse Rest API
Page 2: Building Web Applications Using Parse Rest API

Building Web Applications Using Parse RESTAPIUsing Laravel V4.x to build a simple blog

Mhd Zaher Ghaibeh

This book is for sale at http://leanpub.com/building-web-applications-using-parse-rest-api

This version was published on 2015-01-10

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight toolsand many iterations to get reader feedback, pivot until you have the right book and buildtraction once you do.

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

Page 3: Building Web Applications Using Parse Rest API

Tweet This Book!Please help Mhd Zaher Ghaibeh by spreading the word about this book on Twitter!

The suggested tweet for this book is:

I just bought the book Building Web Applications Using Parse REST APIhttp://s.zah.me/1qWXTwX

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 thishashtag on Twitter:

https://twitter.com/search?q=#laravelandparseit_english

Page 4: Building Web Applications Using Parse Rest API

Contents

Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2What Is Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2What Is Parse Data? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3Now What? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Creating Parse.com Data Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Data Types: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Creating Your Classes: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

User Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Setting Up The Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Creating The Migration File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Creating The Seed File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17Setting up the Layout template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19Creating The Authentication Controller . . . . . . . . . . . . . . . . . . . . . . . . . . 19

Creating Posts Controller: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23Configure Parse.com Library: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23Testing Our Parse.com Configuration: . . . . . . . . . . . . . . . . . . . . . . . . . . . 24Creating Admin Posts Controller: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

Creating The Comments Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41Creating The Comments Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41Building General Actions: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

Putting everything together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54Posts Missing functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54Comments Missing functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56Front-end Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64getPost Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

Refactoring the posts controller, to use Models . . . . . . . . . . . . . . . . . . . . . . 70Create the Posts Model Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70Create getPosts Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72Create getPost Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74Creating deleteItem Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77Create handleItem Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

Page 5: Building Web Applications Using Parse Rest API

CONTENTS

Refactoring the comments controller, to use Models . . . . . . . . . . . . . . . . . . . 83The Comments Model Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83getComments Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83getComment Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85getPostComments Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86deleteItem Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87handleItem Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Mastering Parse Query Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91How to use parse query class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

Learning Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98More about Parse Products . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98More about Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

Preparing your production server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99Creating Your Laravel & nginx Server . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

Final Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

Page 6: Building Web Applications Using Parse Rest API

Forewordby Mhd Zaher Ghaibeh

First of all I have to say thanks for everyone who encourage me to start this small journey andtype this beginners book, about how to use and benefit from Parse on your next web project,and I should also say thank for Boydlee Pollentine¹ who encourage me to read about Parse, andknow how to use it with web applications, not only for mobiles, and also to my oldest friendHala Deeb² who always encourage me to not limit my imagination.

Who Am I?

I am the Co-founder of Creative Web Group Syria³, a web development startup that specializesin developing modern web applications and utilizing the latest web development technologiesand methodologies. I have 8 years of web development experience and holds a Bachelor OfInformation Technology from the Syrian University, Damascus. I am currently working withTipsy & Tumbler Limited⁴ as Lead developer.

Thank You

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

Contact Info

Name : Mhd Zaher Ghaibeh

Email : [email protected]

Blog : http://www.zah.me⁶ / Arabic Blog

¹http://boydlee.com/²http://www.haladeeb.name/³http://creativewebgroup-sy.com/⁴http://www.tipsyandtumbler.co.uk/⁵[email protected]⁶http://www.zah.me

Page 7: Building Web Applications Using Parse Rest API

IntroductionWhat Is Laravel

Quoting from Laravel Documentation⁷:

Laravel Philosophy

Laravel is a web application framework with expressive, elegant syntax. We believedevelopment must be an enjoyable, creative experience to be truly fulfilling. Laravelattempts to take the pain out of development by easing common tasks used in themajority of web projects, such as authentication, routing, sessions, and caching.

Laravel aims to make the development process a pleasing one for the developerwithout 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 otherweb frameworks, including frameworks implemented in other languages, such asRuby on Rails, ASP.NET MVC, and Sinatra.

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

To simplify the idea, Laravel is a powerful web application framework, which will help you tocreate 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 many Symfony2⁸ 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 whichyou can search for on Packagist⁹.

• Built with testing in mind.• Great Community.

⁷http://laravel.com/docs⁸http://www.symfony.com⁹http://packagist.org

Page 8: Building Web Applications Using Parse Rest API

Introduction 3

Please Note:This book is not intended to teach you Laravel, this book is going to demonstrate howyou can use and interact with Parse Data¹⁰ from Parse.com¹¹. If your looking for aresources to learn Laravel 4, you can check Dayle Rees¹² book Code Bright¹³.

Installing Laravel 4

There are so many ways to install Laravel, the one which I really like is to go to Laravel website¹⁴,download the latest version and extract it on your computer, Open up terminal if you’re on aMac or Command Prompt if you’re using Windows, navigate to the directory where you haveextracted 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 from http://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 dothat, click here¹⁶ (I will use jasmine.dev here to reference to the blog app).

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

What Is Parse Data?

Quoting from Parse.com¹⁷ website:

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

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

¹⁰https://parse.com/products/data#howitworks¹¹https://parse.com/¹²https://twitter.com/daylerees¹³https://leanpub.com/codebright¹⁴http://laravel.com¹⁵http://www.getcomopser.org¹⁶http://httpd.apache.org/docs/2.2/vhosts/examples.html¹⁷http://www.parse.com/products/data

Page 9: Building Web Applications Using Parse Rest API

Introduction 4

Why Using Parse.com

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

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

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

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

• Easy Scalability and extendability by simply adding new feature for your application, forexample you can use Parse Social²² to easily integrate your application with Facebook andTwitter, which

You can take a look at Parse Customers²³ page to get around and see what other developers hasbeen using Parse Products²⁴ for.

Parse.com 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 yourcontent without writing a single line of code.

Data Browser

• Advanced filtering directly from within the data browser.

¹⁸https://www.parse.com/products/data¹⁹https://www.parse.com/products/²⁰https://www.parse.com/products/hosting²¹https://www.parse.com/products/analytics²²https://www.parse.com/products/social²³https://www.parse.com/customers/featured²⁴https://www.parse.com/products

Page 10: Building Web Applications Using Parse Rest API

Introduction 5

Data Filtering

• Ability to interact with other Parse.com²⁵ products.

Signup With Parse.com

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

1. Go to Parse.com Signup 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.

²⁵http://parse.com/products/²⁶http://www.parse.com²⁷https://parse.com/#signup

Page 11: Building Web Applications Using Parse Rest API

Introduction 6

2. After you click the create button, you will have a small window, which will contain allyour keys which you will use to interact with Parse.com.

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 onthe web, otherwise people will be able to access your data.

Page 12: Building Web Applications Using Parse Rest API

Introduction 7

NowWhat?

Let’s see, we have installed Laravel 4, we have created an account on Parse.com, what else dowe need?

Actually we are missing just one component, which is the ‘PHP Parse.com Library. Sadly,Parse.com does not have such library, but that hasn’t stopped apotropaic²⁸ from creating one,Considering this is the only library recommended by Parse.comwebsite https://parse.com/docs/api_-libraries²⁹, this is what we are going to use, and we might modify it if we find anything we needto change or enhance.

How to install parse.com-php-library

The easiest way to install the library is by downloading it to our app directory, and adding it tocomposer 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 thecode of the library? Next time the librarywas updated, youwill lose yourmodifications.

• Download the library from apotropaic³⁰ github repo.• Extract the library in app/libraries/parse directory.• Edit composer.json so 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 :

²⁸https://github.com/apotropaic/parse.com-php-library²⁹https://parse.com/docs/api_libraries³⁰https://github.com/apotropaic/parse.com-php-library

Page 13: Building Web Applications Using Parse Rest API

Introduction 8

composer dump-autoload

and that’s it, we are done.

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

Page 14: Building Web Applications Using Parse Rest API

Creating Parse.com Data ClassesData Types:

To create the class, we need first to know the types of data which is available in Parse.com Data.

Here is a list with all of the data types within Parse.com Data:

1. Date: The Date type contains a field iso which contains a UTC timestamp stored in ISO8601 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 binarydata. The specific base64 encoding is the one used by MIME, and does not containwhitespace.

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 Parse.com

Hosting, in the free account you will have 1GB to upload your files to, but keep in mindthat the file should not exceed 10MB in size.

9. GeoPoint: The GeoPoint type is used with maps and geolocation data, so it will containthe 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 ourPosts 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 thosewho never worked with NOSQL before, and to be honest the simplest way for me todescribe it, is that the Class represent the Table in any RDMS, and the row record inRDMS is simply the equivalent for Object in Parse.com Data. So whenever we say letsget the object id, or lets get the object, make sure that you translate it in your mind likelets get the row id, or lets get the row record.

Page 15: Building Web Applications Using Parse Rest API

Creating Parse.com Data Classes 10

First of all, lets go to Parse.com³¹ and login to the dashboard, because we are going to use thedata browser to create our first class.

Parse.com Dashboard - Data Browser

To create our first class, we simply click on the top left blue button which say New Class, andthat 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 toonly begin with a letter.

Now that we have created our class, we should create the tables which will be contained withinthe 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.

³¹http://parse.com

Page 16: Building Web Applications Using Parse Rest API

Creating Parse.com 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 selectthe 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 thetitle 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 thatParse.com data already have some default columns which we can use? Whenever you create anew class Parse.com 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 itwill 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 whichyou create your record.

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

Page 17: Building Web Applications Using Parse Rest API

Creating Parse.com 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 addthe value of each column directly using the Data Browser, you will notice how much easy it isto 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 setit to false.

Undefined Value

By default, you can have one or all of your custom columns to have value, but this isnot a mandatory thing, I mean for example here, you can have a body for the post, butyour not required to have a title and when you retrieve the data, you will not get anytitle field and thats what they mean by undefined.

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

Comments Class:

Now that we’ve become familiar with the Data browser, I don’t think we need to get more intohow 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.• Comment body: yes, you guess it right, its also a String, which will hold the full comment.

Page 18: Building Web Applications Using Parse Rest API

Creating Parse.com Data Classes 13

• Approved: boolean type, which will indicate that the comment is approved by the blogauthor 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 aPointer, which will point to the the Post which the comment belong to (you can think ofit 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 surethat 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 itin the field.

Comment class with some data

Page 19: Building Web Applications Using Parse Rest API

User AuthenticationSetting Up The Database

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

app/config/database.php

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 to sqlite, to match thefollowing 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 the default valueto become sqlite.

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:

Page 20: Building Web Applications Using Parse Rest API

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 bedifferent than the one you will have cause it will use the current date and time forthe execution time, and as you can see am executing the command on 14/09/2013 at15:41:14

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

app/database/migrations/2013_09_14_154114_CreateUsersTable.php

<?php

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)

{

//

Page 21: Building Web Applications Using Parse Rest API

User Authentication 16

});

}

}

In simple words, the up function executed when you run the command:

php artisan migrate

And yes you guessed it right down function executed whenever you run one of the commands:

php artisan migrate:rollback

php artisan migrate:reset

php artisan migrate:refresh

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

public function down()

{

Schema::drop('users');

}

Now the users table 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 niceto add the created_at and updated_at fields for us, lets see how its going to be, first we need tomodify the up function to do the creation job for us.

public function up()

{

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

{

// adding the email field

$table->string('email')->unique()->nullable()->default(null);

// 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 the LaravelSchema³² documentation to read more about it.

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

³²http://laravel.com/docs/schema#adding-columns

Page 22: Building Web Applications Using Parse Rest API

User Authentication 17

php artisan migrate

If you got no error, then everything has went as it should and now we can continue seeding theusers database 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 itUsersTableSeeder.php

<?php

class UsersTableSeeder extends Seeder

{

public function run()

{

User::create(array(

'email' => '[email protected]',

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

));

}

}

Secondwe need to edit a file called DatabaseSeeder.phpwhich can be found under database\seedsdirectory, and edit it so that it has the following code:

Page 23: Building Web Applications Using Parse Rest API

User Authentication 18

<?php

class DatabaseSeeder extends Seeder {

public function run()

{

Eloquent::unguard();

$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 fileapp\filter.php which already has the required filter to authenticate our website visitor, andthose 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('/');

});

Page 24: Building Web Applications Using Parse Rest API

User Authentication 19

Setting up the Layout template

Weare going to use the latest version of Twitter Bootstrapwhich you can get fromhttp://www.getbootstrap.com³³,download it and put the content inside the assets folder under the public folder.

Nowwe need to create our layout.blade.php file, under the admin folder inside our views folder,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('http://code.jquery.com/jquery-1.10.1.min.js')}}

{{HTML::script('http://code.jquery.com/jquery-migrate-1.2.1.min.js')}}

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

<!--

http://bootsnipp.com/snippets/featured/google-style-login

http://bootsnipp.com/snippets/featured/recent-comments-admin-panel

http://bootsnipp.com/snippets/featured/admin-panel-quick-shortcuts

-->

</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 usedto create the forms, elements .. etc, We always should give the credits to the original creators ofthe codes.

Creating The Authentication Controller

Since this book is not going to teach you Laravel 4³⁴, we are going to create a simpleAuthentication Controller to handle the login/logout actions only. First lets create the Controllerfile under the app\controllers directory and add the following code:

³³http://www.getbootstrap.com³⁴http://www.laravel.com

Page 25: Building Web Applications Using Parse Rest API

User Authentication 20

<?php

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()

{

Auth::logout();

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

}

}

The first function is going to show a simple view which only have the login form, which we willuse 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 notthey will be redirected back to the login page, and the last function is used to logout the userfrom the system. Now in our routes.php we 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 our AuthController.

Here is the code for our simple login form, I am not a good designer, so I hope you can acceptthis for as simple as it is:

Page 26: Building Web Applications Using Parse Rest API

User Authentication 21

@extends('admin.layout')

@section('body')

<div class="row">

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

<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="https://lh5.googleusercontent.com/-\

b0-k99FZlyE/AAAAAAAAAAI/AAAAAAAAAAA/eu7opA4byxI/photo.jpg?sz=120"

alt="">

{{Form::open(array('action'=>'AuthController@postLogin','id'=\

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

{{Form::text('email',Input::old('email'),

array('id'=>'email','placeholder'=>'[email protected]','class'=\

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

)}}

{{Form::password('password',

array('id'=>'password','class'=>'form-control','placeholder'=\

>'password')

)}}

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

btn-block'))}}

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

{{Form::close()}}

</div>

</div>

</div>

@stop

Page 27: Building Web Applications Using Parse Rest API

User Authentication 22

The Login Form

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

Page 28: Building Web Applications Using Parse Rest API

Creating Posts Controller:Configure Parse.com Library:

Since we have downloaded the recommended Parse.com library from apotropaic³⁵ repo, we neednow 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 thesame directory as our library, so for our case we need to create it under app/libraries/parsedirectory.

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

<?php

class parseConfig {

const APPID = 'D8s479IYdHR6uSFBYXsedjPANYNk8tZvfeOkji50';

const MASTERKEY = '4sFcF2UV9126M9x1X5Jh5px6NvmZHSqdTDMVNuki';

const RESTKEY = 'zCTQp4h2wLrjahIUVERkTsEi2156hKlHtCfYOE5P';

const PARSEURL = 'https://api.parse.com/1/';

}

Use your own keys,

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

Now if you opened the file app/libraries/parse/parse.php, you will notice that the creator ofthe 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';

³⁵https://github.com/apotropaic/parse.com-php-library

Page 29: Building Web Applications Using Parse Rest API

Creating Posts Controller: 24

Since we are using composer to autoload our classes we will not need to include any of thesefiles, so we can comment them out, or if you like you can delete them, I like to keep them just incase I have something to check later.

Testing Our Parse.com Configuration:

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

Route::get('/',function(){

$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 :

{

"results":

[{

"active":false,

"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 the RESTApi³⁶ in Parse.com, it recommended that you readthe documentation³⁷.

Creating Admin Posts Controller:

We are going to build our admin controllers, but we would like to make the urls to be likehttp://jasmin.dev/admin/controllername, and its really a simple things to accomplish usingLaravel routing mechanism, so lets edit our routes.php and just add the following to it:

³⁶https://parse.com/docs/rest#general³⁷https://parse.com/docs/rest#general

Page 30: Building Web Applications Using Parse Rest API

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 tostart with the easiest one, the View Action.

View Action: Its really a matter of querying Parse.com for a specific record, using the objectIdvalue, which we can get from Parse.com Data Browser, In my case it’s J9mLUW0heO.

In the parse.com php library, there is a class called parseQuery, this class is the one which weshould use when querying Parse.com 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 therecord, so lets see how the function should look like:

Page 31: Building Web Applications Using Parse Rest API

Creating Posts Controller: 26

public function getRecord($objectId = null)

{

if(is_null($objectId)){

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

record to view');

}

try{

$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 smallreview 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 whichParse.com class we want to query as a parameter.

$recordInfo->where('objectId',$objectId);

Then we told the object of the class, that we want to have only the records (since we don’t knowhowmany records it will return) whichmeets the required conditionwhere the objectId is equalto the given value. So where is used here to create this condition before send it to Parse.com.

$result = $recordInfo->find();

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

Page 32: Building Web Applications Using Parse Rest API

Creating Posts Controller: 27

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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')}}">

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="http://placehold.it/80" 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>

<div class="comment-text">

Page 33: Building Web Applications Using Parse Rest API

Creating Posts Controller: 28

{{$item->body}}

</div>

<div class="action">

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

" href="#">

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

</span>

</a>

@if(!$item->active)

<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">\

</span>

</a>

@endif

<a class="btn btn-danger btn-xs" title="Delet\

e" href="#">

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

/span>

</a>

<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

Page 34: Building Web Applications Using Parse Rest API

Creating Posts Controller: 29

The result of the query

Before we continue, there is a small bug in the parse.com library which prevent usfrom adding the limit,skip,order and include parameter if there was no conditions toour query.

Solving a bug in Parse.com library:

You should open the parseQuery.php file and edit the file to comment the lines 27 till 39 likethis:

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 theelse which we have commented out.

List Action: Now that we have become familiar with parseQuery, lets do the List action, whichwill 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 wedon’t going to list all the records at one time, to do so we made this simple calculation, and weadd it to our constructor function:

Page 35: Building Web Applications Using Parse Rest API

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 the Input class will return null ifthe variable is not defined, if so we don’t have to skip anything, if not we get the value of pagevariable minus one, and we multiply it by 10, since we need to show 10 records in each page. Bydoing that we get the value of the skipped records.

Second, we need to query Parse.com to get the data which we need, by providing it with theamount of records which it needs to skip, and another parameter which will help us to get thecount 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.

$records->setCount(true);

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

$records->setLimit($this->perPage);

// this is the skip parameter which we calculate.

$records->setSkip($this->skip);

// 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 createa 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()

{

try{

$records = new parseQuery('posts');

$records->setCount(true);

$records->setLimit($this->perPage);

$records->setSkip($this->skip);

$result = $records->find();

$paginator = Paginator::make($result->results,$result->count,$this->p\

Page 36: Building Web Applications Using Parse Rest API

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 :

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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')}}">

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>

Page 37: Building Web Applications Using Parse Rest API

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="http://placehold.it/80" 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>

<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>

@if(!$item->active)

<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="#">

Page 38: Building Web Applications Using Parse Rest API

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 easyto understand, all you have to do is to create an object of type parseObject and just execute thedelete function after providing it with the value of the object id which we want to delete. Thefull code for this function is:

public function getDelete($objectId = null){

if(is_null($objectId)){

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

to delete');

}

try{

$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());

}

}

Page 39: Building Web Applications Using Parse Rest API

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 objectof type parseObject, so does the store function, the idea is simple:

1. We will need to create an object of type parseObject.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')

2

3 @section('body')

4 <div class="row">

5

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

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

8 @endif

9

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

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

12 @endif

13

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

23 </a>

Page 40: Building Web Applications Using Parse Rest API

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

We also need to add a link to the Add action to our list view (admin/posts/list.blade.php)

Page 41: Building Web Applications Using Parse Rest API

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, trustme :

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 parse.com not laravel 4

try{

// 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 toparse.com, and when everything goes as we plan, we simply redirect it to the post view page tosee the result.

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

Page 42: Building Web Applications Using Parse Rest API

Creating Posts Controller: 37

public function getEdit($objectId = null)

{

if(is_null($objectId)){

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

record to edit');

}

try{

$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 :

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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\

Page 43: Building Web Applications Using Parse Rest API

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')}}">

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>

<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'))}}

</div>

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

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

{{Form::close()}}

</div>

</div>

</div>

@stop

Page 44: Building Web Applications Using Parse Rest API

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 sendit to Parse.com. The function is similar to the add function, lets see it:

public function postEdit()

{

try{

$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 itsso easy to work with Parse.com RESTApi, its just a matter of reading the documentation andimplementing a few tricks of your own.

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

Page 45: Building Web Applications Using Parse Rest API

Creating Posts Controller: 40

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

Page 46: Building Web Applications Using Parse Rest API

Creating The Comments Controller

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

Creating The Comments Controller

As we have created our Posts controller, and we have created an empty controller and we havecalled it AdminCommentsController.php so now we need to extend the BaseController, as thefollowing (just a note we need to add the same constructor code which we used in out postscontroller):

<?php

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 toremember it.

Page 47: Building Web Applications Using Parse Rest API

Creating The Comments Controller 42

Building General Actions:

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

• 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 inyour view file.

List Action:

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

public function getIndex()

{

try{

$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

);

Page 48: Building Web Applications Using Parse Rest API

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):

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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')}}">

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">

Page 49: Building Web Applications Using Parse Rest API

Creating The Comments Controller 44

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

<img src="http://placehold.it/80" 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}}\

</a>

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

eatedAt))}}

</div>

</div>

<div class="comment-text">

{{$item->commentBody}}

</div>

<div class="action">

<a class="btn btn-primary btn-xs" title="\

View" href="#">

<span class="glyphicon glyphicon-eye-\

open"></span>

</a>

@if(!$item->approved)

<a class="btn btn-success btn-xs" title="\

Approved" href="#">

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

</span>

</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()}}

Page 50: Building Web Applications Using Parse Rest API

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, likechanging a word or deleting some information, but the most important is how you add thevalue 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 linkingthe comment to another post).

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

public function getEdit($objectId = null)

{

try{

if(is_null($objectId)){

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 :\

0;

$data = array(

Page 51: Building Web Applications Using Parse Rest API

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()

{

try{

$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 simplefunction dataType which accept the type of the field as its first argument, and an array with thedata, as its second argument. and the function is look like this :

Page 52: Building Web Applications Using Parse Rest API

Creating The Comments Controller 47

public function dataType($type,$params){

if($type != ''){

switch($type){

case 'date':

$return = array(

"__type" => "Date",

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

);

break;

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 onlything which matter for us is the pointer data type which connect our comment with the postsclass, and the retuned data will look something like :

{

"__type": "Pointer",

"className": "Posts",

"objectId": "Ed1nuqPvc"

}

And the HTML view code for this page (admin/comments/edit.blade.php) is:

Page 53: Building Web Applications Using Parse Rest API

Creating The Comments Controller 48

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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')}}">

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>

<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'))}}

</div>

Page 54: Building Web Applications Using Parse Rest API

Creating The Comments Controller 49

<div class="form-group">

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

{{Form::textarea('commentBody',$item->commentBody,array('id'=\

>'commentBody','placeholder'=>'Comment Body', 'class'=>'form-control', 'requi\

red'=>'required'))}}

</div>

<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'))}\

}

</div>

{{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 Parse.com to get the data, but we are going to includethe posts record with it like:

Page 55: Building Web Applications Using Parse Rest API

Creating The Comments Controller 50

public function getRecord($objectId = null)

{

if(is_null($objectId)){

return Redirect::action('AdminCommentsController@getIndex')->with('er\

ror','Choose a comment to view');

}

try{

$commentRecord = new parseQuery('comments');

$commentRecord->where('objectId', $objectId);

$commentRecord->whereInclude('post');

$commentRecord->setLimit(1);

$result = $commentRecord->find();

$data = array(

'item'=> $result->results[0],

);

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

}catch(ParseLibraryException $e){

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

}

}

Below you can read our html template code (admin/comments/record.blade.php):

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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')}}">Pos\

ts Comments</a></li>

Page 56: Building Web Applications Using Parse Rest API

Creating The Comments Controller 51

<li>Comment on Post {{$item->post->title}}</li>

</ol>

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

<div class="panel-heading">

<span class="glyphicon glyphicon-comment"></span>

<h3 class="panel-title">

Comment on Post {{$item->post->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="http://placehold.it/80" class="img-circ\

le img-responsive" alt="" /></div>

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

<div>

<a href="{{URL::action('AdminPostsController@\

getRecord',$item->post->objectId)}}">

{{ $item->post->title }}</a>

<div class="mic-info">

By: <a href="{{URL::action('AdminComments\

Controller@getRecord',$item->objectId)}}">{{$item->authorName}}</a>

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

dAt))}}

</div>

</div>

<div class="comment-text">

{{$item->commentBody}}

</div>

<div class="action">

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

" href="{{URL::action('AdminCommentsController@getEdit',$item->objectId)}}">

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

</span>

</a>

@if(!$item->approved)

<a class="btn btn-success btn-xs" title="Appr\

oved" href="#">

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

an>

</a>

@else

<a class="btn btn-danger btn-xs" title="Un Ap\

Page 57: Building Web Applications Using Parse Rest API

Creating The Comments Controller 52

prove" href="#">

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

</span>

</a>

@endif

<a class="btn btn-danger btn-xs" title="Delet\

e" href="{{URL::action('AdminCommentsController@getDelete',$item->objectId)}}\

">

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

/span>

</a>

</div>

</div>

</div>

</li>

</ul>

</div>

</div>

</div>

@stop

View Comment

Delete Action:

Delete is some how the easiest function every, besides the query one, since we going just to deletethe comment record based on the record id, and then redirect it back to the list action, like:

public function getDelete($objectId = null)

{

if(is_null($objectId)){

return Redirect::action('AdminCommentsController@getIndex')->with('er\

ror','Choose a comment to delete');

}

try{

$recordInfo = new parseObject('comments');

$recordInfo->delete($objectId);

Page 58: Building Web Applications Using Parse Rest API

Creating The Comments Controller 53

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

->with('success','The comment Has been deleted');

}catch(ParseLibraryException $e){

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

}

}

Now that we have finished most of the functions related to both posts and comments, in thenext chapter we are going to add some of the missing functionality for both the posts and thecomments, like:

• Function to View comments related to a specific post.• Function to add a reply to comments on a specific post.• Function for our public index page.• Function for our public post page.• Function for adding comments on a specific post.• and more maybe ..

Page 59: Building Web Applications Using Parse Rest API

Putting everything togetherNow that we have built our administration section, we need to have add some functionalitybefore we move on to create our front-end.

Posts Missing functionality

We have only one missing functionality which we must add to our Posts controller which is toPublish/Un-Publish a specific post depending on the post object id.

Publish/Un-Publish a Specific Post

Since we have a field in our posts class which indicate if the posts must be publish or not (theactive field), updating the field value will reflect on our posts status, and as we have seen beforein the edit post we can do it directly by interacting with parseObject, so changing the value fromtrue to false and vise versa is what we need, lets see the code:

public function getHide($objectId = null){

if(is_null($objectId)){

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

->with('error','You must select a record to un-publish');

}

try{

$recordInfo = new parseObject('posts');

$recordInfo->active = false;

$recordInfo->update($objectId);

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

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

}catch(ParseLibraryException $e){

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

}

}

//Changing the status of the Post to Published

public function getPublish($objectId = null){

if(is_null($objectId)){

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

->with('error','You must select a record to publish');

}

Page 60: Building Web Applications Using Parse Rest API

Putting everything together 55

try{

$recordInfo = new parseObject('posts');

$recordInfo->active = true;

$recordInfo->update($objectId);

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

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

}catch(ParseLibraryException $e){

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

}

}

We now add the links to our Views. I will not put the full view code here, since we will have thefull code published on Github³⁸ under the MIT license.

@if($item->active)

<a class="btn btn-success btn-xs" title="Published" href="{{URL::action('Admi\

nPostsController@getHide',$item->objectId)}}">

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

</a>

@else

<a class="btn btn-danger btn-xs" title="Un-Publish" href="{{URL::action('Admi\

nPostsController@getPublish',$item->objectId)}}">

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

</a>

@endif

If we clicked on the green button to un-publish a post, the result will be

Un-Publish a post

³⁸https://github.com/linuxjuggler/laravel-and-parse-book

Page 61: Building Web Applications Using Parse Rest API

Putting everything together 56

And if we clicked on the red button to publish/republish a post the result will be

Publish a post

Comments Missing functionality

Our Comments controller is missing a few pieces of functionality:

1. Getting comments for a specific post.2. Reply to a comment for a specific post.3. Approve/Un-Approve a comment.

Getting Comments for a specific post

Our Comments controller is missing the ability to get the comments for a specific post, rightnow it only display all the comments which we have got, but this is not a functionality specificto the comments itself, it is related to the posts controller too, but since we are going to have ourcontroller to handle only one class, we will add the code in here, and linked it in the post recordview. In the comments controller, we are going to add two function, one public, and this is theone which will display the comments, and one private function to get the name of the post ifthere is no comments related to this post.

public function getPostComments($postObjectId = null)

{

try{

if(is_null($postObjectId)){

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

->with('error','You must select a post to view');

}

$fullComments = new parseQuery('comments');

$fullComments->setCount(true);

Page 62: Building Web Applications Using Parse Rest API

Putting everything together 57

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

$fullComments->whereInclude('post');

$fullComments->where('post',$fullComments->dataType('pointer',arr\

ay('posts',$postObjectId)));

$fullComments->setLimit($this->perPage);

$fullComments->setSkip($this->skip);

$fullComments->orderByDescending('createdAt');

$comments = $fullComments->find();

if($comments->count == 0){

$postName = $this->_getPostName($postObjectId);

}else{

$postName = $comments->results[0]->post->title;

}

$paginator = Paginator::make($comments->results, $comments->count\

, $this->perPage);

$data = array(

'items'=> $comments->results,

'paginator' => $paginator,

'total' => $comments->count,

'postName' => $postName

);

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

}catch(ParseLibraryException $e){

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

}

}

private function _getPostName($objectId = null)

{

if(is_null($objectId)){

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

->with('error','You must select a record to view');

}

try{

$recordInfo = new parseQuery('posts');

$recordInfo->where('objectId',$objectId);

$result = $recordInfo->find();

if(!empty($result->results)){

Page 63: Building Web Applications Using Parse Rest API

Putting everything together 58

return $result->results[0]->title;

}

throw new Exception('No Records found');

}catch(ParseLibraryException $e){

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

}

}

And the html view code is

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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">{{ $postName }} Comments</li>

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

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

Logout

</a>

</li>

</ol>

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

<div class="panel-heading">

<span class="glyphicon glyphicon-comment"></span>

<h3 class="panel-title">

`{{ $postName }}` Comments</h3>

<span class="label label-info">

{{$total}}</span>

Page 64: Building Web Applications Using Parse Rest API

Putting everything together 59

</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="http://placehold.it/80" class="img-circ\

le img-responsive" alt="" /></div>

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

<div>

<a href="{{URL::action('AdminPostsController@\

getRecord',$item->post->objectId)}}">

{{ $item->post->title }}</a>

<div class="mic-info">

By: <a href="{{URL::action('AdminComments\

Controller@getRecord',$item->objectId)}}">{{$item->authorName}}</a>

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

dAt))}}

</div>

</div>

<div class="comment-text">

{{$item->commentBody}}

</div>

<div class="action">

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

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

>

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

"></span>

</a>

@if(!$item->approved)

<a class="btn btn-success btn-xs" title="Appr\

oved" href="#">

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

an>

</a>

@else

<a class="btn btn-danger btn-xs" title="Un Ap\

prove" href="#">

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

</span>

</a>

@endif

</div>

</div>

Page 65: Building Web Applications Using Parse Rest API

Putting everything together 60

</div>

</li>

@endforeach

</ul>

</div>

</div>

{{$paginator->links()}}

</div>

@stop

And we need to make sure that we have the code below in our admin/posts/record.blade.phpfile

<a class="btn btn-primary btn-xs" title="View Post Comments" href="{{URL::act\

ion('AdminCommentsController@getPostComments',$item->objectId)}}">

<span class="glyphicon glyphicon-comment"></span>

</a>

Reply to a comment for a specific post

Last chapter, we mentioned the store functionality but we said that we will talk about it in thischapter, so adding a reply to a comment is as easy as adding a post to Parse³⁹, and its the same forany object you create in any class, except that we need to tell Parse that the post id is a pointerto an object in the Posts class, and so the code will be like :

$comments->post = $comments->dataType('pointer',array('posts','J9mLUW0heO'));

And the full code for it is like

public function getAdd($postObjectId = null)

{

if(is_null($postObjectId)){

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

->with('error','Choose a post first');

}

return View::make('admin.comments.add');

}

public function postAdd(){

try{

$comment = new parseObject('comments');

$comment->authorName = Input::get('authorName');

$comment->authorEmail = Input::get('authorEmail');

³⁹http://Parse.com

Page 66: Building Web Applications Using Parse Rest API

Putting everything together 61

$comment->commentBody = Input::get('commentBody');

// as you can see we specify that the postId is a pointer.

$comment->post = $comment->dataType('pointer',array('posts',Input\

::get('postId')));

$comment->approved = true;

$result = $comment->save();

return Redirect::action('AdminPostsController@getRecord', $result\

->results[0]->objectId)

->with('success','Your comment has been added');

}catch(ParseLibraryException $e){

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

}

}

And the html code for the view is

@extends('admin.layout')

@section('body')

<div class="row">

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

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

@endif

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

<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">Add New Reply</li>

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

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

Logout

</a>

</li>

</ol>

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

<div class="panel-heading">

Page 67: Building Web Applications Using Parse Rest API

Putting everything together 62

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

<h3 class="panel-title">

Add New Post</h3>

</div>

<div class="panel-body">

{{Form::open(array('action'=>'AdminCommentsController@postAdd', '\

role'=>'form'))}}

<div class="form-group">

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

{{Form::text('authorName',null,array('id'=>'authorName','plac\

eholder'=>'Your Name', 'class'=>'form-control', 'required'=>'required'))}}

</div>

<div class="form-group">

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

{{Form::text('authorEmail',Auth::user()->email,array('id'=>'a\

uthorEmail','placeholder'=>'Your Email', 'class'=>'form-control', 'required'=\

>'required'))}}

</div>

<div class="form-group">

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

{{Form::textarea('commentBody',null,array('id'=>'commentBody'\

,'placeholder'=>'Comment Body', 'class'=>'form-control', 'required'=>'require\

d'))}}

</div>

{{Form::hidden('postId',$postObjectId)}}

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

{{Form::close()}}

</div>

</div>

</div>

@stop

Approve/Un-Approve a comment

The default functionality is that when a guest submits their comment, it will be added but notpublished until the admin approve it. Sometimes after you approve a comment you realize thatyou need to hide it or un-publish it, so our functionality here is similar to the one we have in theposts controller, so without any further explanation let us read the code

Page 68: Building Web Applications Using Parse Rest API

Putting everything together 63

public function getHide($objectId = null)

{

if(is_null($objectId)){

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

->with('error','Choose a comment to un-publish');

}

try{

$recordInfo = new parseObject('comments');

$recordInfo->approved = false;

$recordInfo->update($objectId);

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

->with('success','The comment Has been un-published.');

}catch(ParseLibraryException $e){

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

}

}

public function getPublish($objectId = null)

{

if(is_null($objectId)){

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

->with('error','Choose a comment to publish');

}

try{

$recordInfo = new parseObject('comments');

$recordInfo->approved = true;

$recordInfo->update($objectId);

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

->with('success','The comment Has been approved.');

}catch(ParseLibraryException $e){

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

}

}

And we need to add the links to our comments view, for simplicity i will only include the htmlcode for the links

Page 69: Building Web Applications Using Parse Rest API

Putting everything together 64

@if($item->approved)

<a class="btn btn-success btn-xs" title="Approved" href="{{URL::action('Admin\

CommentsController@getHide',$item->objectId)}}">

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

</a>

@else

<a class="btn btn-danger btn-xs" title="Un Approve" href="{{URL::action('Admi\

nCommentsController@getPublish',$item->objectId)}}">

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

</a>

@endif

Front-end Functionality

Our Home Controller will have only three main functionality

1. Index function (getIndex), to get all of our posts to the front page.2. Get Post function (getPost), to get the information about a specific post.3. Posting a comment function (postAddComment), to handle the adding guest comments

on a specific post.

Now lets see how the home page is going to look like:

Page 70: Building Web Applications Using Parse Rest API

Putting everything together 65

Jasmine Blog - Home Page

Front-end Html Code

since the code is going to be located on Github⁴⁰, am not going to put the html codeand make reading the code boring for us, i will put a small amount of it, and once yourdone reading this chapter you can go over Github⁴¹ and read the code there.

⁴⁰https://github.com/linuxjuggler/laravel-and-parse-book⁴¹https://github.com/linuxjuggler/laravel-and-parse-book

Page 71: Building Web Applications Using Parse Rest API

Putting everything together 66

getIndex Function

The code here is simple, all we need to do is to query Parse to get the posts that are publishedand order them by creation date in descending order. We will also create the paginator, but letsfirst prepare some of the general code within our constructor function.

<?php

class HomeController extends BaseController {

protected $perPage;

protected $skip;

public function __construct()

{

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

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

$this->skip = (is_null($pageNo)) ? 0 : ( $this->perPage * ( $pageNo -\

1)) ;

}

}

Now the code to get the Posts is somethingwe already done beforewithin our AdminPostControllerclass, and its the same except that we need to get only the published posts.

public function getIndex()

{

try{

$posts = new parseQuery('posts');

$posts->setCount(true);

$posts->setLimit($this->perPage);

$posts->where('active', true);

$posts->setSkip($this->skip);

$posts->orderByDescending('createdAt');

$result = $posts->find();

$paginator = Paginator::make($result->results,$result->count,$this->per\

Page);

$data = array(

'posts' => $result->results,

'paginator' => $paginator,

'total' => $result->count);

return View::make('site.index')->with($data);

} catch(ParseLibraryException $e){

Page 72: Building Web Applications Using Parse Rest API

Putting everything together 67

throw new Exception($e->getMessage());

}

}

getPost Function

Our get post functionality will consist of two functions, the main one is inside the public functionand this will get the post from Parse, and the second one is private and it will get all the commentswhich related to this post and which is also approved

public function getPost($postId = null)

{

if(is_null($postId)){

return Redirect::back()->with('error','You cant access this file dire\

ctly.');

}

try{

$post = new parseQuery('posts');

$post->where('objectId', $postId);

$post->where('active', true);

$result = $post->find();

$comments = $this->_getComment($result->results[0]->objectId);

$data = array(

'post' => $result->results[0],

'comments' => $comments->results,

'commentsCount' => $comments->count);

return View::make('site.post')->with($data);

} catch(ParseLibraryException $e){

return Redirect::back()->with('error',$e->getMessage());

}

}

private function _getComment($postId = null)

{

try {

$comments = new parseQuery('comments');

$comments->setCount(true);

$comments->where('post', $comments->dataType('pointer', array('posts'\

, $postId)));

$comments->where('approved', true);

$comments->orderByDescending('createdAt');

$result = $comments->find();

return $result;

Page 73: Building Web Applications Using Parse Rest API

Putting everything together 68

} catch (ParseLibraryException $e) {

throw new Exception($e->getMessage());

}

}

Adding A Comment

We have done that before, yes the same functionality for replying on a post comment apply here,except that we need to make sure that the comment will not be automatically approved until weread it, the code is

public function postAddComment()

{

try {

$allData = Input::all();

$comment = new parseObject('comments');

$comment->authorName = Input::get('authorName');

$comment->authorEmail = Input::get('authorEmail');

$comment->commentBody = Input::get('commentBody');

$comment->post = $comment->dataType('pointer', array('posts', \

Input::get('postId')));

$comment->approved = false;

$result = $comment->save();

return Redirect::action('HomeController@getPost', Input::get('postId'\

))

->with('success', 'Your comment has been added, and waiting f\

or approval.');

} catch (ParseLibraryException $e) {

return Redirect::back()->with('error', $e->getMessage());

}

}

And our comment form look like

Page 74: Building Web Applications Using Parse Rest API

Putting everything together 69

Post Comments Form

Conclusion

So far we have created a simple blog system, which depends on Parse Data⁴² as the databasefor our posts/comments, in the next few chapters we will see how to refactor our Controllers tocreate our Posts/Comments class which will interact with Parse instead of interacting with Parsedirectly via the controllers. There is nothing wrong with that but also there is nothing wrongwith having your Parse Logic away from your controllers.

Also we will try to check for what the missing functionality inside the PHP Parse Library andhow we can add it to our benefit.

⁴²http://parse.com

Page 75: Building Web Applications Using Parse Rest API

Refactoring the posts controller, touse ModelsAs we have found in the past few chapters, we have had to create some of the functionality overand over again, for example getting the posts in the admin section and getting the posts in thefront-page.

As a good OOP practice, we will have to create a unified class which has only one functionality,which is to communicate with our posts class in Parse.com.

In Laravel, there are many ways to do that, for example we can create our own package, we cancreate a model class, or we can create our own classes and use them inside our controllers, sothere is no limitation to what we can do, we just need to do what we find suitable to our project.

Here, we are going to create our own model class, since at the end we are using Parse.com as ourdatabase.

Create the Posts Model Class

If we open our Model directory we will not see anything special except our User class whichwas created by Laravel for us as an example of how to handle the users authentications, and ourstructure will be something like the image:

Page 76: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 71

Our Application Structure

Page 77: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 72

Now all we need to do is to create a new Model class and we are going to follow the convenientnaming method which Laravel⁴³ use for its model classes, so the name of the model class willbe singular Post meanwhile the name of our Parse.com class is plural posts. So our file will beapp/models/Post.php and it will look like :

1 <?php

2 class Post {

3 protected $tablename = 'posts';

4 }

As you can see, we have just define a normal class called Post and a protected variable$tablename which define our Parse.com class name.

Create getPosts Function

Now the first functionality we need to create is to get all the posts from Parse.com, since thisfunctionality is used in both admin area, and guests area we can combine them in one functionlike this :

1 /**

2 * @param null $active

3 * @param int $limit

4 * @param int $skip

5 * @param string $orderBy

6 * @return bool|mixed

7 * @throws Exception

8 */

9 public function getPosts($active = null, $limit = 5, $skip = 0, $orderBy ='cr\

10 eatedAt' )

11 {

12 try{

13 $records = new parseQuery($this->tablename);

14

15 if(!is_null($active)){

16 $records->whereEqualTo('active', $active);

17 }

18

19 $records->orderByDescending('createdAt');

20 $records->setCount(true);

21 $records->setLimit($limit);

22 $records->setSkip($skip);

23 $result = $records->find();

24

⁴³http://laravel.com/docs/eloquent#basic-usage

Page 78: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 73

25 return $result;

26

27 } catch(ParseLibraryException $e){

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

29 }

30 }

This function will have a default parameters which we need to use for retrieving our records,for example the default values here indicate that we need to get the first 5 records starting fromrecord one ordered descending by the field createdAt. Since we set the active to null we willnot filter the posts based on the status of the post and this something important for the admin,to see all the records no matter if its published or not.

Now lets see the impact of this function on our getIndex function in AdminPostsController

class.

1 public function getIndex()

2 {

3 try{

4 //$records = new parseQuery('posts');

5 //$records->setCount(true);

6 //$records->setLimit($this->perPage);

7 //$records->setSkip($this->skip);

8 //$result = $records->find();

9

10 $posts = new Post();

11 $result = $posts->getPosts(null,$this->perPage,$this->skip);

12

13 $paginator = Paginator::make($result->results,$result->count,$this->p\

14 erPage);

15

16 $data = array(

17 'items'=> $result->results,

18 'paginator' => $paginator,

19 'total' => $result->count

20 );

21

22 return View::make('admin.posts.list')->with($data);

23

24 } catch(Exception $e){

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

26 }

27 }

As we can see, we have replaced five lines of code with only two lines. And the reflection ongetIndex function in HomeController class is :

Page 79: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 74

1 public function getIndex()

2 {

3 try {

4 //$posts = new parseQuery('posts');

5 //$posts->setCount(true);

6 //$posts->setLimit($this->perPage);

7 //$posts->where('active', true);

8 //$posts->setSkip($this->skip);

9 //$posts->orderByDescending('createdAt');

10 //$result = $posts->find();

11

12 $posts = new Post();

13 $result = $posts->getPosts(true, $this->perPage, $this->skip);

14

15 $paginator = Paginator::make($result->results, $result->count, $this-\

16 >perPage);

17

18 $data = array(

19 'posts' => $result->results,

20 'paginator' => $paginator,

21 'total' => $result->count

22 );

23

24 return View::make('site.index')->with($data);

25

26 } catch (Exception $e) {

27 throw new Exception($e->getMessage());

28 }

29 }

And as you can see, we have replaced seven lines of code with two lines, and we reused the samefunction which we created in our Post class.

Create getPost Function

We have here two options:

1. We can edit the getPosts function to handle also the functionality of getting one post byjust sending the objectId as parameter and check for it.

2. Or we can create separate function to do so, I tend to like this option, so I will do that, butfeel free to try the first option if you like.

To retrieve the post from Parse.comwe need to provide the objectId and since we don’t need theguests visitors from seeing or accessing non-published posts, we can provide a second parameterwhich define the status of the post, so the final code for this function will look like :

Page 80: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 75

1 /**

2 * @param $objectId the post object Id

3 * @param null $active the post status

4 * @return null

5 * @throws Exception

6 */

7 public function getItem($objectId, $active = null)

8 {

9 try{

10 $recordInfo = new parseQuery($this->tablename);

11 $recordInfo->where('objectId',$objectId);

12

13 if(!is_null($active))

14 $recordInfo->whereEqualTo('active',$active);

15

16 $result = $recordInfo->find();

17

18 if(!empty($result->results)){

19 return $result->results[0];

20 }

21

22 return null;

23

24 }catch(ParseLibraryException $e){

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

26 }

27 }

And the impact on our getRecord function from our AdminPostsController is :

1 public function getRecord($objectId = null)

2 {

3 if(is_null($objectId)){

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

5 ->with('error','You must select a record to view');

6 }

7

8 try{

9 //$recordInfo = new parseQuery('posts');

10 //$recordInfo->where('objectId',$objectId);

11 //$result = $recordInfo->find();

12

13 $recordInfo = new Post();

14 $result = $recordInfo->getItem($objectId);

15

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

Page 81: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 76

17 $data = array('item'=>$result);

18

19 return View::make('admin.posts.record')->with($data);

20

21 }catch(Exception $e){

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

23 }

24 }

And the impact on our getEdit function from our AdminPostsController is :

1 public function getEdit($objectId = null)

2 {

3 if(is_null($objectId)){

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

5 record to edit');

6 }

7

8 try{

9 //$recordInfo = new parseQuery('posts');

10 //$recordInfo->where('objectId',$objectId);

11 //$result = $recordInfo->find();

12

13 $recordInfo = new Post();

14 $result = $recordInfo->getItem($objectId);

15

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

17 $data = array('item'=>$result);

18

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

20

21 }catch(Exception $e){

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

23 }

24 }

As you can see not that much, but we gain the ability of using it in many other function likegetPost function from our HomeController class like this:

Page 82: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 77

1 public function getPost($postId = null)

2 {

3 if (is_null($postId)) {

4 return Redirect::back()->with('error', 'You cant access this file dir\

5 ectly.');

6 }

7

8 try {

9 //$post = new parseQuery('posts');

10 //$post->where('objectId', $postId);

11 //$post->where('active', true);

12 //$result = $post->find();

13

14 $post = new Post();

15 $result = $post->getItem($postId, true);

16

17 //$comments = $this->_getComment($result->results[0]->objectId);

18 $comments = $this->_getComment($result->objectId);

19 $data = array(

20 //'post' => $result->results[0],

21 'post' => $result,

22 'comments' => $comments->results,

23 'commentsCount' => $comments->count);

24 return View::make('site.post')->with($data);

25

26 } catch (Exception $e) {

27 return Redirect::back()->with('error', $e->getMessage());

28 }

29 }

As you can see, we have sent the active parameter to our function which will make sure thatwe will get the item if its published.

Can you spot what we have changed too??

We have changed the Exception which we catch in the AdminPostsController andHomeController from ParseLibraryException to the regular Exception since we arecatch it in in our Post class and throwing a normal Exception.

Creating deleteItem Function

From the name of the item, it will only have one which is to delete the record from Parse.com,but also we need to have a small trick to delete all the comments which belong to the post, sofor now you can ignore the section where delete the comments executed, and we will talk aboutit later in Comment Model section.

Page 83: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 78

1 /**

2 * @param $itemId post id

3 * @return bool

4 * @throws Exception

5 */

6 public function deleteItem($itemId)

7 {

8 try{

9

10 $comments = new Comment();

11 $commentsResult = $comments->getPostComments($itemId);

12 if($commetsResult->count > 0){

13 foreach($commetsResult->results as $item){

14 $comments->deleteComment($item->objectId);

15 }

16 }

17

18 $recordInfo = new parseObject($this->tablename);

19 $recordInfo->delete($objectId);

20

21 return true;

22

23 }catch(ParseLibraryException $e){

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

25

26 } catch(Exception $e){

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

28 }

29 }

Andwe need only to change a small code in the delete functionwithin our AdminPostsControllerlike this:

1 //$recordInfo = new parseObject('posts');

2 //$recordInfo->delete($objectId);

3 $recordInfo = new Post('posts');

4 $recordInfo->deleteItem($objectId);

Create handleItem Function

This function is going to be responsible of handling the following actions:

1. Create a new Post.2. Edit a Post.

Page 84: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 79

3. Change the status of a Post.

To make sure that the function will do exactly what we want it to do, we will send the input anda parameter to tell the function if we are editing or creating a record.

1 /**

2 * @param $input input data

3 * @param bool $isEdit to check if its update or create

4 * @return bool|mixed

5 * @throws Exception

6 */

7 public function handleItem($input, $isEdit = false)

8 {

9 try{

10 $postData = new parseObject($this->tablename);

11

12 if(isset($input['title']))

13 $postData->title = $input['title'];

14

15 if(isset($input['body']))

16 $postData->body = $input['body'];

17

18 $postData->active = (isset($input['active'])) ? $input['active'] : t\

19 rue;

20

21 if($isEdit){

22 $result = $postData->update($input['objectId']);

23 } else {

24 $result = $postData->save();

25 }

26

27 return $result;

28

29 }catch(ParseLibraryException $e){

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

31 }

32 }

So our first parameter is the input data which we want to use for creating or editing a record, ifwe are creating a new record we need only to send the input data, meanwhile if we are editinga record we must set the second parameter $isEdit to true which will call the update functionwithin the parseObject class.

And here is how to use this function within our AdminParseController class.

Create a Record

Let’s first recall how our postAdd function was written :

Page 85: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 80

1 public function postAdd(){

2 try{

3 $postData = new parseObject('posts');

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

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

6 $postData->active = true;

7

8 $result = $postData->save();

9

10 return Redirect::action('AdminPostsController@getRecord', $result->ob\

11 jectId)->with('success','Your Post Has been added');

12

13 }catch(ParseLibraryException $e){

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

15 }

16 }

As you can remember, we create an instance of parseObject class, we provided it with the dataand we send it to Parse.com, and lets see how it will be after using our Post class :

1 public function postAdd(){

2 try{

3 $post = new Post();

4 $result = $post->handleItem(Input::all());

5

6 return Redirect::action('AdminPostsController@getRecord', $result->ob\

7 jectId)->with('success','Your Post Has been added');

8

9 }catch(Exception $e){

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

11 }

12 }

As you can see, we have replaced the old five lines of code with only two lines of code, and it ismuch nicer and easier to understand don’t you agree.

Edit a Record

Editing a record looks exactly the same as creating new one, except that we need to call theupdate function instead of the save function, so the old code for this function was:

Page 86: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 81

1 public function postEdit()

2 {

3 try{

4 $postData = new parseObject('posts');

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

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

7 $postData->active = true;

8

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

10

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

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

13

14 }catch(ParseLibraryException $e){

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

16 }

17 }

And the new code has become:

1 public function postEdit()

2 {

3 try{

4 $post = new Post();

5 $result = $post->handleItem(Input::all(), true);

6

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

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

9

10 }catch(Exception $e){

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

12 }

13 }

And the result is the same as before, we have replaced five lines of code, with only two simplelines.

Publish/Un-Publish a Record

Our new functions has become more simpler and is now using the new function from Post classand the result is :

Page 87: Building Web Applications Using Parse Rest API

Refactoring the posts controller, to use Models 82

1 public function getHide($objectId = null){

2 if(is_null($objectId)){

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

4 ->with('error','You must select a record to un-publish');

5 }

6

7 try{

8

9 $input = array('active'=>false, 'objectId'=>$objectId);

10 $post = new Post();

11 $result = $post->handleItem($input, true);

12

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

14 ->with('success','Your Post Has been un-publish');

15

16 }catch(Exception $e){

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

18 }

19 }

20

21 public function getPublish($objectId = null){

22 if(is_null($objectId)){

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

24 ->with('error','You must select a record to publish');

25 }

26

27 try{

28 $input = array('active'=>true, 'objectId'=>$objectId);

29 $post = new Post();

30 $result = $post->handleItem($input, true);

31

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

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

34

35 }catch(Exception $e){

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

37 }

38 }

As you can see, by creating our Post class we havemake our code easy to understand and reusablein many places, all we need to do is to send a small parameter and it will change how the functionwill react. In the next chapter we will do the same but with our Comments by creating ourComment class.

Page 88: Building Web Applications Using Parse Rest API

Refactoring the commentscontroller, to use ModelsAs we have created our Post Model, we are going to create our Comment Model, nothing is goingto change except that now we are going to deal with the comments class.

The Comments Model Class

First thing we need to do is to create a newModel class and we are going to follow the convenientnaming method which Laravel⁴⁴ use for its model classes, so the name of the model class will besingular Commentmeanwhile the name of our Parse.com class is plural comments. As a result ourfile will be app/models/Comment.php and it will look like :

1 <?php

2 class Comment {

3 protected $tablename = 'comments';

4 }

As you can see, we have just define a normal class called Comment and a protected variable$tablename which define our Parse.com class name.

getComments Function

Now the first functionality we need to create is to get all the comments from Parse.com, and thisis used in the admin area to see all of the comments despite of the comment status:

1 public function getComments($active = null, $limit = 5, $skip = 0, $orderBy =\

2 'createdAt' )

3 {

4 try{

5 $fullComments = new parseQuery('comments');

6 $fullComments->setCount(true);

7 $fullComments->whereInclude('post');

8 $fullComments->setLimit($limit);

9 $fullComments->setSkip($skip);

10 $fullComments->orderByDescending($orderBy);

11

⁴⁴http://laravel.com/docs/eloquent#basic-usage

Page 89: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 84

12 if(!is_null($active))

13 $fullComments->where('approved',$active);

14

15 $comments = $fullComments->find();

16

17 return $comments;

18 } catch(ParseLibraryException $e){

19 throw new Exception($e->getMessage(), 406);

20 }

21 }

This function will have some default parameters which will be used to retrieve the records, forexample the default values here indicate that we need to get the first 5 records starting fromrecord one ordered descending by the field createdAt. Since we set the active to null we willnot filter the comments based on the status of the comment and this something important for theadmin, to see all the records no matter if its published or not.

Now lets see the impact of this function on our getIndex function in AdminPostsController

class.

1 public function getIndex()

2 {

3 try{

4

5 //$fullComments = new parseQuery('comments');

6 //$fullComments->setCount(true);

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

8 //$fullComments->whereInclude('post');

9 //$fullComments->setLimit($this->perPage);

10 //$fullComments->setSkip($this->skip);

11 //$fullComments->orderByDescending('createdAt');

12 //$comments = $fullComments->find();

13

14 $commentsModel = new Comment;

15 $comments = $commentsModel->getComments();

16

17 $paginator = Paginator::make($comments->results, $comments->count, $t\

18 his->perPage);

19

20 $data = array(

21 'items'=> $comments->results,

22 'paginator' => $paginator,

23 'total' => $comments->count

24 );

25

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

Page 90: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 85

27

28 }catch(ParseLibraryException $e){

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

30 }

31 }

As we can see, we have replaced seven lines of code with only two lines.

getComment Function

To get the comment from Parse.com like any other record, all we have to do is to create a functionand pass the id of the comment which we want, then we query Parse.com and retrieve the actualdata, as simple as one two there:

1 public function getComment($commentId)

2 {

3 try{

4 $fullComment = new parseQuery('comments');

5 $fullComment->whereInclude('post');

6 $fullComment->setLimit(1);

7 $fullComment->where('objectId',$commentId);

8

9 $comment = $fullComment->find();

10

11 return $comment;

12 } catch(ParseLibraryException $e){

13 throw new Exception($e->getMessage(), 406);

14 }

15 }

So the getRecord function has become like:

1 public function getRecord($objectId = null)

2 {

3 if(is_null($objectId)){

4 return Redirect::action('AdminCommentsController@getIndex')->with('er\

5 ror','Choose a comment to view');

6 }

7

8 try{

9 //$commentRecord = new parseQuery('comments');

10 //$commentRecord->where('objectId', $objectId);

11 //$commentRecord->whereInclude('post');

12 //$commentRecord->setLimit(1);

Page 91: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 86

13 //$result = $commentRecord->find();

14

15 $comment = new Comment;

16 $result = $comment->getComment($objectId);

17

18 $data = array(

19 'item'=> $result->results[0],

20 );

21

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

23

24

25 }catch(ParseLibraryException $e){

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

27 }

28 }

As we can see, we have replaced the 5 lines of code with only two lines of code, I guess its notthat bad.

getPostComments Function

We have used this function within our PostModel to retrieve the post comments, and it will hassome default arguments which we can use

• The post object id.• The status of the comments.• How many records to skip.• How many records to retrieve.

1 public function getPostComments($postId, $status = null, $skip = null, $limit\

2 = null)

3 {

4 try{

5

6 $postComments = new parseQuery($this->tablename);

7 $postComments->setCount(true);

8 $postComments->whereInclude('post');

9 $postComments->where('post',$fullComments->dataType('pointer',array('\

10 posts',$postId)));

11

12 if(!is_null($limit))

13 $postComments->setLimit($limit);

Page 92: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 87

14

15 if(!is_null($skip))

16 $postComments->setSkip($skip);

17

18 if(!is_null($status))

19 $postComments->where('approved',$status);

20

21 $postComments->orderByDescending('createdAt');

22 $comments = $postComments->find();

23

24 return $comments;

25

26 } catch(ParseLibraryException $e){

27 throw new Exception($e->getMessage(), 406);

28 }

29 }

With those simple arguments, we can use the same function to get the comments for a specificpost within the admin dashboard and the blog post which is accessible to the guests.

deleteItem Function

Deleting a record is something simple, all we need to do is to create a function and pass the idof the comment which we want to delete like :

1 public function deleteComment($commentId)

2 {

3 try{

4

5 $comment = new parseQuery($this->tablename);

6 $comment->delete($commentId);

7

8 return true;

9

10 } catch(ParseLibraryException $e){

11 throw new Exception($e->getMessage(), 406);

12 }

13 }

handleItem Function

This function is going to be some how a general function between the Create, Edit and Publish& Un-Publish a comment.

We are going to provide it with only two parameters:

Page 93: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 88

• The Input array.• Is it an edit action or not, the default is not.

1 public function handelComment($input, $isEdit = false)

2 {

3 try{

4 $commentData = new parseObject($this->tablename);

5

6 if(isset($input['authorEmail']))

7 $commentData->authorEmail = $input['authorEmail'];

8

9 if(isset($input['postId']))

10 $commentData->post = $commentData->data(array('pointer',array('po\

11 sts',$input['postId'])));

12

13 if(isset($input['authorName']))

14 $commentData->authorName = $input['authorName'];

15

16 if(isset($input['commentBody']))

17 $commentData->commentBody = $input['commentBody'];

18

19 $commentData->approved = (isset($input['approved'])) ? $input['approv\

20 ed'] : false;

21

22 if($isEdit){

23 $result = $commentData->update($input['objectId']);

24 } else {

25 $result = $commentData->save();

26 }

27

28 return $result;

29

30 }catch(ParseLibraryException $e){

31 throw new Exception($e->getMessage(), 406);

32 }

33 }

To make it short, am going to cutoff the data, and just show you the updated functions on ourCommentsController after using the handelComment function.

Page 94: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 89

1 public function postAdd(){

2 try{

3 $comment = new Comment;

4 $result = $comment->handelComment(Input::all());

5

6 return Redirect::action('AdminPostsController@getRecord', $result->re\

7 sults[0]->objectId)

8 ->with('success','Your comment has been added');

9

10 }catch(ParseLibraryException $e){

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

12 }

13 }

1 public function postEdit()

2 {

3 try{

4 $comment = new Comment;

5 $result = $comment->handelComment(Input::all(), true);

6

7 return Redirect::action(array('PostsController@getRecord', Input::get\

8 ('objectId')))

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

10

11 }catch(ParseLibraryException $e){

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

13 }

14 }

1 public function getHide($objectId = null)

2 {

3 if(is_null($objectId)){

4 return Redirect::action('AdminCommentsController@getIndex')

5 ->with('error','Choose a comment to un-publish');

6 }

7

8 try{

9 $input = array('approved' => false, 'objectId' => $objectId);

10 $recordInfo = new Comment;

11 $recordInfo->handelComment($input, true);

12

13

14 return Redirect::action('AdminCommentsController@getIndex')

15 ->with('success','The comment Has been un-published');

Page 95: Building Web Applications Using Parse Rest API

Refactoring the comments controller, to use Models 90

16

17 }catch(ParseLibraryException $e){

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

19 }

20 }

1 public function getPublish($objectId = null)

2 {

3 if(is_null($objectId)){

4 return Redirect::action('AdminCommentsController@getIndex')

5 ->with('error','Choose a comment to approve');

6 }

7

8 try{

9 $input = array('approved' => true, 'objectId' => $objectId);

10 $recordInfo = new Comment;

11 $recordInfo->handelComment($input, true);

12

13 return Redirect::action('AdminCommentsController@getIndex')

14 ->with('success','The comment Has been approved');

15

16 }catch(ParseLibraryException $e){

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

18 }

19 }

And the changes on our postAddComment function under the HomeController is :

1 public function postAddComment()

2 {

3 try {

4 $allData = Input::all();

5 $comment = new Comment;

6 $result = $comment->handelComment($allData);

7

8 return Redirect::action('HomeController@getPost', Input::get('postId'\

9 ))

10 ->with('success', 'Your comment has been added, and waiting f\

11 or approval.');

12 } catch (ParseLibraryException $e) {

13 return Redirect::back()->with('error', $e->getMessage());

14 }

15 }

In the next few chapters, we will have some more information about the parseQuery class whichis the most important class here, and see how to use it in more details, but first we will have asmall tips on how to install Laravel 4.1 with nginx (latest version) & PHP 5.5 on Ubuntu 12.04.

Page 96: Building Web Applications Using Parse Rest API

Mastering Parse Query ClassIn this chapter i will try my best to explain the parseQuery class which is the most class you willever use when working with Parse.com beside the parseObject class.

How to use parse query class

If we open the parseQuery.php file which located at libraries/parse we will notice that thisclass is extended from parse class.

Parse class (parse.php file)

If we have a look at the parse.php file, we will find that it is responsible for making the requestto Parse.com and to manage our data types. The most important function is dataType which wehave used to create the pointer to our post object id.

For now, the function cover only five data types and two operations (used with counter-typedata), which are: * Date data type. * Bytes data type. * Pointer data type. * Geographical Pointerdata type. * File data type. * Increment operation. * Decrement operation.

1 public function dataType($type,$params){

2 if($type != ''){

3 switch($type){

4 case 'date':

5 $return = array(

6 "__type" => "Date",

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

8 );

9 break;

10 case 'bytes':

11 $return = array(

12 "__type" => "Bytes",

13 "base64" => base64_encode($params)

14 );

15 break;

16 case 'pointer':

17 $return = array(

18 "__type" => "Pointer",

19 "className" => $params[0],

20 "objectId" => $params[1]

21 );

Page 97: Building Web Applications Using Parse Rest API

Mastering Parse Query Class 92

22 break;

23 case 'geopoint':

24 $return = array(

25 "__type" => "GeoPoint",

26 "latitude" => floatval($params[0]),

27 "longitude" => floatval($params[1])

28 );

29 break;

30 case 'file':

31 $return = array(

32 "__type" => "File",

33 "name" => $params[0],

34 );

35 break;

36 case 'increment':

37 $return = array(

38 "__op" => "Increment",

39 "amount" => $params[0]

40 );

41 break;

42 case 'decrement':

43 $return = array(

44 "__op" => "Decrement",

45 "amount" => $params[0]

46 );

47 break;

48 default:

49 $return = false;

50 break;

51 }

52

53 return $return;

54 }

55 }

Sadly, thats all what it covers, but the good news is that we can always add whatever we want,so for example if i want to add an array data type, we simply add a new case to the switch likethis :

Page 98: Building Web Applications Using Parse Rest API

Mastering Parse Query Class 93

1 case 'addArray':

2 $return = array(

3 "__op" => "Add",

4 "objects" => $params

5 );

6 break;

And this will be converted to :

1 {"__op":"Add","objects":["flying","kungfu"]}

Which will be add it to an array field in our class. But also we can always check github⁴⁵repository for any new pull requests which can save us a small time.

To fully understands the objects in Parse.com Data, I highly recommend that you readthe full documentation⁴⁶ which describe everything in details.

Parse Query class

If we take a good look at the parseQuery.php file which located under app/libraries/parse,we will notice that the class contains of twenty five functions:

• find function: Which prepare our parameters and return the result of executing therequest function which inherited from parse class.

• setCount function: We have used this function within our codes, and it will just add thecount⁴⁷ parameter to our query, and this will return the total numbers of records we havein Parse Data, and we can use it to minimize the numbers of requests we do to get the datafrom Parse.com.

• getCount function: This function will return the total numbers of records we have.• setLimit function: This function will be used to tell Parse.com howmany records we wantto retrieve, but be careful since Parse.com does not allow more than 1000 records to returnvia each query.

• setSkip function: This function will be used to tell Parse.com how many records we wantto skip before we get our records.

• orderBy function: This function will tell Parse.com which field we want to use to orderascending our retrieved records.

• orderByAscending function: Is identical to the orderBy function.• orderByDescending function: Is the opposite to the orderBy and orderByAscending

functions, but they all work the same, by just send the field name as a parameter to thefunction.

⁴⁵https://github.com/apotropaic/parse.com-php-library/pulls⁴⁶https://parse.com/docs/rest#objects⁴⁷https://parse.com/docs/rest#queries-counting

Page 99: Building Web Applications Using Parse Rest API

Mastering Parse Query Class 94

• whereInclude function: In parse, we saw that we can have fields as Pointer which wecan think of as Foreign key to the primary class, and in general if we query a class whichhas a pointer field to another class, we will get only the objectId, type and the class nameof that field, but if we tell Parse.com that we need to include this field, we will get all theinformation, except it will work for only one level so you can’t get the full information ofa pointers pointer fields.

1 $comments = new parseQuery('comments');

2 $comments->setSkip(0);

3 $comments->setLimit(1);

4 $comments->setCount(true);

5 $result = $comments->find();

6 return Response::json($result);

and the result will be :

1 {

2 "results": [

3 {

4 "post": {

5 "__type": "Pointer",

6 "className": "posts",

7 "objectId": "J9mLUW0heO"

8 },

9 "approved": false,

10 "authorEmail": "[email protected]",

11 "authorName": "jone doe",

12 "commentBody": "this is the comment of the year",

13 "createdAt": "2013-09-06T17:50:46.124Z",

14 "updatedAt": "2013-10-12T16:31:44.834Z",

15 "objectId": "lfZlfnfmTY"

16 }

17 ],

18 "count": 3

19 }

As we can see, since we didn’t tell the Parse.com that we need to include the post pointers, wedidn’t get much information, but if we just add the whereInclude the results will be different:

Page 100: Building Web Applications Using Parse Rest API

Mastering Parse Query Class 95

1 $comments = new parseQuery('comments');

2 $comments->setSkip(0);

3 $comments->whereInclude('post');

4 $comments->setLimit(1);

5 $comments->setCount(true);

6 $result = $comments->find();

7 return Response::json($result);

So lets see the result:

1 {

2 "results": [

3 {

4 "post": {

5 "active": true,

6 "body": "Contrary to popular belief, Lorem Ipsum is not simpl\

7 y random text. It has roots in a piece of classical Latin literature from 45 \

8 BC, making it over 2000 years old. Richard McClintock, a Latin professor at H\

9 ampden-Sydney College in Virginia, looked up one of the more obscure Latin wo\

10 rds, consectetur, from a Lorem Ipsum passage, and going through the cites of \

11 the word in classical literature, discovered the undoubtable source. Lorem Ip\

12 sum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Maloru\

13 m\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is\

14 a treatise on the theory of ethics, very popular during the Renaissance. The\

15 first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a li\

16 ne in section 1.10.32.\r\n\r\nThe standard chunk of Lorem Ipsum used since th\

17 e 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.3\

18 3 from \"de Finibus Bonorum et Malorum\" by Cicero are also reproduced in the\

19 ir exact original form, accompanied by English versions from the 1914 transla\

20 tion by H. Rackham.",

21 "title": "Where does Lorem Ipsum come from?",

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

23 "updatedAt": "2013-11-24T05:41:39.590Z",

24 "objectId": "J9mLUW0heO",

25 "__type": "Object",

26 "className": "posts"

27 },

28 "approved": false,

29 "authorEmail": "[email protected]",

30 "authorName": "jone doe",

31 "commentBody": "this is the comment of the year",

32 "createdAt": "2013-09-06T17:50:46.124Z",

33 "updatedAt": "2013-10-12T16:31:44.834Z",

34 "objectId": "lfZlfnfmTY"

35 }

36 ],

Page 101: Building Web Applications Using Parse Rest API

Mastering Parse Query Class 96

37 "count": 3

38 }

Note:

nice thing about the whereInclude function is that you can pass a string as parameter,which will contain the names of the fields you want to include, separated by commalike: posts,users,someotherfield, and it will get all the information you asked for.

• where and whereEqualTo functions: both functions do the same thing, they get the keyand the value as parameters, so we tell Parse.com that we need the record/records whicha field (key) has the matching value.

• whereNotEqualTo function: Is the opposite of the where function, so it will help us toexclude the records which has the field we specify, with the value we provide.

• whereGreaterThan function: We can use this function to get all the records which hasthe field we specify, with value greater than the value we provide.

• whereLessThan function: Is the opposite of the whereGreaterThan function.• whereGreaterThanOrEqualTo function: This functionwork the same as whereGreaterThanexcept that it will also include the records which has the field we specify, with value alsoequal to the one we provide.

• whereLessThanOrEqualTo function: This will work the same as whereLessThan but itwill include also the records which has the field we specify, with value also equal to theone we provide.

• whereContainedIn function: This will help us to get all the records which has the valueof the field we specify as one of the value we provide as array.

1 $games = new parseQuery('games');

2 $games->whereContainedIn('score',array(1,2,4));

3 $result = $games->find();

• whereNotContainedIn function: Work as the opposite of the whereContainedIn func-tion.

• whereExists function: Some times we may have records with fields contains no data, forParse.com it will be fields of undefined value, so this function will make sure that we canget all the records we want which has value.

1 $games = new parseQuery('games');

2 $games->whereExists('score');

3 $result = $games->find();

• whereDoesNotExist function: Is the opposite of whereExists function.• whereRegex function: We can use this function to query the data based on regularexpression pattern, but you should note that Parse.com start deprecating general regexqueries that don’t match an exact prefix.

Page 102: Building Web Applications Using Parse Rest API

Mastering Parse Query Class 97

1 $games = new parseQuery('games');

2 // this will get all the games which has the name field

3 // start with `J`

4 $games->whereRegex('gameName',"^\QJ\E");

5 $result = $games->find();

• wherePointer function: This is so useful when dealing with pointers, we didn’t use itbut for example we can use it when querying the comments class for records belong to aspecific post.

• whereInQuery function: If you want to retrieve objects where a field contains an objectthat matches another query, you can use the whereInQuery function. Note that the defaultlimit of 100 and maximum limit of 1000 apply to the inner query as well, so with large datasets you may need to construct queries carefully to get the desired behavior. For example,imagine you have Post class and a Comment class, where each Comment has a relation toits parent Post. You can find comments on posts with images by doing:

1 $comments = new parseQuery('comments');

2 $comments->whereInQuery('post','posts',array(

3 'where'=> array(

4 'image' => array('$exists' => true)

5 )

6 ));

7 $result = $comments->find();

8 return Response::json($result);

I know its not that pretty but this is how to get it, and the parameters which will be sent toParse.com will be like:

1 where={"post":{"$inQuery":{"where":{"image":{"$exists":true}}},"className":"p\

2 osts"}}

• whereNotInQuery function: I think by the name we knows what it will do, its theopposite of the whereInQuery function.

By now we have had fast overview of what doe the parseQuery class has for us, and how to useit when querying data from Parse.com

Page 103: Building Web Applications Using Parse Rest API

Learning ResourcesMore about Parse Products

If you like to read more about Parse.com and how to use it or how other developers are using itcheck :

• Parse.com Documentation⁴⁸.• Parse.com Tutorials⁴⁹.• Parse.com Case Study⁵⁰ and Featured⁵¹.• Parse.com Quick start⁵².

More about Laravel

But if your looking to read more about Laravel you can check:

• Laravel Documentation⁵³.• Laravel: From Apprentice To Artisan⁵⁴.• Laravel: Code Bright⁵⁵.• Laravel 4 Cookbook⁵⁶.• Learning Laravel: The Easiest Way⁵⁷.• Laravel Testing Decoded⁵⁸.• Build APIs You Won’t Hate⁵⁹.• Laracasts Video Tutorials⁶⁰.

⁴⁸https://parse.com/docs/rest⁴⁹https://parse.com/tutorials⁵⁰https://parse.com/customers/case_study⁵¹https://parse.com/customers/featured⁵²https://parse.com/apps/quickstart⁵³http://laravel.com/docs⁵⁴https://leanpub.com/laravel⁵⁵https://leanpub.com/codebright⁵⁶https://leanpub.com/laravel4cookbook⁵⁷https://leanpub.com/learninglaravel⁵⁸https://leanpub.com/laravel-testing-decoded⁵⁹https://leanpub.com/build-apis-you-wont-hate⁶⁰https://laracasts.com/

Page 104: Building Web Applications Using Parse Rest API

Preparing your production serverI have put this code as a gist⁶¹ on github gists⁶², so that you can easily fork it or comment on itif you find something useful to share with the rest of us.

Creating Your Laravel & nginx Server

We will install Larave 4.1 with PHP5.5 & Latest nginx on Ubuntu 12.04.3 x64.

Updating your system

1 apt-get update && apt-get upgrade

2 adduser [username]

3 usermod -aG sudo [username]

4 apt-get -y install git

Now we need to logout and login using the user which we have created

1 git config --global user.name "your name"

2 git config --global user.email [email protected]

Installing latest nginx version

Ubuntu 12.04 does not include the latest stable version of nginx, thats why we need to add therepository from the nginx website

1 sudo -s

2 nginx=stable

3 apt-get -y install python-software-properties

4 add-apt-repository ppa:nginx/$nginx

5 apt-get update && apt-get upgrade

6 apt-get -y install nginx

7 service nginx start

8 exit

Installing php5.5

When installing Ubuntu 12.04 you will get the version 5.3.x of php, and since the latest versionis 5.5.x, we need to add an external repository to make sure we get the latest version of php.

⁶¹https://gist.github.com/linuxjuggler/7812986⁶²https://gist.github.com

Page 105: Building Web Applications Using Parse Rest API

Preparing your production server 100

1 sudo -s

2 add-apt-repository ppa:ondrej/php5

3 apt-get update && apt-get upgrade

4 apt-get -y install php5-fpm php5-mcrypt php5-sqlite sqlite php5-cli php5-xcac\

5 he php5-curl php5-json

Just remember that if we want to install mysql we should also install php5-mysql and any otherrequired module.

Note :

Someone noted out there that you should edit your php.ini file and change the value ofcgi.fix_path

1 sudo -s

2 nano /etc/php5/fpm/php.ini

change the value of cgi.fix_path from :

1 ;cgi.fix_path = 1

to

1 cgi.fix_path = 0

Installing composer

Installing Composer is simple and easy, we download the composer.phar file then we copy it tothe bin directory to make sure that we can use it globaly.

1 curl -sS https://getcomposer.org/installer | php

2 sudo mv composer.phar /usr/local/bin/composer

From time to time we need to make sure that we have the latest version of composer so we issuethe command:

1 sudo composer self-update

Installing laravel

Now that we have installed composer we can use it to install laravel in a dirctory of our choice,commenly used /var/www , most severs by default does not have it if they dont have apache

installed by default .

Page 106: Building Web Applications Using Parse Rest API

Preparing your production server 101

1 sudo -s

2 cd /var

3 mkdir www

4 chown -R [username]:[username] www

5 exit

6 cd www && composer create-project laravel/laravel

We will just change the owner of the storage directory to be the web server, or we can just makeit writable for all users, I like changing the ownership ..

1 chown -R www-data:www-data laravel/app/storage

Configure nginx for laravel

Now that we have everything we need, we still have one last step, which is to configure nginxso that it will serve our site.

1 sudo -s

2 cd /etc/nginx/sites-avaliable

3 # if you didnt find this directory, try to check `/etc/nginx/conf.d/`

4 rm default

5 nano default

then we have to paste this and edit it as we need

1 server {

2

3 # Port that the web server will listen on.

4 listen 80;

5

6 # Host that will serve this project.

7 server_name .jasmine.dev;

8

9 # Useful logs for debug.

10 access_log /var/www/laravel/access.log;

11 error_log /var/www/laravel/error.log;

12 rewrite_log on;

13

14 # The location of our projects public directory.

15 root /var/www/laravel/public;

16

17 # Point index to the Laravel front controller.

18 index index.php;

19

Page 107: Building Web Applications Using Parse Rest API

Preparing your production server 102

20 location / {

21

22 # URLs to attempt, including pretty ones.

23 try_files $uri $uri/ /index.php?$query_string;

24

25 }

26

27 # Remove trailing slash to please routing system.

28 if (!-d $request_filename) {

29 rewrite ^/(.+)/$ /$1 permanent;

30 }

31

32 # PHP FPM configuration.

33 location ~* \.php$ {

34 fastcgi_pass unix:/var/run/php5-fpm.sock;

35 fastcgi_index index.php;

36 fastcgi_split_path_info ^(.+\.php)(.*)$;

37 include /etc/nginx/fastcgi_params;

38 fastcgi_param SCRIPT_FILENAME $document_root$fa\

39 stcgi_script_name;

40 }

41

42 # We don't need .ht files with nginx.

43 location ~ /\.ht {

44 deny all;

45 }

46

47 # Set header expirations on per-project basis

48 location ~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$ {

49 expires 365d;

50

51 }

52 }

Final steps:

I like to have a link to my Laravel installation under my home directory so :

1 $ ln -s /var/www/laravel www

Finally we restart nginx or we can just reboot your VPS.

1 sudo service nginx restart

Page 108: Building Web Applications Using Parse Rest API

Final WordsI hope that you have learned a good amount of information about dealing with Parse.com REST,but as I have said before remember that Your Limitation is just Your Imagination.

And if you have something you want to talk about you can email me, and i will do my best toanswer your email as soon as i get it.

Thanks for being a good reader, and i hope that 2014 will be much more better than 2013 for allof us.