Laravel 5 Cookbook
Laravel 5 Cookbook
Nathan Wu
© 2015 - 2016 Nathan Wu
Contents
Book Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Book Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
What You Will Get . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Book Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Translation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Book Status, Changelog and Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Changelog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Current Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Laravel 5 Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Chapter 1: Back End Recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Project Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
List Of Recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Recipe 1 - Introducing CLI (Command Line Interface) . . . . . . . . . . . . . . . . . . . . 8
Recipe 2 - All About Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Recipe 3 - Build A Laravel Starter App . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Recipe 4 - Create A User Authentication System with Facebook and Socialite . . . . . . . 31
Recipe 5 - Create A User Authentication System Using Laravel Auth Scaffold . . . . . . . 42
Recipe 6 - Image Upload In Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Recipe 7 - Seeding Your App Using Faker . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Recipe 8 - Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Recipe 9 - Testing Your App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Recipe 10 - Writing APIs with Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1
Book Description
If you’re looking for a book that can help you to build amazing web applications, this is the book
for you! Aimed at people who have some experience with Laravel, this cookbook has your back!
There are many proven code rich recipes for working with Laravel. Each recipe includes practical
advice, tips and tricks for working with jQuery, AJAX, JSON, API, data persistence, complex
application structure, modular PHP, testing, deployment and more.
Think about this book as a collection of all premium Laravel tutorials or the successor to the popular
Learning Laravel 5 book.
Laravel 5 Cookbook also includes tested code that you can download and reuse in your own
applications. You’ll save time, learn more about Laravel and other related technologies in the process.
We also have a forum for discussion and debate. You can freely ask any questions, provide your
valuable feedback and help others.
It’s time to discover Laravel more!
Requirements
The projects in this book are intended to help people who have grasped the basics of PHP and
HTML to move forward, developing more complex projects, using Laravel advanced techniques.
The fundamentals of the PHP and Laravel are not covered, you will need to:
2
Book Description 3
Book Structure
Note: This is a draft version. This book is still under active development, that means some chapters
and its content may change. The book also may have some errors and bugs. For any feedback, please
send us an email. Thank you.
Feedback
Feedback from our readers is always welcome. Let us know what you liked or may have disliked.
Simply send an email to [email protected].
We’re always here.
Translation
We’re also looking for translators who can help to translate our book to other languages.
Feel free to contact us at [email protected].
Here is a list of our current translators:
List of Translators¹
• Version: 0.20
• Status: Complete (Beta Version)
• Updated: May 15th, 2016
5
Laravel 5 Cookbook
6
Chapter 1: Back End Recipes
Introduction
Building APIs and large applications using modern technologies can be a daunting task. In this
chapter, you’ll learn best practices and modern techniques for back-end development, starting with
an introduction to the command line and Git.
These complete, easy-to-use recipes show you how to use cookies, sessions, web storage and some
popular Laravel packages. You’ll also learn about writing APIs and debugging techniques.
In addition to mastering the technologies, you’ll understand when they’re needed and how to use
them.
Project Files
All project files of this book can be downloaded at:
https://github.com/LearningLaravel/cookbook/releases⁵
At the end of each recipe, you can find the recipe’s project files (Tag). Feel free to use each of them
at any stage of your development process.
List Of Recipes
Note: As this is a cookbook, you may skip any recipe that you know already. The book
is still under active development, that means some chapters and its recipes may change.
The book also may have some errors and bugs. For any feedback, please send us an
email.
Backend recipes
• Recipe 1 - Introducing CLI (Command Line Interface)
• Recipe 2 - All About Git
• Recipe 3 - Build A Laravel Starter App
• Recipe 4 - Create A User Authentication System with Facebook and Socialite
⁵https://github.com/LearningLaravel/cookbook/releases
7
Chapter 1: Back End Recipes 8
Iterm inteface
Iterm inteface
Chapter 1: Back End Recipes 11
• Git allows you to create as many branches of your project as you want. You can use each
branch to test, create a new feature, fix bugs, etc.
• You can see what was changed in your project’s files. This helps you understand what
happened and improve your code.
• You can easily store all the versions of your site and restore previous versions whenever you
want.
• Store your files on cloud-based Git repository host services like Github and Bitbucket.
• You can easily share your files with others.
• A VCS or Git helps your team work more efficiently. Everyone knows what is going on and
can merge the changes into a common version.
1 xcode-select --install
For more information and other methods, you can see this guide:
https://git-scm.com/book/en/v2/Getting-Started-Installing-Git¹⁰
Configuring Git
When you first install Git, you should set your name, email address and enable coloring to pretty
up command outputs. Open your CLI and run these commands:
1 cd Code/Laravel
Note: If you’re using Homestead, the Code directory is where we will put our Laravel
apps. Code/Laravel is your working directory. You can use Git on Homestead or on
your local machine, it’s up to you.
1 git init
This command creates an empty Git repository. If you’re using Homestead, the path of the Git
directory is:
¹⁰https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
Chapter 1: Back End Recipes 13
1 /home/vagrant/Code/Laravel/.git/
“.git” is a hidden folder and it doesn’t contain your project’s files yet.
1 git status
You will see a list of untracked files, that means Git doesn’t monitor those files yet.
Untracked files
To tell Git that you want to include all these files, use the git add -A command:
1 git add -A
Note: Alternatively, you can use git add –a or git add –all or git add . command.
Chapter 1: Back End Recipes 14
Add files
The git add command tells git to add changes in your project to the staging area. However, those
changes aren’t saved yet until you run git commit:
You can use the -m flag (stands for message) to give a comment on the command line. My message
is “First commit”, but you can use whatever you like.
Well done! You’ve made your first commit!
Chapter 1: Back End Recipes 15
New repository
When creating a new repository, you can choose any name that you like. Choose Private if you
don’t want anyone access your files.
Note: Don’t worry too much about the settings, you can change those settings later.
Your new repository (repo) is empty. You need to upload your files to that Github repo.
Every repository has a unique remote URL, your remote URL should look like this:
https://github.com/YourGithubUsername/YourRepoName.git
Take note of this link.
Good! We will try to upload our Laravel app (/Code/Laravel) to Github.
Navigate to the working directory (on your Homestead or local machine):
1 cd /Code/Laravel
Note: Your remote URL should be different. Be sure to use your remote URL.
After adding a new remote, we can push our files to Github using the git push command:
1 git push
Cloning a repository
To download any repo, you can use the git clone command.
First, navigate to the location where you want to place the cloned directory:
1 cd Code
Type git clone and the unique remote URL to clone the repo:
Note: you can clone any public repository. If you don’t want anyone to download your
repo, set it private.
Recipe 2 Wrap-up
In this recipe, you learned some major Git commands. Throughout this book, we’ll use Git to
download the source code, front end components and deploy our Laravel applications. We won’t
talk about it anymore because this is a Laravel book, not a Git book. If you wish to learn more about
Git, check these sites out:
Atlassian Git Tutorials¹³
Git-Tower¹⁴
Codeschool - Try Git¹⁵
Super Useful Need To Know Git Commands¹⁶
It’s time to start learning about Laravel!
¹³https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
¹⁴https://www.git-tower.com/learn/git/ebook
¹⁵https://www.codeschool.com/courses/try-git
¹⁶http://zackperdue.com/tutorials/super-useful-need-to-know-git-commands
Chapter 1: Back End Recipes 19
Installing Laravel
Let’s start by installing Laravel!
Note: Please note that I’m using Homestead. If you don’t use Homestead, the process
could be different.
1 vagrant ssh
1 cd Code
Be sure to have the latest version of Laravel Installer by running this command:
New repository
Great! You should have a new Laravel app! Feel free to change the name of the app to your liking.
Note: If you’re not familiar with this process, please read the Learning Laravel 5 book.
1 cd ~/.homestead
1 vim Homestead.yaml
We use VIM to edit the file. If you don’t know how to use VI or VIM, you can open it with your
favorite editor by using this command:
1 open Homestead.yaml
Find:
1 sites:
2 - map: homestead.app
3 to: /home/vagrant/Code/Laravel/public
Just a quick reminder, this section allows us to map a domain to a folder on our VM. For example, we
can map homestead.app to the public folder of our Laravel project, and then we can easily access
our Laravel app via this address: “http://homestead.app”.
Our new app is called cookbook, and I would like to access it via this address: “http://cookbook.app”.
So, let’s add the following code:
Chapter 1: Back End Recipes 21
1 - map: cookbook.app
2 to: /home/vagrant/Code/cookbook/public
Tip: if you’re using VIM, press ESC and then write :wq (write quit) to save and exit
Remember that, when we add any domain, we must edit the hosts file on our local machine to
redirect requests to our Homestead environment.
On Linux or Mac, you can find the hosts file at etc/hosts or /private/etc/hosts. You can edit the
hosts file using this command:
1 192.168.10.10 cookbook.app
Note: All sites will be accessible by HTTP via port 8000 and HTTPS via port 44300 by
default.
To let the system know that we’ve edited the hosts file, run this command:
1 source /private/etc/hosts
Finally, SSH into our Homestead (by using vagrant ssh or homestead ssh), and use the serve
command to activate our new site:
Good job! If everything is working correctly, we should see our app’s home page:
Chapter 1: Back End Recipes 22
1 <html>
2 <head>
3 <title>Home Page</title>
4
5 <link href='//fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' typ\
6 e='text/css'>
7
8 <style>
9 body {
10 margin: 0;
11 padding: 0;
12 width: 100%;
13 height: 100%;
14 color: #B0BEC5;
15 display: table;
16 font-weight: 100;
17 font-family: 'Lato';
18 }
19
20 .container {
21 text-align: center;
22 display: table-cell;
23 vertical-align: middle;
24 }
25
26 .content {
27 text-align: center;
28 display: inline-block;
29 }
30
31 .title {
32 font-size: 96px;
33 margin-bottom: 40px;
34 }
35
36 .quote {
37 font-size: 24px;
38 }
39 </style>
40 </head>
41 <body>
42 <div class="container">
Chapter 1: Back End Recipes 24
43 <div class="content">
44 <div class="title">Home Page</div>
45 <div class="quote">Our Home page!</div>
46 </div>
47 </div>
48 </body>
49 </html>
Open PagesController, which can be found at app/Http/Controllers, and create a new home
action:
When you have the PagesController, the next thing to do is modifying our routes!
Open routes.php file. Change the default route to:
1 Route::get('/', 'PagesController@home');
In this book, we will use the first one (using Bootstrap CDN). This is also the fastest method.
Let’s open home.blade.php, remove the Lato font and these css styles:
Done! You now have fully integrated Twitter Bootstrap into our app!
1 <html>
2 <head>
3 <title> @yield('title') </title>
4 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6\
5 /css/bootstrap.min.css">
6 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6\
7 /css/bootstrap-theme.min.css">
8
9 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
10 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.mi\
11 n.js"></script>
12 </head>
13 <body>
14
15 @include('shared.navbar')
16
17 @yield('content')
18
19 </body>
20 </html>
We can now change our home view to extend the master layout.
Chapter 1: Back End Recipes 29
1 @extends('master')
2 @section('title', 'Home')
3
4 @section('content')
5 <div class="container">
6 <div class="content">
7 <div class="title">Home Page</div>
8 <div class="quote">Our Home page!</div>
9 </div>
10 </div>
11 @endsection
Refresh your browser, we should have a new home page with a nice navigation bar:
1 @extends('master')
2 @section('title', 'About')
3
4 @section('content')
5 <div class="container">
6 <div class="content">
7 <div class="title">About Page</div>
8 <div class="quote">Our about page!</div>
9 </div>
10 </div>
11 @endsection
1 @extends('master')
2 @section('title', 'Contact')
3
4 @section('content')
5 <div class="container">
6 <div class="content">
7 <div class="title">Contact Page</div>
8 <div class="quote">Our contact page!</div>
9 </div>
10 </div>
11 @endsection
1 Route::get('/about', 'PagesController@about');
2 Route::get('/contact', 'PagesController@contact');
Please note that we’re using Laravel 5.2, so we need to add these routes into the web middleware
group:
Congratulations! You’ve just taken the first step in building awesome Laravel applications!
Note: If you’re using Git, this is a good time to initialize your repo with git init.
Recipe 3 Wrap-up
Tag: Version 0.1 - Recipe 3¹⁸
Installing Socialite
In most web frameworks, authenticating users using 3rd party providers is never as easy as it could
be. Luckily, Laravel 5 provides a simple way to authenticate with OAuth providers using Socialite.
Currently, Socialite officially supports authentication with Facebook, GitHub, Google, Twitter and
Bitbucket. If you want to use Socialite with other providers (Youtube, Wordpress, etc.), check out
SocialiteProviders¹⁹, which is a collection of OAuth 1 and 2 packages that extends Socialite.
Actually, Socialite is an official package, and it’s not included in Laravel by default. To use Socialite,
we have to install it by running this command:
Alternatively, you may edit the composer.json file, add below code into the require section and
run composer update:
1 "laravel/socialite": "~2.0"
1 Laravel\Socialite\SocialiteServiceProvider::class,
Click Add a New App and choose Website to create a Facebook app.
Alternatively, you can access this link:
https://developers.facebook.com/quickstarts/?platform=web
Enter your app’s name and click Create New Facebook App ID:
Chapter 1: Back End Recipes 34
Choose a Category for your app and then click Create App ID
Click Skip Quick Start to access your App Dashboard
That’s it! You can get your App ID and App Secret here.
Follow the instructions to create a test app. You can name the app whatever you want.
After that, click Settings -> Add Platforms -> Website
Enter your app URL into the Site URL field. (For example, cookbook.app)
Chapter 1: Back End Recipes 36
Enter your app URL into the App Domains field, too.
Click Save Changes to update your Test App.
Well done! Don’t forget to grab your Test App ID and Test App Secret.
1 'facebook' => [
2 'client_id' => 'yourFacebookAppID',
3 'client_secret' => 'yourFacebookAppSecret',
4 'redirect' => 'http://yourLaravelAppURL/login/facebook/callback',
5 ],
If you’re using the .env config file, you may use the following instead:
Chapter 1: Back End Recipes 37
1 'facebook' => [
2 'client_id' => env('FACEBOOK_ID'),
3 'client_secret' => env('FACEBOOK_SECRET'),
4 'redirect' => env('FACEBOOK_URL'),
5 ],
1 FACEBOOK_ID=yourFacebookAppID
2 FACEBOOK_SECRET=yourFacebookAppSecret
3 FACEBOOK_URL=http://yourLaravelAppURL/login/facebook/callback
Done! Laravel automatically detects your Facebook app information and prepares everything for
you!
Note: If you’re using Homestead, use your Test App ID and Secret.
This migration will create a new users table with the facebook_id column.
If you have an existing app, you have to create a new migration to update the users table:
Chapter 1: Back End Recipes 38
1 <?php
2
3 use Illuminate\Database\Schema\Blueprint;
4 use Illuminate\Database\Migrations\Migration;
5
6 class UpdateUsersTable extends Migration
7 {
8 public function up()
9 {
10 if(Schema::hasColumn('users', 'facebook_id')) {
11
12 } else {
13 Schema::table('users', function ($table) {
14 $table->string('facebook_id')->unique();
15 });
16 }
17 }
18
19 public function down()
20 {
21 Schema::table('users', function ($table) {
22 $table->dropColumn('facebook_id');
23 });
24 }
25 }
Next, don’t forget to run php artisan migrate to update your database.
Note: If you don’t have a database yet, create a new one. The name of our database
is cookbook. If you wish to learn more about working with databases, read Learning
Laravel 5 book’s Chapter 3²⁰.
One last step, open app/User.php file and update our User Model:
²⁰http://learninglaravel.net/laravel5/building-a-support-ticket-system
Chapter 1: Back End Recipes 39
1 protected $fillable = [
2 'name', 'email', 'password', 'facebook_id',
3 ];
1 Route::get('login/facebook', 'Auth\AuthController@redirectToFacebook');
2 Route::get('login/facebook/callback', 'Auth\AuthController@getFacebookCallback');
11 $user->save();
12 } else {
13 $user = User::where('facebook_id', $data->id)->first();
14 if(is_null($user)){
15 // Create a new user
16 $user = new User();
17 $user->name = $data->user['name'];
18 $user->email = $data->email;
19 $user->facebook_id = $data->id;
20 $user->save();
21 }
22
23 Auth::login($user);
24 }
25 return redirect('/')->with('success', 'Successfully logged in!');
26 }
Note: Facebook now returns a full name instead of first name and last name. We have
to use $data->user[‘name’] to get the name of the user.
Because we’re using the Socialite facade and the Auth facade, be sure to tell Laravel about them.
Find:
Add above:
1 use Socialite;
2 use Auth;
Done! We can now be able to log in or register a new account using Facebook.
1 @section('content')
2 <div class="container">
3 <div class="content">
4 <div class="title">Home Page</div>
5 @if(!Auth::check())
6 <div class="quote">Our Home page!</div>
7 @else
8 <div class="quote">You are now logged in!</div>
9 @endif
10 </div>
11 </div>
12 @endsection
We use Auth::check() to check if the user is already logged into our application. If the user is
authenticated, we display the You are now logged in message.
Once again, because we’re using Laravel 5.2, we have to put our routes into the web middleware
group to use Session. The routes.php should look like this:
Save the changes and reload our home page, we should see:
Chapter 1: Back End Recipes 42
Note: The users/register routes are optional. You may use them to build a registration
page to create test users (Recipe 203 or Learning Laravel 5 book’s Chapter 4) and test
the Facebook login feature. If you don’t need them, you may just remove them.
Recipe 4 Wrap-up
Tag: Version 0.2 - Recipe 4²¹
That’s it! You can now log in or register a new member using Facebook.
Using this technique, you can use Socialite to authenticate users with other providers!
By default, your Facebook app is in development mode (aka sandbox mode). Don’t forget to make
your app live and use the real App ID and App Secret.
If you want to add a Facebook login button to your app, simply add the following to wherever you
want:
Note: We use Font Awesome here. If you want to learn how to integrate Font Awesome,
please read the next recipe. Recipe 5 also shows how to add the Facebook button into
your login page.
Make:auth command
As you see, Laravel has generated some views, created a new HomeController and updated our
routes.php file.
Go ahead and reload our home page:
Make:auth command
With all this done, we now have a complete user authentication system!
The home route (user dashboard) and HomeController are also created.
The Routes::auth() method is used to define the login route, the register route and the password
reset routes.
You may realize that our home page and other pages now have a different master layout. The new
layout is views/layouts/app.blade.php:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7
8 <title>Laravel</title>
9
10 <!-- Fonts -->
11 <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/fo\
12 nt-awesome.min.css" rel='stylesheet' type='text/css'>
13 <link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700" re\
14 l='stylesheet' type='text/css'>
15
16 <!-- Styles -->
17 <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.mi\
18 n.css" rel="stylesheet">
19 {{-- <link href="{{ elixir('css/app.css') }}" rel="stylesheet"> --}}
20
21 <style>
Chapter 1: Back End Recipes 45
22 body {
23 font-family: 'Lato';
24 }
25
26 .fa-btn {
27 margin-right: 6px;
28 }
29 </style>
30 </head>
31 <body id="app-layout">
32 <nav class="navbar navbar-default">
33 <div class="container">
34 <div class="navbar-header">
35
36 <!-- Collapsed Hamburger -->
37 <button type="button" class="navbar-toggle collapsed" data-toggl\
38 e="collapse" data-target="#spark-navbar-collapse">
39 <span class="sr-only">Toggle Navigation</span>
40 <span class="icon-bar"></span>
41 <span class="icon-bar"></span>
42 <span class="icon-bar"></span>
43 </button>
44
45 <!-- Branding Image -->
46 <a class="navbar-brand" href="{{ url('/') }}">
47 Laravel
48 </a>
49 </div>
50
51 <div class="collapse navbar-collapse" id="spark-navbar-collapse">
52 <!-- Left Side Of Navbar -->
53 <ul class="nav navbar-nav">
54 <li><a href="{{ url('/home') }}">Home</a></li>
55 </ul>
56
57 <!-- Right Side Of Navbar -->
58 <ul class="nav navbar-nav navbar-right">
59 <!-- Authentication Links -->
60 @if (Auth::guest())
61 <li><a href="{{ url('/login') }}">Login</a></li>
62 <li><a href="{{ url('/register') }}">Register</a></li>
63 @else
Chapter 1: Back End Recipes 46
64 <li class="dropdown">
65 <a href="#" class="dropdown-toggle" data-toggle="dro\
66 pdown" role="button" aria-expanded="false">
67 {{ Auth::user()->name }} <span class="caret"></s\
68 pan>
69 </a>
70
71 <ul class="dropdown-menu" role="menu">
72 <li><a href="{{ url('/logout') }}"><i class="fa \
73 fa-btn fa-sign-out"></i>Logout</a></li>
74 </ul>
75 </li>
76 @endif
77 </ul>
78 </div>
79 </div>
80 </nav>
81
82 @yield('content')
83
84 <!-- JavaScripts -->
85 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.\
86 js"></script>
87 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.mi\
88 n.js"></script>
89 {{-- <script src="{{ elixir('js/app.js') }}"></script> --}}
90 </body>
91 </html>
This master layout also has Bootstrap, jQuery, Font Awesome, Lato font and a navigation bar.
1 <html>
2 <head>
3 <title> @yield('title') </title>
4 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6\
5 /css/bootstrap.min.css">
6 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6\
7 /css/bootstrap-theme.min.css">
8
9 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
10 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.mi\
11 n.js"></script>
12 </head>
13 <body>
14
15 @include('shared.navbar')
16
17 @yield('content')
18
19 </body>
20 </html>
Replace with:
We now have a new layout. All the pages are still working fine.
Login page
To test out the new pages, you can try to register a new member, login and logout.
Sadly, our app still has one bug. Let’s see what it is and how to fix it in the next section.
It’s fairly simple to fix. When users register a new account, their facebook id is set as their provided
email, which should be unique.
Now that we’ve taken care of the bug!
We’re using Font Awesome here, so let’s add the following inside of our master layout’s head tag
to integrate Font Awesome:
1 <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-a\
2 wesome.min.css" rel='stylesheet' type='text/css'>
Recipe 5 Wrap-up
Tag: Version 0.3 - Recipe 5²²
1 Intervention\Image\ImageServiceProvider::class,
Intervention Image supports both PHP’s GD library and Imagick extension. You can choose which
one that you want to use in Intervention Image’s configuration file.
Let’s generate the configuration file by running this command:
• Uploading images
• Display images
• Manipulating images (resize, crop, etc.)
In the next section, we’re going to implement a cover image for our home page.
Chapter 1: Back End Recipes 52
1 @extends('layouts.app')
2
3 @section('content')
4
5 <div class="container spark-screen">
6 <div class="row">
7 <div class="col-md-10 col-md-offset-1">
8 <div class="panel panel-default">
9
10 <div class="panel-heading">Dashboard</div>
11
12 <div class="panel-body">
13
14 <form method="POST" action="/upload" enctype="multipart/form\
15 -data">
16
17 @foreach ($errors->all() as $error)
18 <p class="alert alert-danger">{{ $error }}</p>
19 @endforeach
20
21 @if (session('status'))
22 <div class="alert alert-success">
23 {{ session('status') }}
24 </div>
25 @endif
26
27 {!! csrf_field() !!}
28
29 <div class="form-group">
30 <label for="image">Choose an image</label>
31 <input type="file" id="image" name="image">
32 </div>
33
34 <button type="submit" class="btn btn-default">Upload</bu\
35 tton>
36
Chapter 1: Back End Recipes 53
37 </form>
38
39 </div>
40 </div>
41 </div>
42 </div>
43 </div>
44 @endsection
Please keep in mind that when you upload a file, you need to include this line in your form:
1 enctype="multipart/form-data"
Just a quick reminder, we display the errors when the form is not valid. If the validator fails, Laravel
will store all errors in the session. We can easily access the errors via $errors object:
Chapter 1: Back End Recipes 54
1 @if (session('status'))
2 <div class="alert alert-success">
3 {{ session('status') }}
4 </div>
5 @endif
Note: If you want to learn more about how to work with forms, read the Learning
Laravel 5 book’s Chapter 3²³
Storing images
To validate the form, we will create a new ImageFormRequest:
²³http://learninglaravel.net/laravel5/building-a-support-ticket-system
Chapter 1: Back End Recipes 55
1 <?php
2
3 namespace App\Http\Requests;
4
5 use App\Http\Requests\Request;
6
7 class ImageFormRequest extends Request
8 {
9 /**
10 * Determine if the user is authorized to make this request.
11 *
12 * @return bool
13 */
14 public function authorize()
15 {
16 return true;
17 }
18
19 /**
20 * Get the validation rules that apply to the request.
21 *
22 * @return array
23 */
24 public function rules()
25 {
26 return [
27 'image' => 'required',
28 ];
29 }
30 }
The next step is to wire the form to a controller and process it.
Let’s generate a new ImagesController:
That seems overwhelming at first, but it’s actually very easy to understand. First, we check if the
form has a file (image) or not:
1 if ($request->hasFile('image')) {
Once the validation is done, we may retrieve all of the input data using:
1 $file = $request->file('image');
1 $name = $file->getClientOriginalName();
Sweet! If everything looks good, we can store the file at our public/images folder:
Finally, redirect users back to our home page and display a status message:
1 <?php
2
3 namespace App\Http\Controllers;
4
5 use App\Http\Requests;
6 use App\Http\Requests\ImageFormRequest;
7
8 class ImagesController extends Controller
9 {
10 public function store(ImageFormRequest $request)
11 {
12
13 if ($request->hasFile('image')) {
14
15 $file = $request->file('image');
16
17 $name = $file->getClientOriginalName();
18
19 $file->move(public_path() . '/images/', $name);
20
21 return redirect('/')->with('status', 'Your image has been uploaded s\
22 uccessfully!');
23 }
24
25 }
26 }
1 Route::post('upload', 'ImagesController@store');
Well done! Now when users make a POST request to this route, Laravel will execute the
ImagesController’s store action.
Let’s test what we’ve just made to make sure that it works properly:
Chapter 1: Back End Recipes 58
Sample image
Let’s view the site in your browser and try to upload the image:
Check your public/images directory, you will see the image there.
Displaying images
This is the simple part. Once we have that image file, we can easily display it on our home page.
Open home.blade.php, find:
1 <div class="panel-heading">Dashboard</div>
Add below:
²⁴images/chap1-pic24.png
Chapter 1: Back End Recipes 59
Cover image
Note: If you’re using your own image, be sure to change the file name.
Manipulating images
What’s the point in installing Intervention Image if we don’t use it? It’s time to use “the power” of
Intervention Image to manipulate our images!
Note: We talked about Intervention Image earlier and this will be your first use of it.
Let’s start by telling Intervention Image where our image is. First, open the ImagesController and
find:
1 if ($request->hasFile('image')) {
2
3 $file = $request->file('image');
4
5 $name = $file->getClientOriginalName();
6
7 $file->move(public_path().'/images/', $name);
Add below:
Chapter 1: Back End Recipes 60
1 $imagePath = public_path().'/images/'.$name;
When we have the path of our image, this is how we resize the image:
Now try to upload the image again and check its size. The image should be resized to 1000x250 px:
Greyscale API
²⁵http://image.intervention.io/
Chapter 1: Back End Recipes 61
Recipe 6 Wrap-up
Tag: Version 0.4 - Recipe 6²⁶
Image upload is a hard concept to grasp if you are new to Laravel development. However, by using
Intervention Image and Laravel Request, we have just created our first image upload function! As
you see, the syntax is fairly straightforward.
What is Faker?
Faker is a PHP library that we use to generate dummy data. It can be used to generate all sorts of
data for testing purposes or bootstrapping our applications.
If you’re using Laravel 5.1 or newer, the Faker library has been already installed by default.
If you’re an old version of Laravel, you can install Faker by running this Composer command:
• Random Digit
• Word
• Paragraph
• Name
• City
• Year
• Domain Name
• Credit Card Number
²⁶https://github.com/LearningLaravel/cookbook/releases/tag/v0.4
Chapter 1: Back End Recipes 62
Note: You can generate the Post model and its migration at the same time by adding
the -m option.
²⁷https://github.com/fzaninotto/Faker
Chapter 1: Back End Recipes 63
Add above:
1 use App\Post;
2 use Faker\Factory as Faker;
3 use Illuminate\Support\Str;
The last part needed to seed data is to run this Artisan command:
20 new posts
1 Route::get('/blog', 'BlogController@index');
Tell Laravel that you want to use the Post model in this controller. Add above:
Chapter 1: Back End Recipes 65
1 use App\Post;
Create a new index view at views/blog directory. The following are the contents of the views/blog/in-
dex.blade.php file:
1 @extends('master')
2 @section('title', 'Blog')
3 @section('content')
4
5 <div class="container col-md-8 col-md-offset-2">
6
7 @if (session('status'))
8 <div class="alert alert-success">
9 {{ session('status') }}
10 </div>
11 @endif
12
13 @if ($posts->isEmpty())
14 <p> There is no post.</p>
15 @else
16 @foreach ($posts as $post)
17 <div class="panel panel-default">
18 <div class="panel-heading">{!! $post->title !!}</div>
19 <div class="panel-body">
20 {!! mb_substr($post->content,0,500) !!}
21 </div>
22 </div>
23 @endforeach
24 @endif
25 </div>
26
27 @endsection
Chapter 1: Back End Recipes 66
We use mb_substr (Multibyte String) function to display only 500 characters of the post.
If you want to learn more about the function, visit:
http://php.net/manual/en/function.mb-substr.php²⁸
We should add a blog link to our navigation bar to access the blog page faster. Open shared/-
navbar.blade.php and find:
1 <li><a href="/about">About</a></li>
Add above:
1 <li><a href="/blog">Blog</a></li>
Recipe 7 Wrap-up
Tag: Version 0.5 - Recipe 7²⁹
²⁸http://php.net/manual/en/function.mb-substr.php
²⁹https://github.com/LearningLaravel/cookbook/releases/tag/v0.5
Chapter 1: Back End Recipes 67
You now know how to use Faker to generate good-looking dummy data!
This technique can be applied to more than just posts. You can use it to create larger applications
and test your apps.
Recipe 8 - Pagination
Simple pagination
Recently, we showed all blog posts in one page. Eventually, if thousands of posts are posted, this will
become problematic. Pagination is a good solution to this overload issue.
As you may know, creating pagination from scratch is not an easy task. Luckily, Laravel has a
paginate method that we can use to create the pagination without having to write any extra code.
Let’s open our BlogController, find:
1 $posts = Post::all();
Replace with:
1 $posts = Post::paginate(10);
As you see, we use the paginate method to create the pagination. This will return an instance of
IlluminatePaginationLengthAwarePaginator.
Alternatively, you can use the new simplePaginate method. This will return an instance of
IlluminatePaginationPaginatorThis Paginator class.
1 $posts = Post::simplePaginate(10);
These objects provide several useful methods that we can use to customize and display our
pagination.
I want to display 10 posts on a page, so I put 10 as the parameter.
If you would like to use Query Builder, you may write:
Chapter 1: Back End Recipes 68
1 $posts = DB::table('posts')->paginate(10);
1 @if ($posts->isEmpty())
2 <p> There is no post.</p>
3 @else
4 @foreach ($posts as $post)
5 <div class="panel panel-default">
6 <div class="panel-heading">{!! $post->title !!}</div>
7 <div class="panel-body">
8 {!! mb_substr($post->content,0,500) !!}
9 </div>
10 </div>
11 @endforeach
12 @endif
Add below:
Good job!
Let’s give our brand new pagination system a try:
Pagination
If you use the simplePaginate method, you should now see something like this:
Simple Pagination
Chapter 1: Back End Recipes 69
• $posts->count()
• $posts->currentPage()
• $posts->hasMorePages()
• $posts->lastPage() (Not available when using simplePaginate)
• $posts->nextPageUrl()
• $posts->perPage()
• $posts->previousPageUrl()
• $posts->total() (Not available when using simplePaginate)
• $posts->url($page)
As Laravel is constantly updated, be sure to check the documentation to know all latest helper
methods:
https://laravel.com/docs/master/pagination³⁰
Ajax pagination
When creating Ajax pagination, we will need to return the pagination as JSON. The Paginator
classes implement the IlluminateContractsSupportJsonableInterface contract and have toJson
method. That means you can easily convert the result instance to JSON by simply returning it from
a route or controller action.
Let’s try to return the pagination as JSON from our BlogController’s index action:
1 use Response;
2 class BlogController extends Controller
3 {
4 public function index()
5 {
6 $posts = Post::paginate(10);
7 $response = Response::json($posts,200);
8 return $response;
9 }
10 }
³⁰https://laravel.com/docs/master/pagination
Chapter 1: Back End Recipes 70
Now let’s try to return the instance from a route. Open our routes.php file, add a new route:
1 Route::get('json', function () {
2 return App\Post::paginate();
3 });
Recipe 8 Wrap-up
Tag: Version 0.6 - Recipe 8³¹
Great! Having the knowledge of the above will let you create a simple pagination or ajax pagination
in no time.
Now this won’t do much for our app yet, but these techniques can be used to build many applications
in all sorts of different styles.
Testing is really boring and there’s a lot of code which is hard to unit test properly. That is true.
However, not having a comprehensive test suite means that our applications may not meet the
stated requirements and we’re taking more risks.
“Even good programmers make mistakes. The difference between a good programmer
and a bad one is that a good one detects it sooner by using automated tests” - Sebastian
Bergmann
That may sound a bit exaggerated, but we should know how to write and test our code effectively
to improve the quality of our applications.
To beginners of Laravel, testing can truly seem like a very difficult job. But don’t worry.
Remember that, if you can write PHP, you can write tests.
Now let’s get started and talk more about these concepts as we go along!
• Manual Testing: is the process of running series of tasks manually to find the defects in our
applications.
• Automated Testing: is the process of using automated tools to run tests based on algorithms
to check your applications.
There are different types of automated tests. Here are the popular ones:
• Unit tests
• Integration tests
• Acceptance tests (aka Functional tests)
In this recipe, we will talk about some useful tools and techniques that we can use to manually test
our app. We also learn about PHPUnit and write some automated tests to check our application.
1 $response = Response::json($posts,200);
2 dd($response);
1 dd("This is a test");
Amazing, right?
You’ll be using this function a lot since it’s very useful for showing off data and debugging our app.
Alternatively, we can use var_dump() and print_r() PHP function to display the information about
a variable. These functions are very useful when working with arrays.
W3resouce has a really good tutorial about them, you may check it out at:
http://www.w3resource.com/php/function-reference/var_dump.php³²
Recently, Kint - a powerful and modern PHP debugging tool - is also becoming very popular.
It’s a good replacement for var_dump() and print_r(). Using Kint in conjunction with the dd()
function is a powerful combination.
You can install Kint by simply running this Composer command:
1 "require": {
2 "raveren/kint": "^1.0"
3 }
Chrome DevTools
Want to learn how to use Devtools? CodeSchool has a nice video course about it (free):
http://discover-devtools.codeschool.com³⁵
You may also check the Chrome Devtools’ documentation:
https://developers.google.com/web/tools/chrome-devtools/iterate/inspect-styles/shortcuts ³⁶
vardumpling() extension:³⁷ This Google Chrome extension beautifies your var_dumps and makes
them easier to read.
³⁴https://developers.google.com/web/tools/chrome-devtools/iterate/inspect-styles/shortcuts
³⁵http://discover-devtools.codeschool.com
³⁶https://developers.google.com/web/tools/chrome-devtools/iterate/inspect-styles/shortcuts
³⁷https://chrome.google.com/webstore/detail/vardumpling/aikblkmigebodlhkdepmfmgdgmbokkdn
Chapter 1: Back End Recipes 76
vardumpling
JSON Formatter extension:³⁸ Similar to the vardumpling extension, if you want to view JSON files
directly on your browser, you’ll love this Chrome’s plugin.
³⁸https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa
Chapter 1: Back End Recipes 77
JSON Formatter
Postman:³⁹ This is the most popular extension for working with APIs. Postman helps us build, test,
and document APIs faster.
³⁹https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en
Chapter 1: Back End Recipes 78
Postman
Clockwork:⁴⁰ Chrome DevTools doesn’t support PHP by default, but we can extend the DevTools
with a new panel that supports PHP and Laravel. Once installed, we can debug, view cookies/sessions
data, run database queries, etc.
⁴⁰https://chrome.google.com/webstore/detail/clockwork/dmggabnehkmmfmdffgajcflpdjlnoemp
Chapter 1: Back End Recipes 79
Clockwork
Laravel Debug Bar:⁴¹ Laravel Debugbar is one of the best Laravel packages. It adds a “debug bar”
to our application. Using the bar, we can view all important information of our site, such as: queries,
routes, views, collections, etc.
It’s very easy to install the package. First, run this Composer command:
After that, open your config/app.php file and add Debugbar’s ServiceProvider to the providers
array:
1 Barryvdh\Debugbar\ServiceProvider::class,
Go ahead and view our app in the browser, you’ll see a nice debug bar:
Debugbar
⁴²https://github.com/barryvdh/laravel-debugbar
Chapter 1: Back End Recipes 81
you may know that all our test files are placed in the tests directory.
Let’s go to the tests directory, we should see two files:
TestCase.php:
Chapter 1: Back End Recipes 82
1 <?php
2
3 class TestCase extends Illuminate\Foundation\Testing\TestCase
4 {
5 /**
6 * The base URL to use while testing the application.
7 *
8 * @var string
9 */
10 protected $baseUrl = 'http://localhost';
11
12 /**
13 * Creates the application.
14 *
15 * @return \Illuminate\Foundation\Application
16 */
17 public function createApplication()
18 {
19 $app = require __DIR__.'/../bootstrap/app.php';
20
21 $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
22
23 return $app;
24 }
25 }
Basically, we don’t need to worry about this file, it’s just a base class. If we want to write new
tests, simply extend this TestCase class.
If you want to use a different URL while testing your application, you may change the URL here.
ExampleTest.php:
1 <?php
2
3 use Illuminate\Foundation\Testing\WithoutMiddleware;
4 use Illuminate\Foundation\Testing\DatabaseMigrations;
5 use Illuminate\Foundation\Testing\DatabaseTransactions;
6
7 class ExampleTest extends TestCase
8 {
9 /**
10 * A basic functional test example.
Chapter 1: Back End Recipes 83
11 *
12 * @return void
13 */
14 public function testBasicExample()
15 {
16 $this->visit('/')
17 ->see('Laravel 5');
18 }
19 }
This is a test. As you see, it’s just a method. If you write a new method (a new test), your method
must be public and you must start them with test. It’s a naming convention.
By reading the testBasicExample test, can you guess what it does?
Very simple! It says: “Visit our home page (/) and see the words Laravel 5”.
Now, let’s try to run our first test!
Vagrant ssh into your homestead, navigate to your app, and run this command:
1 vendor/bin/phpunit
Because we don’t have the words Laravel 5 on our home page, the test should fail.
We should see a red message.
First test
1 <div class="panel-heading">Dashboard</div>
Replace with:
1 vendor/bin/phpunit
Second test
• ->assertViewMissing($key): Assert that the response view is missing a piece of bound data.
• ->assertRedirectedTo($uri, $with = []): Assert whether the client was redirected to a given
URI.
• ->assertRedirectedToRoute($name, $parameters = [], $with = []): Assert whether the client
was redirected to a given route.
• ->assertRedirectedToAction($name, $parameters = [], $with = []): Assert whether the
client was redirected to a given action.
• ->assertSessionHas($key, $value = null): Assert that the session has a given value.
• ->assertSessionHasAll(array $bindings): Assert that the session has a given list of values.
• ->assertSessionHasErrors($bindings = [], $format = null): Assert that the session has errors
bound.
• ->assertHasOldInput(): Assert that the session has old input.
I get these methods from the testing section of Laravel’s documentation⁴⁴. Be sure to check it out!
1 <?php
2
3 use Illuminate\Foundation\Testing\WithoutMiddleware;
4 use Illuminate\Foundation\Testing\DatabaseMigrations;
5 use Illuminate\Foundation\Testing\DatabaseTransactions;
6
7 class BlogTest extends TestCase
8 {
9 /**
10 * A basic test example.
11 *
12 * @return void
13 */
14 public function testExample()
15 {
⁴⁴https://laravel.com/docs/master/testing
Chapter 1: Back End Recipes 86
16 $this->assertTrue(true);
17 }
18 }
We don’t need the testExample method, so just remove it and write our new test:
1 vendor/bin/phpunit
Open our BlogController, if the response has the 200 status code…
1 $response = Response::json($posts,200);
2 return $response;
Second test
Another thing to note is that we can use multiple methods to create our test:
Chapter 1: Back End Recipes 87
Recipe 9 Wrap Up
Tag: Version 0.7 - Recipe 9⁴⁵
Up to this point, we have learned some useful testing techniques and created several unit tests.
You should be able to test your application effectively now.
Remember that, the more tests that you create, the more your testing skills will be improved.
Practice makes perfect.
When APIs use REST architecture, they are called REST APIs (aka RESTful APIs).
A typical REST API has these following constraints⁴⁶:
• Client - server: Servers (back end) and clients (front end) can be developed independently.
• Stateless: Session state should be stored on the client. Client data should not be stored on the
server between requests.
• Cacheable: Client can cache responses to improve scalability and performance.
REST API use HTTP requests to communicate with the servers. Each request specifies a certain
HTTP verb in the request header, such as:
There are many HTTP verbs, but the most popular ones for building REST APIs are:
• GET
• POST
• PUT
• DELETE
Imgur API
In this section, let’s move onto creating a new endpoint that lists all blog posts.
Chapter 1: Back End Recipes 90
1 Route::resource('posts', 'PostsController');
As you see, we don’t use Route::get or Route::post here, we use Route:resource. In Laravel, this is
a resourceful route.
This route tells Laravel to create multiple routes to handle a variety of RESTful actions on the
posts resource.
Simply put, instead of creating multiple routes manually:
1 Route::get('posts', 'PostsController@index');
2 Route::post('posts', 'PostsController@store');
3 ...
we may just use a resourceful route and Laravel will automatically generate all the related routes
for us.
Once again, when having a new route, we may need a new controller.
We don’t have the PostsController yet. Let’s create one by running this Artisan command:
By adding a –resource flag, Laravel generates a new resource controller for us, instead of a plain
controller.
1 <?php
2
3 namespace App\Http\Controllers;
4
5 use Illuminate\Http\Request;
6
7 use App\Http\Requests;
8 use App\Http\Controllers\Controller;
9
10 class PostsController extends Controller
11 {
Chapter 1: Back End Recipes 91
12 /**
13 * Display a listing of the resource.
14 *
15 * @return \Illuminate\Http\Response
16 */
17 public function index()
18 {
19 //
20 }
21
22 /**
23 * Show the form for creating a new resource.
24 *
25 * @return \Illuminate\Http\Response
26 */
27 public function create()
28 {
29 //
30 }
31
32 /**
33 * Store a newly created resource in storage.
34 *
35 * @param \Illuminate\Http\Request $request
36 * @return \Illuminate\Http\Response
37 */
38 public function store(Request $request)
39 {
40 //
41 }
42
43 /**
44 * Display the specified resource.
45 *
46 * @param int $id
47 * @return \Illuminate\Http\Response
48 */
49 public function show($id)
50 {
51 //
52 }
53
Chapter 1: Back End Recipes 92
54 /**
55 * Show the form for editing the specified resource.
56 *
57 * @param int $id
58 * @return \Illuminate\Http\Response
59 */
60 public function edit($id)
61 {
62 //
63 }
64
65 /**
66 * Update the specified resource in storage.
67 *
68 * @param \Illuminate\Http\Request $request
69 * @param int $id
70 * @return \Illuminate\Http\Response
71 */
72 public function update(Request $request, $id)
73 {
74 //
75 }
76
77 /**
78 * Remove the specified resource from storage.
79 *
80 * @param int $id
81 * @return \Illuminate\Http\Response
82 */
83 public function destroy($id)
84 {
85 //
86 }
87 }
Believe it or not, by just running two commands, we have all RESTful routes and actions that we
need to make an API endpoint.
To make sure that we have all the posts’ routes, you can list all your application’s routes by running
the following command:
Posts’ routes
Posts’ routes
If you want to learn more about RESTful resource controllers, you may take a look at the official
documentation⁴⁸.
Next, open PostsController and update the index action as follows:
⁴⁸https://laravel.com/docs/master/controllers#restful-resource-controllers
Chapter 1: Back End Recipes 94
Go ahead and visit http://cookbook.app/posts⁴⁹, you should see all the blog posts in JSON format:
posts route
Our API will likely change over time. One day, we may need to change our code significantly to
add more features or restructure our application. Therefore, we should version our API from the
beginning.
As you know, Laravel 5.2 has introduced a new feature called middleware groups and we’ve used
the web middleware group in previous recipes. Let’s open the app/Http/Kernel.php file:
1 protected $middlewareGroups = [
2 'web' => [
3 \App\Http\Middleware\EncryptCookies::class,
4 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
5 \Illuminate\Session\Middleware\StartSession::class,
6 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
7 \App\Http\Middleware\VerifyCsrfToken::class,
8 ],
9
10 'api' => [
11 'throttle:60,1',
12 ],
13 ];
As you see, there is another middleware group called api. When building APIs with Laravel 5.2 (or
newer), it’s better to utilize this middleware group.
Open routes.php, add our posts resourceful route into the api middleware group:
In order to version our API, we also add the prefix (api/v1) to the group. Now we can access our
first API endpoint at http://cookbook.app/api/v1/posts⁵⁰.
In the future, if we want to develop a new version of our APIs, all we have to do is creating a new
middleware group!
Another thing to note, you may see the words throttle:60,1 in the api middleware group:
1 'api' => [
2 'throttle:60,1',
3 ],
Well, it’s the API rate limiting feature of Laravel. If a user (or a bot) is hitting our API endpoint
a million times a minute, our application would be still running fine. When they try to make too
many requests in a short time, they will get this message:
⁵⁰http://cookbook.app/api/v1/posts
Chapter 1: Back End Recipes 96
The default throttle allows users to make 60 requests per minute. They can’t access our application
for one minute if they hit the limit.
Feel free to change the limit to whatever you want.
Postman
⁵¹https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en
Chapter 1: Back End Recipes 97
Pagination
Usually, we don’t want to display all posts at once. As you may have guessed, we can easily paginate
our posts by using the paginate method.
Open PostsController and update the index action as follows:
Pagination
Status Code
After sending requests, we usually get back a message with a status code. This status code is called
HTTP Response Code. It’s very important to understand those status codes because they are used
to express various success and failure states of our application.
You may view a list of response codes here:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html⁵²
These are the most common ones:
• 201: Created - The request has been fulfilled and resulted in a new resource being created.
• 202: Accepted - The request has been accepted for processing, but the processing has not been
completed.
• 204: No Content - The server has fulfilled the request but does not need to return an entity-
body, and might want to return updated metainformation.
• 301: Moved Permanently - The requested resource has been assigned a new permanent URI
and any future references to this resource should use one of the returned URIs.
• 302: Moved Temporarily - The requested resource resides temporarily under a different URI.
• 400: Bad Request - The request could not be understood by the server due to malformed
syntax.
• 401: Unauthorized - The request requires user authentication.
• 403: Forbidden - The server understood the request, but is refusing to fulfill it.
• 404: Not Found - The server has not found anything matching the Request-URI.
• 500: Internal Server Error - The server encountered an unexpected condition which prevented
it from fulfilling the request.
1 single post
Cool! Now we have the post and the status code is 200. Everything’s working fine.
If we enter a wrong id, we should get this message:
Wrong id
Now the status code is 404. We know that the post doesn’t exist.
1 <?php
2
3 namespace App;
4
5 use Illuminate\Database\Eloquent\Model;
6
7 class Post extends Model
8 {
9 protected $fillable = [
10 'title', 'content', 'slug', 'status',
Chapter 1: Back End Recipes 102
11 ];
12
13 }
The $fillable property should have the following: title, content, slug, status.
Be sure that we’ve told Laravel to use Str and Request:
1 <?php
2
3 namespace App\Http\Controllers;
4
5 use Illuminate\Http\Request;
6
7 use App\Http\Requests;
8 use App\Http\Controllers\Controller;
9
10 use App\Post;
11 use Response;
12 use Illuminate\Support\Str;
13
14 class PostsController extends Controller
15 {
If we only enter the title, there should be an error message. The status code is 422.
If we enter everything correctly, we should be able to create a new post:
Chapter 1: Back End Recipes 103
Updating a post
Now that we have created a new post. Let’s look at how we can use PUT request to update a post.
The API endpoint should be /posts/{postid} and the action that we use is update.
Open PostsController, update the update action as follows:
17 $post->save();
18
19 $response = Response::json([
20 'message' => 'The post has been updated.',
21 'data' => $post,
22 ], 200);
23
24 return $response;
25 }
Deleting a post
In our last section, we used PUT request to update our posts. We’ll follow the same process that we
used to delete a post, but this time, we use DELETE request.
The API endpoint should be /posts/{postid} and the action that we use is destroy.
Open PostsController and update the destroy action as follows:
⁵⁴http://cookbook.app/api/v1/posts/2
Chapter 1: Back End Recipes 105
Now let’s try to run again, but this time, enter 5 as the post’s id.
Adding CORS
CORS⁵⁶ stands for Cross-Origin Resource Sharing, which is a mechanism that allows modern
browsers to send and receive restricted data (images, fonts, files, etc.) from a domain other than the
one that made the request.
Simply put, if we don’t enable CORS, we can’t access our API from other applications.
To enable CORS, we may build a custom middleware⁵⁷ to add CORS header to our response. This is
a simple method, but the simplest one is to use a popular package called laravel-cors⁵⁸.
To install the package, run this Composer command:
Once installed, open config/app.php and add the CorsServiceProvider to our providers array:
1 Barryvdh\Cors\ServiceProvider::class,
Next, open routes.php and add the cors middleware to our api middleware group:
That’s it!
Our API is now working properly!
⁵⁶https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
⁵⁷http://learninglaravel.net/laravel-51-easily-enable-cors
⁵⁸https://github.com/barryvdh/laravel-cors
Chapter 1: Back End Recipes 107
Chapter 10 Wrap-up
Tag: Version 0.8 - Recipe 10⁵⁹
Congratulations! There we have it! A Laravel application that can be used as a backend service for
mobile applications or AJAX-based websites.
Now let’s move onto Chapter 2. We will learn some front end recipes to improve user experience.
In the future, I’ll add more backend recipes, so that we can learn more about API and other Laravel
features.
This is just a beginning.
⁵⁹https://github.com/LearningLaravel/cookbook/releases/tag/v0.8
Chapter 2: Front End Recipes
Introduction
Whether you are a beginner or intermediate web developer, if you wish to make good interactive
web applications, then this chapter is for you.
In this chapter, you’ll be getting some recipes about front-end web technologies and popular front-
end tools. These recipes cover best practices and modern techniques for front-end development
such as: integrating Twitter Bootstrap, AJAX loading, notifications, file uploads, cropping images
and many more.
By the end, you should have a better understanding of how to work with AJAX, Jquery, front end
frameworks and responsive design. You can apply these techniques to build beautiful applications
and add that interactivity to any site you work on.
List Of Recipes
Frontend recipes
• Recipe 201 - Notifications
• Recipe 202 - Integrating Buttons With Built-in Loading Indicators
• Recipe 203 - Create A Registration Page Using AJAX and jQuery
• Recipe 204 - Create A Login Page Using AJAX And jQuery
• Recipe 205 - Upload Files Using AJAX And jQuery
• Recipe 206 - Cropping Images Using jQuery
108
Chapter 2: Front End Recipes 109
SweetAlert
⁶⁰http://wavded.github.io/humane-js
⁶¹http://github.hubspot.com/messenger/docs/welcome/
⁶²http://t4t5.github.io/sweetalert
Chapter 2: Front End Recipes 110
1 "uxweb/sweet-alert": "~1.1"
1 UxWeb\SweetAlert\SweetAlertServiceProvider::class,
Add below:
⁶³https://github.com/uxweb/sweet-alert
⁶⁴https://github.com/t4t5/sweetalert/archive/master.zip
⁶⁵https://github.com/limonte/sweetalert2
Chapter 2: Front End Recipes 111
Find:
1 </body>
Add above:
1 <script src="/js/sweetalert.min.js"></script>
2 @include('sweet::alert')
1 <html>
2 <head>
3 <title> @yield('title') </title>
4 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6\
5 /css/bootstrap.min.css">
6 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6\
7 /css/bootstrap-theme.min.css">
8 <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/fo\
9 nt-awesome.min.css" rel='stylesheet'
10 type='text/css'>
11 <link rel="stylesheet" href="/css/sweetalert.css">
12
13 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
14 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.mi\
15 n.js"></script>
16 </head>
17 <body>
18
19 @include('shared.navbar')
20
21 @yield('content')
22
23 <script src="/js/sweetalert.min.js"></script>
24 @include('sweet::alert')
25 </body>
26 </html>
1 return view('home');
Add above:
Done! Head over to our home page and refresh the page:
⁶⁶http://t4t5.github.io/sweetalert
Chapter 2: Front End Recipes 113
This is how we show our first notification! Very simple, isn’t it?
We’ve just used Sweet Alert Facade to display the notification. Alternatively, we may use Sweet
Alert Helper to accomplish the same result:
Find:
Replace with:
A successful notification
When adding persistent(‘Your Custom Text’), users must click the button to close the notification.
An error notification
When using autoclose(‘time’), the notification will be closed automatically after the defined time
has passed.
Chapter 2: Front End Recipes 116
Installing Ladda
When building modern applications, it’s important to provide some creative loading effects to
improve user experience. In this section, I’ll show you how to install Ladda⁶⁸ - a popular Javascrip-
t/Jquery plugin that we can use to make button loading effects.
⁶⁷https://github.com/LearningLaravel/cookbook/releases/tag/v0.9
⁶⁸https://github.com/hakimel/Ladda
Chapter 2: Front End Recipes 117
Ladda
Add below:
Find:
1 </body>
Add above:
1 <script src="/js/spin.min.js"></script>
2 <script src="/js/ladda.min.js"></script>
3 <script src="/js/custom_script.js"></script>
Note: You may use jQuery instead. If you use jQuery, be sure to use the ladda.jquery.min.js
file. Read the Ladda documentation⁷¹ to know about that method. Feel free to use Gulp,
Elixir or other tools to minify the custom_script.js file.
⁷¹https://github.com/hakimel/Ladda
Chapter 2: Front End Recipes 120
As you see, we can choose the effect by setting the data-style attribute:
1 data-style="expand-left"
That’s it! Go ahead and click the Register button, you should see the loading effect:
Register button
Of course that we can put the spinner inside our Login With Facebook button as well:
1 <a href="/login/facebook">
2 <div class="btn btn-md btn-primary ladda-button" data-style="expand-left">
3 <i class="fa fa-facebook"></i> Login with Facebook </div>
4 </a>
If you like the style of original Ladda buttons that we just see in the demo. Open our master layout,
and change:
To:
Chapter 2: Front End Recipes 121
Ladda buttons
Using ladda.min.css, we may change buttons’ size and color by using the data-size and data-color
attribute.
Ladda buttons
This should give you a sample of how to use Ladda. Let’s try to change other buttons by yourself to
create some effects that fit your needs.
Using loading effects is very important when working with AJAX, because it’s a great way to inform
users that we’re processing their requests.
⁷²https://github.com/LearningLaravel/cookbook/releases/tag/v0.10
Chapter 2: Front End Recipes 123
1 Route::get('users/register', 'Auth\AuthController@getRegister');
2 Route::post('users/register', 'Auth\AuthController@postRegister');
Note: You may use different routes, different actions or different controllers if you want.
By now you should be a pro at handling views, so let’s create a new view called ajax_register
(resources/views/auth/ajax_register.blade.php):
Chapter 2: Front End Recipes 124
1 @extends('layouts.app')
2
3 @section('content')
4 <div class="container">
5 <div class="row">
6 <div class="col-md-8 col-md-offset-2">
7 <div class="panel panel-default">
8 <div class="panel-heading">AJAX Register</div>
9 <div class="panel-body">
10 <form class="form-horizontal" id="registration" method="\
11 POST" action="{{ url('/users/register') }}">
12 {!! csrf_field() !!}
13
14 <div class="form-group">
15 <label class="col-md-4 control-label">Name</labe\
16 l>
17
18 <div class="col-md-6">
19 <input type="text" class="form-control" name\
20 ="name">
21 </div>
22 </div>
23
24 <div class="form-group">
25 <label class="col-md-4 control-label">E-Mail Add\
26 ress</label>
27
28 <div class="col-md-6">
29 <input type="email" class="form-control" nam\
30 e="email">
31
32 </div>
33 </div>
34
35 <div class="form-group">
36 <label class="col-md-4 control-label">Password</\
37 label>
38
39 <div class="col-md-6">
40 <input type="password" class="form-control" \
41 name="password" id="password">
42
Chapter 2: Front End Recipes 125
43 </div>
44 </div>
45
46 <div class="form-group">
47 <label class="col-md-4 control-label">Confirm Pa\
48 ssword</label>
49
50 <div class="col-md-6">
51 <input type="password" class="form-control" \
52 name="password_confirmation">
53 </div>
54 </div>
55
56 <div class="form-group">
57 <div class="col-md-6 col-md-offset-4">
58 <button type="submit" class="btn btn-primary\
59 ladda-button" data-style="expand-left"
60 data-size="s" data-color="green">
61 <i class="fa fa-btn fa-user"></i> Regist\
62 er
63 </button>
64 <a href="/login/facebook"> <div class="btn b\
65 tn-md btn-primary ladda-button"
66 data-style="expand-left" data-size="s" data-\
67 color="blue">
68 <i class="fa fa-facebook"></i> Login wit\
69 h Facebook </div></a>
70 </div>
71 </div>
72 </form>
73 </div>
74 </div>
75 </div>
76 </div>
77 </div>
78 @endsection
Of course, this view is used to display our AJAX registration form. As you can see, it’s just a simple
HTML form. Because this will be an AJAX form, we don’t need to use session or the errors variable
here.
Chapter 2: Front End Recipes 126
• Parsley⁷⁴
• Validate.js⁷⁵
• jQuery Validation Plugin⁷⁶
• Verify.js⁷⁷
• gvalidator⁷⁸
⁷³http://cookbook.app/users/register
⁷⁴http://parsleyjs.org
⁷⁵http://rickharrison.github.io/validate.js/
⁷⁶http://jqueryvalidation.org
⁷⁷http://verifyjs.com
⁷⁸https://code.google.com/archive/p/gvalidator
Chapter 2: Front End Recipes 127
Parsley is the most popular one, so we will use it to add inline validation to our registration form.
To install Parsley, you may choose one of the following methods:
Method 1: Using a CDN. Open our master layout (app.blade.php) and find:
1 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
Add below:
1 <script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.3.5/parsley.min\
2 .js"></script>
1 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
Add below:
1 <script src="/js/parsley.min.js"></script>
Change to:
Change to:
You might have noticed that we’re adding the required attribute to our input fields. Users must
enter all the required fields before submitting the form.
We also use data-parsley-equalto=”#password” to make sure that the value of our password_-
confirmation field must be the same with the password field’s value.
Let’s give our brand new inline validation system a try.
Chapter 2: Front End Recipes 129
If we enter wrong values and click the Register button. We should see some errors immediately.
Parsley also detects the email field automatically and validate the field for us!
Amazing! Right?
The great thing is, we can customize all Parsley’s classes and elements in the DOM when it
validates.
Let’s create a new app.css stylesheet and place it at public/css/app.css.. Add the following:
1 input.parsley-success,
2 select.parsley-success,
3 textarea.parsley-success {
4 color: #468847;
5 background-color: #DFF0D8;
6 border: 1px solid #D6E9C6;
7 }
8
9 input.parsley-error,
10 select.parsley-error,
11 textarea.parsley-error {
12 color: #B94A48;
13 background-color: #F2DEDE;
14 border: 1px solid #EED3D7;
Chapter 2: Front End Recipes 130
15 }
16
17 .parsley-errors-list {
18 margin: 2px 0 3px;
19 padding: 0;
20 list-style-type: none;
21 font-size: 0.9em;
22 line-height: 0.9em;
23 opacity: 0;
24
25 transition: all .3s ease-in;
26 -o-transition: all .3s ease-in;
27 -moz-transition: all .3s ease-in;
28 -webkit-transition: all .3s ease-in;
29 }
30
31 .parsley-errors-list.filled {
32 opacity: 1;
33 }
Add below:
Add below:
We can now access our AJAX registration and AJAX login page via the main menu.
1 <script src="/js/custom_script.js"></script>
2 </body>
3 </html>
1 $( document ).ready(function() {
2
3 // Our code will be here.
4
5 });
We have to put our code inside the ready event. When the document is ready, our code will run
without waiting for other assets (images, files, etc.) to load.
Alternatively, you may use:
1 window.onload = function() {
2
3 // Our code will be here.
4
5 };
or simply use:
1 $(function() {
2 // Our code will be here.
3 };
Note: Please note that the last two methods are not recommended.
Choose one of the methods above, and put the code at the end of our custom_script.js file.
Next, we need to find our registration form by using the following:
1 $( document ).ready(function() {
2
3 var form = $('#registration');
4
5 });
As you see, we just use jQuery ID Selector to select our registration form. Please note that our
registration form must have the id attribute:
Chapter 2: Front End Recipes 133
Once the form is selected, we use e.preventDefault() method to prevent the submit button (the
Register button) from submitting the form using the default action. Simply put, our browser should
understand that we want to use the Register button to do other things. If we try to click the button
now, it does nothing.
1 $( document ).ready(function() {
2
3 var form = $('#registration');
4
5 form.submit(function(e){
6 e.preventDefault();
7 });
8 });
This final step is interesting. We will use jQuery’s $.ajax() function to send an asynchronous HTTP
request:
1 $(document).ready(function () {
2
3 var form = $('#registration');
4
5 form.submit(function (e) {
6 e.preventDefault();
7
8 $.ajax({
9 url: form.attr('action'),
10 type: "POST",
11 data: form.serialize(),
12 dataType: "json"
13 })
14 .done(function (response) {
15 // If the request succeeds, do something
16 })
17 .fail(function () {
18 // If the request fails, do something
19 });
20 });
21 });
Chapter 2: Front End Recipes 134
1 $(document).ready(function () {
2
3 var form = $('#registration');
4
5 form.submit(function (e) {
6 e.preventDefault();
7
8 $.ajax({
9 url: form.attr('action'),
10 type: "POST",
11 data: form.serialize(),
12 dataType: "json"
13 })
14 .done(function (response) {
15 if (response.success) {
16 swal({
17 title: "Hi " + response.name,
18 text: response.success,
19 timer: 2000,
20 showConfirmButton: false,
21 type: "success"
22 });
23 window.location.replace(response.url);
24 } else {
25 swal("Oops!", response.errors, 'error');
26 }
27 })
28 .fail(function () {
⁸¹http://www.formget.com/javascript-serialize
Chapter 2: Front End Recipes 135
1 swal({
2 title: "Hi " + response.name,
3 text: response.success,
4 timer: 2000,
5 showConfirmButton: false,
6 type: "success"
7 });
1 window.location.replace(response.url);
If we can’t register a new member, we use Sweet Alert to display the errors:
If the request fails (server problems) we also use Sweet Alert to trigger error messages.
Because we haven’t built the backend (server) yet, we should see an error message.
Let’s build the backend!
You may notice that we’ve just created some validation rules. If you’re not familiar with this, please
take a look at the documentation⁸²
⁸²https://laravel.com/docs/master/validation#available-validation-rules
Chapter 2: Front End Recipes 137
1 **Note:** You may create a RegisterFormRequest to validate the form or change th\
2 e rules if you want.
Next, if the validation fails, an error response will be generated to notify users. If the form is valid,
a successful response will be generated, a new user will be created and we will send the user to a
preferred location (dashboard, for example):
This might all be a lot to take in all at once, but the code is pretty easy.
Notice that we also use Auth::login to log the user in.
If we’ve done our job properly, we now have a working AJAX registration form!
Chapter 2: Front End Recipes 138
If everything is fine, a new user should be created, we see a successful message and we’re redirected
to another location.
Congratulations! By now you should have a good grasp of how to build an AJAX registration form.
This technique can be used to build many other AJAX forms. Go ahead and try to build another
form to test your skill.
You may try to build the login form as well. I know that you can do it!
1 Route::get('users/login', 'Auth\AuthController@getLogin');
2 Route::post('users/login', 'Auth\AuthController@postLogin');
⁸³https://github.com/LearningLaravel/cookbook/releases/tag/v0.11
Chapter 2: Front End Recipes 140
1 @extends('layouts.app')
2
3 @section('content')
4 <div class="container">
5 <div class="row">
6 <div class="col-md-8 col-md-offset-2">
7 <div class="panel panel-default">
8
9 <div class="panel-heading">AJAX Login</div>
10 <div class="panel-body">
11
12 <form class="form-horizontal" id="login" method="POST" a\
13 ction="{{ url('/login') }}">
14 {!! csrf_field() !!}
15
16 <div class="form-group">
17 <label class="col-md-4 control-label">E-Mail Add\
18 ress</label>
19
20 <div class="col-md-6">
21 <input type="email" class="form-control" nam\
22 e="email">
23 </div>
24 </div>
25
26 <div class="form-group">
27 <label class="col-md-4 control-label">Password</\
28 label>
29
30 <div class="col-md-6">
31 <input type="password" class="form-control" \
32 name="password">
33 </div>
34 </div>
35
36 <div class="form-group">
37 <div class="col-md-6 col-md-offset-4">
38 <div class="checkbox">
39 <label>
40 <input type="checkbox" name="remembe\
41 r"> Remember Me
42 </label>
Chapter 2: Front End Recipes 141
43 </div>
44 </div>
45 </div>
46
47 <div class="form-group">
48 <div class="col-md-6 col-md-offset-4">
49 <button type="submit" class="btn btn-primary\
50 ladda-button" data-style="expand-left"
51 data-size="s" data-color="green">
52 <i class="fa fa-btn fa-sign-in"></i> Log\
53 in
54 </button>
55 <a href="/login/facebook">
56 <div class="btn btn-md btn-primary ladda\
57 -button" data-style="expand-left"
58 data-size="s" data-color="blue"><i \
59 class="fa fa-facebook"></i> Login with
60 Facebook
61 </div>
62 </a>
63 <a class="btn btn-link" href="{{ url('/passw\
64 ord/reset') }}">Forgot Your
65 Password?</a>
66 </div>
67 </div>
68 </form>
69 </div>
70 </div>
71 </div>
72 </div>
73 </div>
74 @endsection
Note: Please read the previous recipe to learn how to install and use Parsley if you don’t
know what Parsley is.
As you may already know, we need to add data-parsley-validate to the form that we want to be
validated.
Open the ajax_login view and find:
Change to:
Update to:
Well done! We’ve integrated inline validation into our login form!
Inline validation
1 <script src="/js/custom_script.js"></script>
2 </body>
3 </html>
1 $( document ).ready(function() {
2
3 // Registration form (previous recipe)
4
5 // Our code will be here.
6
7 });
Once the form is selected, don’t forget to use e.preventDefault() method to prevent the submit
button (the Login button) from submitting the form using the default action.
After that, we can use the $.ajax() function to submit the form:
13 if (response.success) {
14 swal({
15 title: "Welcome back!",
16 text: response.success,
17 timer: 5000,
18 showConfirmButton: false,
19 type: "success"
20 });
21
22 window.location.replace(response.url);
23
24 } else {
25 swal("Oops!", response.errors, 'error');
26 }
27 })
28 .fail(function () {
29 swal("Fail!", "Cannot login now!", 'error');
30 });
31 });
Just like we previously set up the registration form, we’ll use Sweet Alert to display a successful
message and we’ll redirect the user to another location if the response from the server is OK (200).
If not, we also use Sweet Alert to display error notifications.
The form can be used to send our AJAX request now.
First, we use Validator to validate the form. If our validation rules pass, we use Auth::attempt to
authenticate the user.
If the login credentials of the user are correct, we return a successful response with a URL. If not,
we simply return an error message.
You may notice that we also use the $remember variable to store the value of the Remember Me
select box.
Chapter 2: Front End Recipes 147
We use the variable for the remember me functionality in our application. When we pass the
$remember variable (which is a boolean) as the second argument to the attempt method, if the
value of the variable is 1 (yes), our app keeps the user authenticated indefinitely.
For more information about using the Auth facade, check this out:
https://laravel.com/docs/master/authentication#authenticating-users⁸⁵
Let’s give our new login form a try.
If we enter wrong information, an error notification should appear:
Error
By applying the techniques above, we can easily build an AJAX login page!
Although we have only dealt with users, these concepts can be applied to create many types of
forms.
• DropzoneJS⁸⁸
• Plupload⁸⁹
• Uploadify⁹⁰
• jQuery DROPAREA⁹¹
• jqUploader⁹²
In this recipe, we’ll learn about jQuery File Upload Plugin, which is the most popular jQuery file
upload library.
Here are some of the most prominent jQuery File Upload’s features:
You may view all jQuery File Upload’s features and its documentation at:
https://github.com/blueimp/jQuery-File-Upload⁹³
Be sure to check out the demo⁹⁴ to see how it works.
⁸⁸http://www.dropzonejs.com
⁸⁹http://www.plupload.com/example_queuewidget.php
⁹⁰http://www.uploadify.com
⁹¹http://gokercebeci.com/dev/droparea
⁹²http://pixeline.be/experiments/jqUploader
⁹³https://github.com/blueimp/jQuery-File-Upload
⁹⁴https://blueimp.github.io/jQuery-File-Upload
Chapter 2: Front End Recipes 150
• jquery.fileupload-image.js
• jquery.fileupload-process.js
• jquery.fileupload-ui.js
• jquery.fileupload.js
• jquery.iframe-transport.js
• vendor/jquery.ui.widget.js
1 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
Add below:
⁹⁵https://github.com/blueimp/jQuery-File-Upload/archive/master.zip
Chapter 2: Front End Recipes 151
1 <!-- The Load Image plugin is included for the preview images and image resizing\
2 functionality -->
3 <script src="//blueimp.github.io/JavaScript-Load-Image/js/load-image.all.min.js"\
4 ></script>
5 <!-- The Canvas to Blob plugin is included for image resizing functionality -->
6 <script src="//blueimp.github.io/JavaScript-Canvas-to-Blob/js/canvas-to-blob.min\
7 .js"></script>
8 <!-- jQuery File Upload Plugin -->
9 <script src="/js/jquery.ui.widget.js"></script>
10 <script src="/js/jquery.iframe-transport.js"></script>
11 <script src="/js/jquery.fileupload.js"></script>
12 <script src="/js/jquery.fileupload-process.js"></script>
13 <script src="/js/jquery.fileupload-image.js"></script>
1 <script src="//blueimp.github.io/JavaScript-Load-Image/js/load-image.all.min.js"\
2 ></script>
3 <script src="//blueimp.github.io/JavaScript-Canvas-to-Blob/js/canvas-to-blob.min\
4 .js"></script>
doesn’t mean you should just use them in a production environment. We should download those
files, and put them in the public/js directory.
Our master layout should look like this:
1 <script src="/js/load-image.all.min.js"></script>
2 <script src="/js/canvas-to-blob.min.js"></script>
3 <!-- jQuery File Upload Plugin -->
4 <script src="/js/jquery.ui.widget.js"></script>
5 <script src="/js/jquery.iframe-transport.js"></script>
6 <script src="/js/jquery.fileupload.js"></script>
7 <script src="/js/jquery.fileupload-process.js"></script>
8 <script src="/js/jquery.fileupload-image.js"></script>
1 @extends('layouts.app')
2 @section('title', 'About')
3
4 @section('content')
5
6 <div class="container">
7 <div class="content">
8 <div class="title">About Page</div>
9 <div>
10 <div id="files" class="files">
11 <div id="testimage"><img src="/images/testimage.png" alt\
12 ="test image"></div>
13 </div>
14 <span class="btn btn-info btn-file">
15 Upload an image
16 <input id="fileupload" class="upload" type="file" na\
17 me="files[]">
18 </span>
19 <div id="progress" class="progress" style="display:none;">
20 <div class="progress-bar progress-bar-success"></div>
21 </div>
22 </div>
23 </div>
24 </div>
25
26 @endsection
Currently, we don’t have the testimage.png yet, so the image won’t display.
(Optional) You may download the image below (or use any image that you like) and save it at
public/images/testimage.png.
Learning Laravel 5 cover image⁹⁶
Here is our upload button:
⁹⁶http://learninglaravel.net/img/LearningLaravel5_cover.png
Chapter 2: Front End Recipes 153
As you see, we don’t have to create a form here. A simple button is more than enough.
Lastly, we put the progress bar at the bottom:
Upload form
You can also customize the style of the upload button and everything else to your liking. For instance,
we may style the button in pure css⁹⁷. Open our public/css/app.css file, add the following:
⁹⁷http://geniuscarrier.com/how-to-style-a-html-file-upload-button-in-pure-css
Chapter 2: Front End Recipes 155
1 .btn-file {
2 position: relative;
3 overflow: hidden;
4 margin: 10px;
5 }
6 .btn-file input.upload {
7 position: absolute;
8 top: 0;
9 right: 0;
10 margin: 0;
11 padding: 0;
12 font-size: 20px;
13 cursor: pointer;
14 opacity: 0;
15 filter: alpha(opacity=0);
16 }
1 Route::post('imageupload', 'ImagesController@storeImage');
1 <script src="/js/jquery.fileupload-image.js"></script>
Add below:
Chapter 2: Front End Recipes 157
Because the web middleware group has the CSRF middleware, we have to generate a CSRF token
and send it with our form. If we don’t have a CSRF token, we will get a 500 internal server error.
Note: if you don’t want to use the CSRF feature and generate the token, you may put
the imageupload route outside of the web middleware group.
1 $.ajaxSetup({
2 headers: {'X-CSRF-Token': $('meta[name=_token]').attr('content')}
3 });
4
5 $(function () {
6 'use strict';
7
8 var url = '/imageupload';
9
10 $('#fileupload').fileupload({
11 url: url,
12 dataType: 'json',
13 autoUpload: true,
14 acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
15 singleFileUploads: true,
16 maxFileSize: 999000,
17 previewMaxWidth: 300,
18 previewMaxHeight: 300,
19 previewCrop: false
20 }).on('fileuploadadd', function (e, data) {
21
22 $('#progress').fadeIn();
23 data.context = $('<div class="fileinfo"><div/>').appendTo('#files');
24 $.each(data.files, function (index, file) {
25 var node = $('<p/>')
26 .append($('<span/>').text(file.name));
27 node.appendTo(data.context);
28 });
29 }).on('fileuploadprocessalways', function (e, data) {
30
Chapter 2: Front End Recipes 158
73 $(data.context.children()[index])
74 .append('<br>')
75 .append(error);
76 });
77 });
78 });
This may seem overwhelming at first, but the code is easy. Let’s take a look deeper!
First, we use $.ajaxSetop() to add a default header to every request:
1 $.ajaxSetup({
2 headers: {'X-CSRF-Token': $('meta[name=_token]').attr('content')}
3 });
There are many options⁹⁸ that we can use to configure the plugin. For instance, the url option can
be used to specify where the request is sent (/imageupload).
You may remove some options or add more option if you want.
jQuery File Upload also provides us some callbacks⁹⁹ that we can use to execute code during some
events.
⁹⁸https://github.com/blueimp/jQuery-File-Upload/wiki/Options
⁹⁹https://github.com/blueimp/jquery-file-upload/wiki/options#callback-options
Chapter 2: Front End Recipes 160
As you see, we use the fileuploadadd callback here. This callback is invoked as soon as files are
added to the fileupload widget.
When files are added, we will display a progress bar, and add the files’ name to the #files section.
fileuploadprocessalways is the callback for the end of an individual file processing queue.
When the file is processed, we will display preview images on the page.
When the file is being processed, we calculate the progress bar percentage here and change the width
of the bar using CSS.
Chapter 2: Front End Recipes 161
When the file is uploaded successfully, we remove all the images in the #files section using:
1 $('#files').empty();
After that, we will insert a new image into the #files section.
Actually, we can just simply use the following code to display the image:
However, some browsers have browser cache. When the new image has the same name with the
old one, the image is not updated. We could not see the new image.
This is how we fix the issue:
Chapter 2: Front End Recipes 162
As you see, we may add a current timestamp at the end of the image URL. For example, the URL
of the image is changed to images/testimage.png?1458728374846.
1 } else if (file.error) {
2 var error = $('<span class="text-danger"/>').text(file.error\
3 );
4 $(data.context.children()[index])
5 .append('<br>')
6 .append(error);
7 }
8 });
If the file has some errors, we insert the error messages to the page.
Finally, if the request fails, all that left to do is to display an error message.
Here’s what our form should now look like when we try to upload a new image:
Chapter 2: Front End Recipes 163
If you still feel confused, try to remove some callbacks and modify some options to get a better
understanding of what’s really going on behind the scenes.
Note: Please note that we may also use Sweet Alert to display the messages as well.
Some callbacks (such as fileuploadprocessalways) can be removed.
Note: We will use Intervention Image in this section. If you don’t have the package
installed, please read the Recipe 6.
If you don’t have the ImagesController yet, let’s generate a new one:
Once we have the ImagesController file, open it and add this storeImage action:
Chapter 2: Front End Recipes 164
As you notice, we set the image’s name as testimage.png. After that, We also use Intervention
Image to convert the image to PNG and move it to our public/images directory:
Chapter 2: Front End Recipes 165
1 $destination = 'images';
2 $size = $file->getSize();
3 $filename = 'testimage';
4 $extension = 'png';
5 $fullName = $filename . '.' . $extension;
6 $pathToFile = $destination . '/' . $fullName;
7 $upload_success = Image::make($file)->encode('png')->save($destination . '/' . $\
8 fullName);
If the image is uploaded successfully, we return a JSON object containing a files array:
1 if ($upload_success) {
2 $json['files'][] = array(
3 'name' => $filename,
4 'size' => $size,
5 'url' => $pathToFile,
6 'message' => 'Uploaded successfully'
7 );
8 return Response::json($json);
9 }
Note: even if only one file is uploaded, the response should always be a JSON object
containing a files array.
1 else {
2 $json['files'][] = array(
3 'message' => 'error uploading images',
4 );
5 return Response::json($json, 202);
6 }
Upload an image
We should see a progress bar and a preview image while uploading. Our new image appears
without reloading the page.
If you’ve done it right, your image should now be uploaded successfully.
Here is a little tip. If you don’t want to set the image’s name or its extension, you may use the
following:
Chapter 2: Front End Recipes 167
1 $destination = 'images';
2 $time = time();
3 $formatTime = date("Y-m-d_h-m", $time);
4 $filename = $formatTime . '_' . str_random(8);
5 $extension = $file->getClientOriginalExtension();
6 $size = $file->getSize();
7 $fullName = $filename . '.' . $extension;
8 $pathToFile = $destination . '/' . $fullName;
9 $upload_success = Image::make($file)->save($destination . '/' . $fullName);
This time we generate the image’s name automatically and preserve the original image’s extension.
You may also insert the image link into your database to keep track of it. When you have the links,
you can display images anywhere on your site.
So we’ve seen how to go from integrating jQuery File Upload to uploading images asynchronously.
As you see, the plugin is is very customizable. Using the techniques, you can build some useful
features such as: uploading the site’s cover, changing user’s profile picture, etc.
This just covers the basics of what you can do with jQuery File Upload, be sure to explore its features
more!
• Cropper¹⁰¹
• Croppic¹⁰²
¹⁰⁰https://github.com/LearningLaravel/cookbook/releases/tag/v0.13
¹⁰¹https://github.com/fengyuanchen/cropper
¹⁰²http://www.croppic.net
Chapter 2: Front End Recipes 168
• Cropit¹⁰³
• JCrop¹⁰⁴
• Cropimg¹⁰⁵
• jQuery Guillotine Plugin¹⁰⁶
Cropper demo
Installing Cropper
Let’s get started by downloading the latest release of Cropper¹⁰⁹.
Unzip (decompress) the downloaded file, and go to the cropper-master/dist directory.
Copy the cropper.min.css file to our public/css directory.
Copy the cropper.min.js file to our public/js directory.
Open our master layout (app.blade.css), find:
Add above:
¹⁰⁷http://fengyuanchen.github.io/cropper
¹⁰⁸https://github.com/fengyuanchen/cropper
¹⁰⁹https://github.com/fengyuanchen/cropper/archive/master.zip
Chapter 2: Front End Recipes 170
Find:
1 <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
Add below:
1 <script src="/js/cropper.min.js"></script>
2 <script src="/js/crop.js"></script>
Create a new file called crop.js and place it inside our public/js directory. This is our custom
Javascript file.
Cropper is now ready to use!
1 @extends('layouts.app')
2 @section('title', 'Contact')
3
4 @section('content')
5 <div class="container">
6 <div class="content">
7 <div class="title">Contact Page</div>
8 <div class="img-container">
9 <img id="image" src="/images/testimage.png">
10 </div>
11 </div>
12 </div>
13 @endsection
As you see, we just create a normal image and put it inside a wrapper (img-container). The size
of the cropper inherits from the size of the wrapper, so be sure to always wrap the image with a
visible block element.
We can display a new cropper by adding the following to our crop.js file:
Chapter 2: Front End Recipes 171
1 $(function () {
2
3 'use strict';
4
5 var $image = $('#image');
6 $image.cropper();
7
8 });
We use jQuery to find the image. After that, we use $image.cropper() to initialize the cropper.
Now we can see the cropper in our browser!
New cropper
As mentioned earlier, the size of the cropper is the wrapper’s size. Let’s change the cropper’s size by
adding these CSS rules to our app.css file:
Chapter 2: Front End Recipes 172
1 img {
2 max-width: 100%;
3 }
4
5 .img-container {
6 margin-bottom: 20px;
7 max-width: 516px;
8 }
1 <div class="img-container">
Add above:
1 $image.cropper();
Add below:
Cropper has a method called getCroppedCanvas that we can use to get a canvas drawn the
cropped image when clicking the Crop Image button:
1 $('#crop-btn').click(function() {
2 croppingData = $image.cropper("getCroppedCanvas");
3 });
Once having the cropping data, we can display the cropped image in the image-data section:
1 $('.image-data').html(croppingData);
Great! Every time we click the Crop Image button, we should see a new cropped image immediately!
Add above:
Form buttons
Of course, we don’t want users to click the Crop Image button if they don’t choose any image yet.
Let’s temporarily hide the button!
Open our app.css file (public/css/app.css), and add:
1 #crop-btn {
2 display:none;
3 }
Chapter 2: Front End Recipes 177
1 function PreviewImage() {
2 var oFReader = new FileReader();
3 oFReader.readAsDataURL(document.getElementById("uploaded-image").files[0]);
4 oFReader.onload = function(oFREvent) {
5 $('#crop-btn').show();
6 $("#image").cropper('destroy');
7 document.getElementById("image").src = oFREvent.target.result;
8 $("#image").cropper();
9 };
10 }
1 $('#crop-btn').show();
1 $("#image").cropper('destroy');
Be sure that there is no cropper on the page by destroying it. If we don’t do this step, when users
select an image again, the new image will not be displayed.
1 document.getElementById("image").src = oFREvent.target.result;
2 $("#image").cropper();
Finally, we replace the old image with the chosen one and display a new cropper.
The thing to notice here is that we must use the onchange event to trigger the PreviewImage
function when a user selects an image:
Chapter 2: Front End Recipes 178
Choose an image
Chapter 2: Front End Recipes 179
If we click the Choose an image button and pick an image, then a cropper with our image will be
displayed on the page!
The last thing we need to do is create a hidden input to send our image data to our server (backend)
and another action to handle that request.
As you may notice, our form has the hidden input already, so just open the crop.js and update as
follows:
1 $('#crop-btn').click(function() {
2 croppingData = $image.cropper("getCroppedCanvas");
3 $('.image-data').html(croppingData);
4 $('#cropped-image').val(croppingData.toDataURL();
5 });
When we use croppingData.toDataURL(), the image will be converted to base64¹¹⁰. Simply put,
base64 encoded data is a string of characters that contains our image data. We can decode that
base64 data later to create a new image.
Next, add this route to our web middleware group:
1 Route::post('cropimage', 'ImagesController@storeCroppedImage');
¹¹⁰https://en.wikipedia.org/wiki/Base64
Chapter 2: Front End Recipes 180
15 ullName);
16
17 Alert::success('Image has been cropped successfully!', 'Success!')->auto\
18 close(2000);
19
20 return redirect('/contact');
21
22 } else if(isset($files['uploaded-image']) ) {
23
24 $file = $files['uploaded-image'];
25 $destination = 'images';
26 $filename = 'testimage';
27 $extension = 'png';
28 $fullName = $filename . '.' . $extension;
29 $image = Image::make($file)->encode('png')->save($destination . '/' . $f\
30 ullName);
31
32 Alert::success('Image has been uploaded successfully!', 'Success!')->aut\
33 oclose(2000);
34
35 return redirect('/contact');
36
37 } else {
38
39 Alert::error('There is an error', 'Error')->autoclose(2000);
40
41 return redirect('/contact');
42 }
43 }
Let’s take a look deeper at this action so that we can see how it works.
1 if ($files['cropped-image'] != "") {
2
3 $file = $files['cropped-image'];
4
5 $destination = 'images';
6 $filename = 'testimage';
7 $extension = 'png';
8 $fullName = $filename . '.' . $extension;
9
10 $image = Image::make($file)->encode('png')->save($destination . '/' . $f\
Chapter 2: Front End Recipes 181
11 ullName);
12
13 Alert::success('Image has been cropped successfully!', 'Success!')->auto\
14 close(2000);
15
16 return redirect('/contact');
17
18 }
We’ll start by checking if our cropped-image field is empty. If it’s not empty, we use Intervention
Image to create a new image. We also use Sweet Alert to display a successful message. After that,
the user will be redirected to the contact page.
1 } else if(isset($files['uploaded-image']) ) {
2
3 $file = $files['uploaded-image'];
4 $destination = 'images';
5 $filename = 'testimage';
6 $extension = 'png';
7 $fullName = $filename . '.' . $extension;
8 $image = Image::make($file)->encode('png')->save($destination . '/' . $f\
9 ullName);
10
11 Alert::success('Image has been uploaded successfully!', 'Success!')->aut\
12 oclose(2000);
13
14 return redirect('/contact');
15
16 }
If we don’t get the cropped-image but we still get the uploaded-image, that means our users have
uploaded an image but they haven’t cropped it. We still save the image, display a successful message
and redirect them back to the contact page.
1 } else {
2
3 Alert::error('There is an error', 'Error')->autoclose(2000);
4
5 return redirect('/contact');
6 }
Lastly, if there is no image or there is an error, we just simply display an error message and redirect
users back to our contact page.
Be sure that our ImagesController has all the required classes:
Chapter 2: Front End Recipes 182
1 <?php
2
3 namespace App\Http\Controllers;
4
5 use App\Http\Requests;
6 use App\Http\Requests\ImageFormRequest;
7
8 use Image;
9 use Illuminate\Support\Facades\Input;
10 use Response;
11 use Alert;
12
13 class ImagesController extends Controller
Well done! Go ahead and test your application! Let’s make sure that everything is working properly.
Chapter 2: Front End Recipes 183
Note: Sometimes, you may not see any changes. This is the browser cache issue. You
have to manually refresh the page to clear your browser cache and see the new image.
There are many ways to solve this issue: using a different image name, using Javascript
to reload the page automatically, adding a timestamp to the image’s name, redirecting
users to a different page, etc.
Additionally, you may also set cropper options using $().cropper(options). For example, you may
Chapter 2: Front End Recipes 184
1 $("#image").cropper({
2 aspectRatio: 200/200,
3 resizable: true,
4 zoomable: false,
5 rotatable: false,
6 });
Be sure to check out the documentation¹¹¹ to know more about other options.
Congratulations! Now that you know the theory behind the cropping image functionality.
Don’t forget to take advantage of all features of Cropper plugin to enhance your application. There
is much more that you can now build using this technique. For example, you can use Cropper to get
the height, width and x/y coordinates of the crop box, then crop the image at the backend.
Remember, this is just a beginning.
Have fun coding!
The chapter is now complete. However, more recipes will be added later. Feedback from
our readers is always welcome. Please leave your testimonials at http://learninglaravel.net/laravel¹¹³
¹¹¹https://github.com/fengyuanchen/cropper
¹¹²https://github.com/LearningLaravel/cookbook/releases/tag/v0.14
¹¹³http://learninglaravel.net/laravel
Chapter 3: Deployment Recipes
Introduction
After learning some tricky topics to successfully build a full stack application, it’s time to deploy
your app. This chapter contains some helpful recipes about working with Heroku, Digital Ocean,
etc.
Deploy your applications blazingly fast using GIT and secret techniques are also discussed in the
book!
List Of Recipes
Deployment recipes
• Recipe 301 - Deploying your applications using DigitalOcean (PHP 7 and Nginx)
• Recipe 302 - Deploying your applications using Heroku
• Recipe 303 - Deploying your applications blazingly fast using GIT.
185
Chapter 3: Deployment Recipes 186
DigitalOcean
If you’re not a DigitalOcean member yet, you’ll need to register a new account at DigitalOcean.
You can use the link below to get $10 for free, that means you can use their $5 cloud server for
two months.
Register a new DigitalOcean account and get $10 for free!¹¹⁴
¹¹⁴https://www.digitalocean.com/?refcode=5f7e95cb014e
Chapter 3: Deployment Recipes 187
Note: You will need to provide your credit card information or Paypal to activate your
account.
Little tip: Learning Laravel also has a freebies section¹¹⁵, you may find some useful coupons there.
Note: There are some newer versions of Ubuntu (14.10, 15.04, etc.), but the 14.04 is an
LTS (Long Term Support) version, that means we will receive updates and support for
at least five years. Ubuntu 14.04 also has more compatible plugins. By the way, you may
try to use a newer version if you want.
¹¹⁵http://learninglaravel.net/topics/freebies
¹¹⁶https://cloud.digitalocean.com/droplets/new
Chapter 3: Deployment Recipes 188
1 ssh root@yourIPAddress
The first time you login, it will ask you to change the password. Enter the current Unix password
again, and then enter your new password to change it.
Finally, run this command to check and update all current packages to the latest version:
Next, run this command to install Nginx, PHP 7, PHP7.0-FPM, PHP-MySQL, PHP7.0-Zip, Curl,
phpredis, xdebug and other useful packages.
You may use this command to see all the PHP 7 packages:
Available packages:
Chapter 3: Deployment Recipes 190
Note: You may remove some packages that you don’t use and install them later when
you need.
Once installed, you can now visit your Nginx server via the IP address (Be sure to use your droplet’s
IP address):
http://128.199.206.121¹¹⁷
¹¹⁷http://128.199.206.121
Chapter 3: Deployment Recipes 192
1 php -v
After that, we need to edit the server block (aka virtual hosts) file. Open it:
Find:
1 root /usr/share/nginx/html;
This is the path to your Laravel application, we don’t have a Laravel application yet, but let’s change
it to:
1 root /var/www/learninglaravel.net/html;
Note: You may use a different address (change learninglaravel.net to your website’s
address) if you want. Be sure to replace all the addresses.
Find:
Chapter 3: Deployment Recipes 193
Change to:
Find:
1 location / {
2 # First attempt to serve request as file, then
3 # as directory, then fall back to displaying a 404.
4 try_files $uri $uri/ =404;
5 # Uncomment to enable naxsi on this location
6 # include /etc/nginx/naxsi.rules
7 }
Change to:
1 location / {
2 # First attempt to serve request as file, then
3 # as directory, then fall back to displaying a 404.
4 # try_files $uri $uri/ =404;
5 try_files $uri/ $uri /index.php?$query_string;
6 # Uncomment to enable naxsi on this location
7 # include /etc/nginx/naxsi.rules
8 }
Add below:
1 location ~ \.php$ {
2 try_files $uri /index.php =404;
3 fastcgi_split_path_info ^(.+\.php)(/.+)$;
4 fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
5 fastcgi_index index.php;
6 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
7 include fastcgi_params;
8 }
Next, let’s make a test file called index.html to test our configurations:
1 <html>
2 <head>
3 <title>Learning Laravel</title>
4 </head>
5 <body>
6 <h1>Learning Laravel test page. PHP 7 and Nginx</h1>
7 </body>
8 </html>
Now when you visit your website via its IP address, you should see:
Note: If your server is restarted, you have to add the swapfile again.
Once installed, run this command to move composer.phar to a directory that is in your path, so
that you can access it globally:
1 mv composer.phar /usr/local/bin/composer
1 export PATH="$PATH:~/.composer/vendor/bin"
2 source ~/.bashrc
Once finished, we’re finally at the part that we’ve been waiting for: Installing Laravel!
We will put our Laravel application at /var/www/learninglaravel.net/. Type the following to get
there:
1 cd /var/www/learninglaravel.net/
Installing Laravel
This is a pretty standard process. I hope you understand what we’ve done. If you don’t, please read
Learning Laravel 5 book’s Chapter 1¹¹⁸.
By now, we should have our Laravel app installed at /var/www/learninglaravel.net/laravel.
Once that step is done, we must give the directories proper permissions:
Find:
1 root /var/www/learninglaravel.net/html;
Change to:
1 root /var/www/learninglaravel.net/laravel/public;
Laravel is running
Possible Errors
If you see this error:
Chapter 3: Deployment Recipes 198
This is a Laravel 5 bug. Sometimes, your app doesn’t have a correct application key (this key is
generated automatically when installing Laravel)
You need to run these commands to fix this bug:
1 shutdown -h now
Now, go to DigitalOcean Control Panel. Go to your droplet. Click on the Snapshots button to view
the Snapshots section.
Chapter 3: Deployment Recipes 199
Take a snapshot
Tips
Here are some little tips when using a droplet:
Tip 1:
If you have a domain and you want to connect it to your site, open the server block file, and edit
this line:
1 server_name localhost;
Modify to:
1 server_name yourDomain.com;
Now you can be able to access your site via your domain.
Tip 2:
You can access your server using FTP as well (to upload, download files, etc.), use this information:
Chapter 3: Deployment Recipes 200
Note: Keep in mind that you can choose other languages later.
Chapter 3: Deployment Recipes 202
Next, we must download and install Heroku Toolbelt¹²⁰, which is a terminal utility that provides
you access to the Heroku Command Line Interface (Heroku CLI).
You may choose and download Heroku Toolbelt for your system at the devcenter¹²¹ or at the Heroku
Toolbelt page¹²².
Once installed, we can use the heroku command from our command shell:
1 heroku
¹²⁰https://toolbelt.heroku.com
¹²¹https://devcenter.heroku.com/articles/getting-started-with-php#set-up
¹²²https://toolbelt.heroku.com
Chapter 3: Deployment Recipes 203
Chapter 3: Deployment Recipes 204
The first time we use the heroku command, Heroku installs some dependencies and plugins for
us, and then we’ll see a list of commands.
Done! We can now use Heroku to deploy our applications.
1 vagrant ssh
1 cd Code
Note: Feel free to change the name of the app to your liking.
Heroku command
Now we need to write down or just remember the application key. We’ll need this key later.
1 base64:trchbNOb9jbqH8rz03/kLhMIybDIIcxHZi4zKMPx5tc=
1 cd laraheroku
1 git init
2 git add .
3 git commit -m "My new laraheroku app"
Delploying to Heroku
We’ll need to create a Procfile, which is a configuration file that tells Heroku about our applications’
settings. Our Laravel application’s root is the public/ subdirectory, so we have to create a new
Procfile to serve the application from /public.
To begin, be sure that we’re at the laraheroku’s root.
Creating a new Procfile and add “web: vendor/bin/heroku-php-apache2 public” to the file by
using the following:
1 git add .
2 git commit -m "Procfile for Heroku"
Now we create a new Heroku application that we can push to, using this command:
1 heroku create
Heroku command
As you see, a random name was automatically chosen for our application. https://intense-oasis-
43391.herokuapp.com¹²³ is my application URL.
Heroku automatically detects our application is written in PHP. However, we should tell Heroku
about that again, because sometimes it may not work as expected:
¹²³https://intense-oasis-43391.herokuapp.com
Chapter 3: Deployment Recipes 206
Before deploying our app for the first time, we must set a Laravel encryption key, which is the
application key used by Laravel to encrypt user sessions and other information.
We may use heroku config:set APP_KEY= command to do this:
We should have the key already when creating our new Laravel application. If you don’t have the
key, you can generate a new one by running this Artisan command (on Homestead):
Finally, we can deploy our application to Heroku by pushing our files to the Heroku Git remote
(https://git.heroku.com/intense-oasis-43391.git):
Deploy to Heroku
You may also use this Heroku command to open your application in a new window:
¹²⁴https://intense-oasis-43391.herokuapp.com
Chapter 3: Deployment Recipes 207
1 heroku open
1 ssh root@yourIPAddress
1 cd /var/www/learninglaravel.net
I will create a new directory called repos and put my Git repository there:
1 mkdir repos
2 cd repos
1 cd /var/www/learninglaravel.net
If you already have the laravel directory, you should remove it by running this command:
1 rm -r laravel
1 cd Code/laravel
Note: If you don’t have the laravel directory yet, be sure to create a new Laravel
application and name it laravel.
Note: if you’re using a different site name, be sure to replace learninglaravel with
your site name. Replace yourIPAddress with your real server IP address as well.
1 git add .
2 git commit -a -m "Push files to the server"
3 git push learninglaravel master
1 cd /var/www/learninglaravel.net/laravel
Next, we can use git pull to fetch learninglaravel.git repository and merge the changes into the
laravel repository:
Once that step is done, we must give the directories proper permissions:
1 git add .
2 git commit -a -m "Update files"
3 git push learninglaravel master
And then use git pull to merge the changes (on our server):
¹²⁷http://learninglaravel.net/laravel