IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Introduction

This article explains step by step how to create a Laravel 5 web app from scratch. Read goals section for more details about the app.

Goals

  • Create a PHP web app from scratch using Laravel
  • Application will send notifications to email users (using Mandrill) and to mobile app using Pushbots
  • Only authorized users could access to web app
  • Application will use:
  • Views implemented using blade templates
  • Controllers using routes.php file from Laravel
  • Model: Specific business logic for action app will execute
  • Use of php artisan Laravel command for creating controllers, migrations, models, etc.
  • Development environment using Homestead
  • Custom/Server deployment on server using Apache and LAMP
  • Deployment using Forge?

Previous steps. Environment configuration

Github repository

First you will need a github account. Fill signup form at:

https://github.com/

Then follow the steps at:

https://help.github.com/articles/create-a-repo/

and create a new github repo. Check "intialitze repo with a README". In this tutorial we will use:

laravelnotificationsapp

as repo name.

Resources

Laravel app installation

Follow this steps:

$ cd && mkdir github && cd github

Now we are going to install Laravel ([1]) on this folder.

First step install is installing Composer as a global command in your Ubuntu system ([2]):

$ sudo apt-get install curl php5-cli
$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/bin/composer

Now you can install laravel installer:

$ composer global require "laravel/installer=~1.1"

Previous step will install laravel command at:

~/.composer/vendor/bin/laravel

Configure your bash to include ~/.composer/vendor/bin/ into PATH environment variable. Edit:

$ editor ~/.bashrc

And add this line:

export PATH=${PATH}:~/.composer/vendor/bin

Install a new Laravel app with (execute command inside ~/github folder):

$ laravel new laravelnotificationsapp

NOTA: For installing last development version of Laravel you can try with my tool bootstrap-app

Go inside laravelnotificationsapp folder:

$ cd laravelnotificationsapp

If you are not used to laravel file structure read Estructura de fitxers Laravel.

Now configure enviroment variables file for Laravel

$ mv .env.example .env

Now execute common first laravel config steps:

$ php artisan key:generate
Application key [p90yWNaIARDNA97fMuCSDjazrYQaPOFE] set successfully.

Check enviroment file:

$ cat .env 
APP_ENV=local
APP_DEBUG=true
APP_KEY=p90yWNaIARDNA97fMuCSDjazrYQaPOFE
 
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null

watch if APP_KEY is setted correctly. This environment variable is used at:

$ editor config/app.php

At line:

'key' => env('APP_KEY', 'SomeRandomString'),

env function in laravel read environment variables form .env file. This line use APP_KEY environment variable if exists or SomeRandomString if not exist (default value not recommended for security reasons).

NOTA: Why we need environment variables and an .env file in a PHP app? This file will be configured in different ways in different environments (local, server, etc). Environment is always local and is not never commited to git (see default Laravel's .gitignore file)

Install and configure phpstorm

I recommend this IDE for PHP Development but you can user another IDE if you wish.

Once PHPStorm installed got to File > open an select folder:

~/github/laravelnotificationsapp

Execute:

Ctrl+Alt+s

to access settings. Search for Plugins, click on "Browse repositories" search for Laravel and install

Laravel plugin

Resources:

Configure Github repo and first commit/push

Open a terminal (you can use if you wish PHPStorm terminal with Alt+F12) and init git:

$ cd  ~/github/laravelnotificationsapp
$ git init

Now you have a .git folder in your project. Check git status:

$ git status

As you can see all files are untracked. Observe that some folders are ignored by default by Laravel

$ cat .gitignore
/vendor
/node_modules
.env

Perfect!. The file .env has confidential data and /vendor folder contains third party composer dependencies like /node_modules contains third party node.js dependencies.

Add untracked files to local git repo and execute first commit:

$ git add .
$ git commit -a -m "First version"

Check all is ok:

$ git status

You repo would be in clean state.

Now add github remote repo created at step 1 of this tutorial with:

$ git remote add origin REPO_SSH_URL

Where REPO_SSH_URL would be copied from GITHUB URL repo. In my case:

https://github.com/acacha/laravelnotificationsapp

Copy from right sidebar the section ssh clone URL

[email protected]:acacha/laravelnotificationsapp.git

So the command in my case is:

$ git remote add origin  [email protected]:acacha/laravelnotificationsapp.git

Now configure SSH keys as described at:

https://help.github.com/articles/generating-ssh-keys/

In Ubuntu we can create the keys with:

$ ssh-keygen -t dsa

Check if remote repository is configured ok:

$ git remote -v
origin  [email protected]:acacha/laravelnotificationsapp.git (fetch)
origin  [email protected]:acacha/laravelnotificationsapp.git (push)

Now you are ready for your first pull/push to remote repo:

$ git pull origin master
$ git push origin master

Naming Your Laravel Application

See default Laravel composer configuration at file composer.json:

$ cat composer.json
{
	 ...
	"autoload": {
		"classmap": [
			"database"
		],
		"psr-4": {
			"App\\": "app/"
		}
	},
	...

Laravel use "PSR-4" for autoload and the default namespace is App

"psr-4": {
       "App\\": "app/"
 }

We can rename the app with:

$ php artisan app:name LaravelNotificationApp

Check with git how many files have been changed automatically by this command:

$ git status

Commit changes with:

$ git commit -a -m "Renamed Laravel app to LaravelNotificationApp"

Debug utilities

TODO: Step by step installation of:

Configuring development environment with homestead, vagrant and VirtualBox

First of all install (or reinstall to latest version if you already have installed) Virtual Box i Vagrant

Go to webpages:

https://www.virtualbox.org/wiki/Linux_Downloads
https://www.vagrantup.com/downloads.html

And download Debian packages (if you have a modern PC it will be 64bits). Install with:

$ cd ~/Downloads
$ sudo dpkg -i vagrant_1.7.2_x86_64.deb
$ sudo dpkg -i virtualbox-4.3_4.3.24-98716~Ubuntu~raring_amd64.deb

NOTA: Change commands to reflect you own versions! use tab to write file name

.

Now use vagrant to download laravel homestead virtual machine

$ vagrant box add laravel/homestead

Choose

1) VirtualBox

now install homestead using Composer

$ composer global require "laravel/homestead=~2.0"

Check you already have composer bin folder at you path:

$ cat ~/.bashrc | grep composer/vendor
export PATH=${PATH}:~/.composer/vendor/bin

Initialize homestead with:

$ homestead init

This will create file:

~/.homestead/Homestead.yaml 

You can edit homestead configuration file with

$ homestead edit

An example of config file:

 ---
ip: "192.168.10.10"
memory: 2048
cpus: 1
provider: virtualbox

authorize: ~/.ssh/id_dsa.pub

keys:
    - ~/.ssh/id_dsa

folders:
    - map: ~/github
      to: /home/vagrant/github

sites:
    - map: laravelnotificationsapp.dev
      to: /home/vagrant/github/laravelnotificationsapp/public

databases:
    - homestead

variables:
    - key: APP_ENV
      value: local

# blackfire:
#     - id: foo
#       token: bar

Default IP for laravel homestead is:

192.168.10.10

This ip is used as default because VirtualBox will configure for you a virtual interface with IP 192.168.10.1

 vboxnet1  Link encap:Ethernet  direcciónHW 0a:00:27:00:00:01  
          Direc. inet:192.168.10.1  Difus.:192.168.10.255  Másc:255.255.255.0
          Dirección inet6: fe80::800:27ff:fe00:1/64 Alcance:Enlace
          ACTIVO DIFUSIÓN FUNCIONANDO MULTICAST  MTU:1500  Métrica:1
          Paquetes RX:0 errores:0 perdidos:0 overruns:0 frame:0
          Paquetes TX:113 errores:0 perdidos:0 overruns:0 carrier:0
          colisiones:0 long.colaTX:1000 
          Bytes RX:0 (0.0 B)  TX bytes:17351 (17.3 KB)

Access to Homestead VM would be done with

$ ssh-keygen -t dsa

NOTA: Change you homestead config file because by default use RSA instead of DSA

Now we config when our projecte files are stored. We use folder:

~/github

Then you have to change folders section to:

   - map: ~/github
     to: /home/vagrant/Code

This section maps ~/github folder at rou machine with /home/vagrant/Code at homestead virtual machine. Then all files at ~/github will be available at folder /home/vagrant/Code in your vagrant virtual machine.

Now we configure sites at section sites. Change:

sites:
   - map: laravelapp.dev
     to: /home/vagrant/github/laravelapp/public

or similar to:

sites:
   - map: laravelnotificationsapp.dev
     to: /home/vagrant/github/laravelnotificationsapp/public

NOTA: Tou can define multiple folder mappings ans sites mappings or datatabase. No problem

Now change DNS local resolution:

$ sudo joe /etc/hosts
# LARAVEL
192.168.10.10 laravelnotificationsapp.dev

Check with:

$ ping laravelnotificationsapp.dev
PING laravelnotificationsapp.dev (192.168.10.10) 56(84) bytes of data.

Pimng does not responds because machine is not started but check that name resolution is ok!

We are now ready:

$ homestead up

Check app at

http://laravelnotificationsapp.dev

You can connect to machine with

$ homestead ssh

Halt/suspend machine with:

$ homestead halt
$ homestead suspend  -> resume homestead virtual machine

Update virtual machine with:

$ homestead update

Resources:

Configuring base authorization system and database

Congratulations! Now we can start tinkering with code. Check default Laravel configuration pages going to:

http://laravelnotificationsapp.dev/home

You will be redirected to:

http://laravelnotificationsapp.dev/auth/login

At phpstorm:

Ctrl+shift+n

IMPORTANT: Ctrl+shift+n is really useful for navigation and opening files at PHPStorm

And search for file routes.php at folder app/Http:

<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/

Route::get('/', '[email protected]');

Route::get('home', '[email protected]');

Route::controllers([
	'auth' => 'Auth\AuthController',
	'password' => 'Auth\PasswordController',
]);

First route:

Route::get('/', '[email protected]');

Is welcome page at URL:

http://laravelnotificationsapp.dev/

That executes code at method index in controller WelcomeController at folder app/Http/Controllers:

<?php namespace LaravelNotificationApp\Http\Controllers;
class WelcomeController extends Controller {

	/*
	|--------------------------------------------------------------------------
	| Welcome Controller
	|--------------------------------------------------------------------------
	|
	| This controller renders the "marketing page" for the application and
	| is configured to only allow guests. Like most of the other sample
	| controllers, you are free to modify or remove it as you desire.
	|
	*/

	/**
	 * Create a new controller instance.
	 *
	 * @return void
	 */
	public function __construct()
	{
		$this->middleware('guest');
	}

	/**
	 * Show the application welcome screen to the user.
	 *
	 * @return Response
	 */
	public function index()
	{
		return view('welcome');
	}

}

That shows view welcome:

 return view('welcome');

Views are at folder /resources/views and use blade templating system (all files extension is .blade.php), then file is:

 welcome.blade.php

Showing a Laravel logo and cite. We don't need this welcome then wi will change it afterwards.

Other default routes are:

 Route::get('home', '[email protected]');

 
 Route::controllers([
 	'auth' => 'Auth\AuthController',
 	'password' => 'Auth\PasswordController',
 ]);

Web Page URL:

http://laravelnotificationsapp.dev/home

maps to index method at HomeController at folder Http/Controllers

<?php namespace LaravelNotificationApp\Http\Controllers;

class HomeController extends Controller {

	/*
	|--------------------------------------------------------------------------
	| Home Controller
	|--------------------------------------------------------------------------
	|
	| This controller renders your application's "dashboard" for users that
	| are authenticated. Of course, you are free to change or remove the
	| controller as you wish. It is just here to get your app started!
	|
	*/

	/**
	 * Create a new controller instance.
	 *
	 * @return void
	 */
	public function __construct()
	{
		$this->middleware('auth');
	}

	/**
	 * Show the application dashboard to the user.
	 *
	 * @return Response
	 */
	public function index()
	{
		return view('home');
	}

}

But if you see method index:

 public function index()
 	{
 		return view('home');
 	}

Why login form is triggered instead of home view when URL /home is called? The answer is at constructor:

 public function __construct()
	{
		$this->middleware('auth');
	}

And the use of Middleware. Laravel have a custom midleware named auth that is called before every method in the controller and checks for an authenticated user before executing the method. Very cool!

By default no users are created. If you go to:

http://laravelnotificationsapp.dev/auth/register

You can try to create/register a new user but the form will fail because we have not already created/configured our database. With homestead we a MySQL database is already created for our app:

$ homestead edit

See section

databases:

   - homestead

This section defines wich databases will be created at creation of database. If machine is already create we can reprovision it with:

Edit homestead configuration with (same as editing file ~/.homestead/Homestead.yaml):

$ homestead edit

Check for you virtual machine with vagrant:

$ vagrant global-status
...
b916503  default virtualbox running  /home/sergi/.composer/vendor/laravel/homestead 

Take not of id b916503 and

$ homestead up
$ vagrant provision b916503

The new configuration will web applied (database will be created). Log into machine:

$ homestead ssh

To access to database the default username and password are:

$ mysql -uhomestead -psecret

or using root:

$ sudo mysql -psecret
> SHOW DATABASES;
> exit

You can install you favourite MySQL managment system in virtual machine or use port reriection to connect MySQL apps to dabase. For example phpmyadmin

NOTA: Homestead use Nginx as web server instead of Apache!

$ homestead ssh
$ sudo apt-get install phpmyadmin
$ sudo ln -s /usr/share/phpmyadmin/ /home/vagrant/github/phpmyadmin
$ cd ~/github && serve phpmyadmin.app /home/vagrant/github/phpmyadmin

If gives you an error:

$ sudo tail -f /var/log/nginx/error.log
2015/03/18 11:54:16 [emerg] 3671#0: invalid number of arguments in "listen" directive in /etc/nginx/sites-enabled/phpmyadmin.app:2

Edit config file:

$ sudo editor /etc/nginx/sites-enabled/phpmyadmin.app

And add 80 to Listen directive:

$ sudo service nginx reload

Now edit file /etc/hosts at your main machine (not virtual machine):

192.168.10.10  phpmyadmin.app

and you could access phpmyadmin with URL

http://phpmyadmin.app


Now we can chamge app database on .env file:

$ editor .env

and change DB_DATABASE to:

DB_DATABASE=laravelnotificationsapp

It's time to populate our database using migrations. In homestead machine execute:

$ cd ~/github/laravelnotificationsapp
$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

Migrations are at folder /database/migrations and could be create with php artisan make:migration command . Now we only need default laravel migrations but I shall return to that theme later.

Now tables are created ok as you can see with PHPMyAdmin but i wil show you now a way to play with database using laravel tinkering. For example we cant add a user to database:


$ php artisan tinker
> $user = new LaravelNotificationApp\User();
> $user->name = "laravel";
> $user->password = "$2y$12$/.5NLCIIKjo2Z.Xs19Z1VOxs1qYf0.rKq96GvAlzq/Fud6f1YRGSS";
> $user->email = "[email protected]";
> $user->save();

Where

$2y$12$/.5NLCIIKjo2Z.Xs19Z1VOxs1qYf0.rKq96GvAlzq/Fud6f1YRGSS 

is the hash for password secret in Blowfish format the default one with laravel. Now you can go to:

http://laravelnotificationsapp.dev/auth/login

and use laravel username and password secret. If all is correcte then logout:

http://laravelnotificationsapp.dev/auth/logout

Check that register works correctly at:

http://laravelnotificationsapp.dev/auth/register

And we can see new user with tinker:

 $ php artisan tinker
  $user->find(2);
   => <LaravelNotificationApp\User #0000000066af9d660000000070e6220f> {
      id: 2,
      name: "laravel",
      email: "[email protected]",
      created_at: "2015-03-18 16:02:39",
      updated_at: "2015-03-18 16:04:22"
  }

Configuring email with mandrill

Mandrill account creation

First go to page

https://mandrill.com/signup

And follow steps to signup.

Create API Token

Go to page:

Settings > SMTP and API info

And click on button new API Key. Please create 3 keys one with checkbox Test key an another without.

Laravel configuration

Install Guzzle HTTP composer dependency:

$ composer require "guzzlehttp/guzzle:~4.0"

At file:

config/services.php

Change:

 	'mandrill' => [
		'secret' => ,
	],

with:

'mandrill' => [
		'secret' => $_ENV['MANDRILL_KEY'],
	],

And config Laravel email file (config/mail.php). Most changes have to be done at environmen (.env file) but check that

'from' => ['address' => env('MAIL_FROM_ADDRESS', null), 'name' => env('MAIL_FROM_NAME', null)],

check also:

'driver' => env('MAIL_DRIVER', 'smtp'),
...
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
...
'port' => env('MAIL_PORT', 587),

and:

 'pretend' => false,

No config environment .env:

MAIL_DRIVER=mandrill
MAIL_HOST=smtp.mandrillapp.com
MAIL_PORT=587 
MANDRILL_KEY=PUT_YOUR_TEST_MANDRILL_KEY_HERE
[email protected]
MAIL_FROM_NAME="MY NAME"

Please use Test API token on development environment and the other token in production. Test emails can be seen on:

Outbund

section in Mandrill. This emails are not really send so that's perfect for testing pourposes

I recommend you to create two environment files:

$ cp .env .env.production

In production environment (.env.production file) change config to

APP_ENV=production
APP_DEBUG=false
MANDRILL_KEY=PUT_YOUR_MANDRILL_PRODUCTION_KEY_HERE

VERY IMPORTANT! Add production file to .gitignore:

$ cat .gitignore
/vendor
/node_modules
.env
.env.production

Resources:

Check Authentication, password recovery, database and mail send

At this point you have a full functional "skeleton"/"bootstrap" web app with support for:

  • User Login and authentication:
  • Check that you can register users, Login and Logout new users
  • Test how all forms supports validations (try filling forms without all required fields, email fields without correct email format, etc)
  • Test password recovery with email and resetting password. Check Mandrill statistics
  • Check how your database is populated with user data
  • Follow the procees and changes in database when you reset user password, how tokens are used ,etc.

At this point we take a break to explain how some things works at Laravel.

How form validation works?

Form validation is a repetitive and a laborious task in every app development. Laravel helps us in this task using Validation class ([3]).

Validation takes place after submitting a Form (we can also validate input data from a different origin, not necessarily a form, for example an API request) and before any logic to process the form (for example database logic). So is a good practice to implement validation in controllers and for this reason default Laravel controller Class at app/Http/Controllers/Controller.php use PHP Trait ValidatesRequests :

 Validate Request Code at: https://github.com/laravel/framework/blob/5.0/src/Illuminate/Foundation/Validation/ValidatesRequests.php

So we can use method:

validate 

In any Laravel Controller. For example the default Laravel Auth Login implementation at App\Http\Controllers\Auth\AuthController:

...
class AuthController extends Controller {
        ...
	use AuthenticatesAndRegistersUsers;

Use php trait AuthenticatesAndRegistersUsers which at method postLogin use validate ing the following way:

public function postLogin(Request $request)
	{
		$this->validate($request, [
			'email' => 'required|email', 'password' => 'required',
		]);

		... other controller logic here
	}

Remember method header is:

 public function validate(Request $request, array $rules, array $messages = array())

So always we pass Request object (with all input/request data encapsuled there) and and array of rules. In login example with define email and password fields as required and also email field as a valid email. A list of availalble rules could be found at:

http://laravel.com/docs/5.0/validation#available-validation-rules

All rules are checked are if at least one rule is not correct an Illuminate\Contracts\Validation\ValidationException will be thrown. This exception is automatically caught and a redirect is generated to the user's previous location. The validation errors are even automatically flashed to the session!

For example at default Login view at file resources/views/atuh/login.blade.php auth.blade.php layout is extended. At this layout the following code:

...
<div class="position-relative">
                        @if (count($errors) > 0)
                            <div class="alert alert-danger">
                                <strong>Whoops!</strong> There were some problems with your input.<br><br>
                                <ul>
                                    @foreach ($errors->all() as $error)
                                        <li>{{ $error }}</li>
                                    @endforeach
                                </ul>
                            </div>
                        @endif

                        @yield('content')

                    </div>
                    <!-- /.position-relative -->
...

Show error messages if exists just before login form.

Another way of validation in Laravel is using facade Validator defined at config/app.php file at Class aliases:

'aliases' => [
...
'Validator' => 'Illuminate\Support\Facades\Validator',
...

We found an example at Default Laravel Register process at AuthenticatesAndRegistersUsers trait method postRegister:

public function postRegister(Request $request)
	{
		$validator = $this->registrar->validator($request->all());

		if ($validator->fails())
		{
			$this->throwValidationException(
				$request, $validator
	        );
...

Where validator method is defined at App/Services/Registrar.php class:

public function validator(array $data)
	{
		return Validator::make($data, [
			'name' => 'required|max:255',
			'email' => 'required|email|max:255|unique:users',
			'password' => 'required|confirmed|min:6',
		]);
	}

Where name, password and email are required fields. Also some size limitations are set with min and max (password is 6 digits minimum) and email field is of type email ant unique at database (it does not allows to register and already registered email!)

Resources:


Classes and traits used in authentication

Laravel authentication

Change/adapt views to your own style

Are you interested in changing Laravel default "Look&Feel"?

Of course you can change Laravel appeareance in many ways. You can see and example using Ace template at branch acetemplate at Github Repo:

https://github.com/bootstrap-app/laravel/tree/acetemplate

In this repo you can found an example of how to change default Laravel appeareance to customize it to your needs.

The proposed look&feel have the following layout:

AceLayout.png

You can customize the default layout at file resources/views/app.blade.php. The following use the layout shown at previous graph:

<!DOCTYPE html>
<html lang="en">
    @include('partials.htmlheader')
<body class="no-skin">

    @include('partials.navbar')

    <div class="main-container" id="main-container">
        <script type="text/javascript">
            try{ace.settings.check('main-container' , 'fixed')}catch(e){}
        </script>

        @include('partials.sidebar')

        <div class="main-content">
            <div class="main-content-inner">
                @include('partials.breadcrumbs')

                <div class="page-content">
                    @include('partials.settingsbox')
            	    @yield('main-content')
                </div>
                <!-- /page-content -->
            </div>
            <!-- /main-content-inner -->
        </div>
        <!-- /main-content -->

        @include('partials.footer')

    </div>
    <!-- /main-container -->

    @include('partials.scripts')

</body>
</html>

As you can see we use a partial for every layout part. All partials could be found at folder partials:

resources/views/partials$ ls -l
total 56 
-rw-rw-r-- 1 sergi sergi   936 abr  7 12:37 breadcrumbs.blade.php
-rw-rw-r-- 1 sergi sergi  1024 abr  7 12:37 footer.blade.php
-rw-rw-r-- 1 sergi sergi  1583 abr  7 12:37 htmlheader.blade.php
-rw-rw-r-- 1 sergi sergi 14531 abr  7 12:37 navbar.blade.php
-rw-rw-r-- 1 sergi sergi  2885 abr  7 12:37 scripts.blade.php
-rw-rw-r-- 1 sergi sergi  3904 abr  7 12:37 settingsbox.blade.php
-rw-rw-r-- 1 sergi sergi 16513 abr  7 12:37 sidebar.blade.php

See files at github:

https://github.com/bootstrap-app/laravel/tree/acetemplate/resources/views/partials

For Login/Register/Remember password we have to change existing views at folder resources/views/auth:

https://github.com/bootstrap-app/laravel/tree/acetemplate/resources/views/auth

Last be sure to add to public folder the necessary assets of Ace Template:

  • resources/assets/less: replace default laravel less folder with less folder from Ace template. Both Laravel and Ace template use Bootstrap 3 with less but some minor changes are applied in Ace template.
  • public/css/img and public/css/images folder: css files are generated using Laravel Elixir as seen before but you have to copy css images manually
  • public/avatars folder: for avatar sample files
  • public/js folder: all javascript used by ace template
  • public/fonts: custom fonts (FontAwesome, Glyphicons...)

TODO:

  • Using Laravel Elixir to customize frontend
  • Performance tunning concatenating css and javascript files

Using HTML email templates in password recovery

Default mail template Laravel uses are at folder:

resources/views/emails/password.blade.php

As you can see

Click here to reset your password: Plantilla:Url('password/reset/'.$token)

this email is a only text email. We can change the email template here (and maybe localize email). For example we can create and HTML email

NOTA: Ace template have a tool at build/email.html to convert and email to HTML and you can see some HTML email templates at section More Pages > Email Templates

And example could be:

<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="initial-scale=1.0" />
    <meta name="format-detection" content="telephone=no" />
    <title></title>
    <style type="text/css">
        body {
            width: 100%;
            margin: 0;
            padding: 0;
            -webkit-font-smoothing: antialiased;
        }
        @media only screen and (max-width: 600px) {
            table[class="table-row"] {
                float: none !important;
                width: 98% !important;
                padding-left: 20px !important;
                padding-right: 20px !important;
            }
            table[class="table-row-fixed"] {
                float: none !important;
                width: 98% !important;
            }
            table[class="table-col"], table[class="table-col-border"] {
                float: none !important;
                width: 100% !important;
                padding-left: 0 !important;
                padding-right: 0 !important;
                table-layout: fixed;
            }
            td[class="table-col-td"] {
                width: 100% !important;
            }
            table[class="table-col-border"] + table[class="table-col-border"] {
                padding-top: 12px;
                margin-top: 12px;
                border-top: 1px solid #E8E8E8;
            }
            table[class="table-col"] + table[class="table-col"] {
                margin-top: 15px;
            }
            td[class="table-row-td"] {
                padding-left: 0 !important;
                padding-right: 0 !important;
            }
            table[class="navbar-row"] , td[class="navbar-row-td"] {
                width: 100% !important;
            }
            img {
                max-width: 100% !important;
                display: inline !important;
            }
            img[class="pull-right"] {
                float: right;
                margin-left: 11px;
                max-width: 125px !important;
                padding-bottom: 0 !important;
            }
            img[class="pull-left"] {
                float: left;
                margin-right: 11px;
                max-width: 125px !important;
                padding-bottom: 0 !important;
            }
            table[class="table-space"], table[class="header-row"] {
                float: none !important;
                width: 98% !important;
            }
            td[class="header-row-td"] {
                width: 100% !important;
            }
        }
        @media only screen and (max-width: 480px) {
            table[class="table-row"] {
                padding-left: 16px !important;
                padding-right: 16px !important;
            }
        }
        @media only screen and (max-width: 320px) {
            table[class="table-row"] {
                padding-left: 12px !important;
                padding-right: 12px !important;
            }
        }
        @media only screen and (max-width: 458px) {
            td[class="table-td-wrap"] {
                width: 100% !important;
            }
        }
    </style>
</head>
<body style="font-family: Arial, sans-serif; font-size:13px; color: #444444; min-height: 200px;" bgcolor="#E4E6E9" leftmargin="0" topmargin="0" marginheight="0" marginwidth="0">
<table width="100%" height="100%" bgcolor="#E4E6E9" cellspacing="0" cellpadding="0" border="0">
    <tr><td width="100%" align="center" valign="top" bgcolor="#E4E6E9" style="background-color:#E4E6E9; min-height: 200px;">
            <table><tr><td class="table-td-wrap" align="center" width="458"><table class="table-space" height="18" style="height: 18px; font-size: 0px; line-height: 0; width: 450px; background-color: #e4e6e9;" width="450" bgcolor="#E4E6E9" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="18" style="height: 18px; width: 450px; background-color: #e4e6e9;" width="450" bgcolor="#E4E6E9" align="left"> </td></tr></tbody></table>
                        <table class="table-space" height="8" style="height: 8px; font-size: 0px; line-height: 0; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="8" style="height: 8px; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" align="left"> </td></tr></tbody></table>

                        <table class="table-row" width="450" bgcolor="#FFFFFF" style="table-layout: fixed; background-color: #ffffff;" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-row-td" style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; font-weight: normal; padding-left: 36px; padding-right: 36px;" valign="top" align="left">
                                    <table class="table-col" align="left" width="378" cellspacing="0" cellpadding="0" border="0" style="table-layout: fixed;"><tbody><tr><td class="table-col-td" width="378" style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; font-weight: normal; width: 378px;" valign="top" align="left">
                                                <table class="header-row" width="378" cellspacing="0" cellpadding="0" border="0" style="table-layout: fixed;"><tbody><tr><td class="header-row-td" width="378" style="font-family: Arial, sans-serif; font-weight: normal; line-height: 19px; color: #478fca; margin: 0px; font-size: 18px; padding-bottom: 10px; padding-top: 15px;" valign="top" align="left">Password Recovery</td></tr></tbody></table>
                                                <div style="font-family: Arial, sans-serif; line-height: 20px; color: #444444; font-size: 13px;">
                                                    <b style="color: #777777;">You (or someone else) have requested to reset your password. Please ignore this email if you have not requested a password recovery</b>
                                                    <br>
                                                    Click here to reset your password:
                                                </div>
                                            </td></tr></tbody></table>
                                </td></tr></tbody></table>

                        <table class="table-space" height="12" style="height: 12px; font-size: 0px; line-height: 0; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="12" style="height: 12px; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" align="left"> </td></tr></tbody></table>
                        <table class="table-space" height="12" style="height: 12px; font-size: 0px; line-height: 0; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="12" style="height: 12px; width: 450px; padding-left: 16px; padding-right: 16px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" align="center"> <table bgcolor="#E8E8E8" height="0" width="100%" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td bgcolor="#E8E8E8" height="1" width="100%" style="height: 1px; font-size:0;" valign="top" align="left"> </td></tr></tbody></table></td></tr></tbody></table>
                        <table class="table-space" height="16" style="height: 16px; font-size: 0px; line-height: 0; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="16" style="height: 16px; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" align="left"> </td></tr></tbody></table>

                        <table class="table-row" width="450" bgcolor="#FFFFFF" style="table-layout: fixed; background-color: #ffffff;" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-row-td" style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; font-weight: normal; padding-left: 36px; padding-right: 36px;" valign="top" align="left">
                                    <table class="table-col" align="left" width="378" cellspacing="0" cellpadding="0" border="0" style="table-layout: fixed;"><tbody><tr><td class="table-col-td" width="378" style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; font-weight: normal; width: 378px;" valign="top" align="left">
                                                <div style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; text-align: center;">
                                                    <a href="{{ url('password/reset/'.$token) }}" style="color: #ffffff; text-decoration: none; margin: 0px; text-align: center; vertical-align: baseline; border: 4px solid #6fb3e0; padding: 4px 9px; font-size: 15px; line-height: 21px; background-color: #6fb3e0;">  Recover password  </a>
                                                </div>
                                                <table class="table-space" height="16" style="height: 16px; font-size: 0px; line-height: 0; width: 378px; background-color: #ffffff;" width="378" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="16" style="height: 16px; width: 378px; background-color: #ffffff;" width="378" bgcolor="#FFFFFF" align="left"> </td></tr></tbody></table>
                                            </td></tr></tbody></table>
                                </td></tr></tbody></table>

                        <table class="table-space" height="6" style="height: 6px; font-size: 0px; line-height: 0; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="6" style="height: 6px; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" align="left"> </td></tr></tbody></table>

                        <table class="table-row-fixed" width="450" bgcolor="#FFFFFF" style="table-layout: fixed; background-color: #ffffff;" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-row-fixed-td" style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; font-weight: normal; padding-left: 1px; padding-right: 1px;" valign="top" align="left">
                                    <table class="table-col" align="left" width="448" cellspacing="0" cellpadding="0" border="0" style="table-layout: fixed;"><tbody><tr><td class="table-col-td" width="448" style="font-family: Arial, sans-serif; line-height: 19px; color: #444444; font-size: 13px; font-weight: normal;" valign="top" align="left">
                                                <table width="100%" cellspacing="0" cellpadding="0" border="0" style="table-layout: fixed;"><tbody><tr><td width="100%" align="center" bgcolor="#f5f5f5" style="font-family: Arial, sans-serif; line-height: 24px; color: #bbbbbb; font-size: 13px; font-weight: normal; text-align: center; padding: 9px; border-width: 1px 0px 0px; border-style: solid; border-color: #e3e3e3; background-color: #f5f5f5;" valign="top">
                                                            <a href="#" style="color: #428bca; text-decoration: none; background-color: transparent;">Ace © 2014</a>
                                                            <br>
                                                            <a href="#" style="color: #478fca; text-decoration: none; background-color: transparent;">twitter</a>
                                                            .
                                                            <a href="#" style="color: #5b7a91; text-decoration: none; background-color: transparent;">facebook</a>
                                                            .
                                                            <a href="#" style="color: #dd5a43; text-decoration: none; background-color: transparent;">google+</a>
                                                        </td></tr></tbody></table>
                                            </td></tr></tbody></table>
                                </td></tr></tbody></table>
                        <table class="table-space" height="1" style="height: 1px; font-size: 0px; line-height: 0; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="1" style="height: 1px; width: 450px; background-color: #ffffff;" width="450" bgcolor="#FFFFFF" align="left"> </td></tr></tbody></table>
                        <table class="table-space" height="36" style="height: 36px; font-size: 0px; line-height: 0; width: 450px; background-color: #e4e6e9;" width="450" bgcolor="#E4E6E9" cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="table-space-td" valign="middle" height="36" style="height: 36px; width: 450px; background-color: #e4e6e9;" width="450" bgcolor="#E4E6E9" align="left"> </td></tr></tbody></table></td></tr></table>
        </td></tr>
</table>
</body>
</html>

Implementing register email confirmation

TODO

Custom 404 and 500 error pages

You can customize error pages like 404 not found or 500 Server error at folder:

resources/views/errors

By default Laravel includes a 503.blade.php file. Add your custom files:

404.blade.php
500.blade.php

With your own content.

You can test error pages using Facade App and method Abort

App::abort(404);

o

App::abort(500);

See branch acetemplate at:

https://github.com/bootstrap-app/laravel/tree/acetemplate

For an example how I integrate a custom template with custom 404 and 500 page errors.

Language support. Adding catalan support for default Laravel Auth forms and Form validation

Laravel localization is supported using Lang facade. Language files are stored at:

resources/lang

Using iso locales to store at separate folders specific locales. And example:

/resources
   /lang
       /en
           messages.php
       /es
           messages.php

By default Laravel only propose you English files at folder:

$ ls -l resources/lang/en/
total 16
-rw-rw-r-- 1 sergi sergi  502 mar 18 10:37 pagination.php
-rw-rw-r-- 1 sergi sergi  744 mar 18 10:37 passwords.php
-rw-rw-r-- 1 sergi sergi 5184 mar 18 10:37 validation.php

To start using another language first copy en folder:

$ cd resources/lang
$ cp -r en ca

Where ca is catalan locale.

Then you can change pagination, password and validation files to your language.

The default language for your application is stored in the

config/app.php 

configuration file. You may change the active language at any time using the App::setLocale method:

App::setLocale('ca');

You can use a localizated string with:

echo Lang::get('messages.welcome');

where messages is the file name and welcome is the string.

Plantilla:Note

Adding social login: Facebook, Twitter, Google Plus. Laravel Socialite

Socialite is a Laravel component...

Resources:

Basic Intranet Layout and navigation

TODO

Adding user profile section

TODO

Users managment CRUD/API

TODO

Payment subscriptions. Stripe and Laravel Cashier

Using Ace template ( see html/pricing.html ) or you favourite template/style you can create a subscription/plans page similar to:

price table example

Please create a new section on sidebar called Plans:

1) Edit file resources/views/partials/sidebar.blade.php (Contains Ace Template Sidebar) 2) Add a new section in nav-list after Home section:

<li class="">
   <a href="/plans/choose">
       <i class="menu-icon fa fa-home"></i>
       <span class="menu-text"> Plans </span>
   </a>
   <b class="arrow"></b>
</li>

Then configure a new route and controller. Route URL and Controller class name will be:

  • route: plans
  • controller: PlansController

Create PlansController using:

$ php artisan make:controller PlansController --plain
Controller created successfully.

And modify routes.php to add plan route:

 Route::controllers([
	'auth' => 'Auth\AuthController',
	'password' => 'Auth\PasswordController',
	'plans' => 'PlansController',
]);

Now the URL:

http://laravelnotificationsapp.app/plans/choose

Give you a 404 Error. Change Controller to:

<?php namespace LaravelNotificationApp\Http\Controllers;

use LaravelNotificationApp\Http\Requests;
use LaravelNotificationApp\Http\Controllers\Controller;

use Illuminate\Http\Request;

class PlansController extends Controller {

    public function __construct() {

    }

    public function getChoose(){
        return view('plans.choose');
    }
}

Now create file resources/views/plans/choose.blade.php with following content:

 @extends('app')

@section('main-content')
    <div class="position-relative">
        @if (isset($subscriptionresult))
            <div class="alert alert-info">
                <strong>Suscription to plan: {{ $plan }} done!<br>
            </div>
        @endif
    </div>
    <!-- /.position-relative -->

    <div class="row">
        <!-- #section:pages/pricing.large -->
        <div class="col-xs-6 col-sm-3 pricing-box"/>

        </div>

        <div class="col-xs-6 col-sm-3 pricing-box">
            <div class="widget-box widget-color-orange">
                <div class="widget-header">
                    <h5 class="widget-title bigger lighter">Basic Package</h5>
                </div>

                <div class="widget-body">
                    <div class="widget-main">
                        <ul class="list-unstyled spaced2">
                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                50 GB Disk Space
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                1 TB Bandwidth
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                1000 Email Accounts
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                100 MySQL Databases
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                $25 Ad Credit
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                Free Domain
                            </li>
                        </ul>

                        <hr />
                        <div class="price">
                            $5
                            <small>/month</small>
                        </div>
                    </div>

                    <div>
                        <div class="btn btn-block btn-warning">
                            <form action="" method="POST">
                                    <input type="hidden" name="_token" value="{{ csrf_token() }}">
                                    <input type="hidden" name="plan" value="basic">
                                    <script
                                            src="https://checkout.stripe.com/checkout.js" class="stripe-button"
                                            data-key="pk_test_K19paoH5iQhKhLqvxt5STeRm"
                                            data-amount="500"
                                            data-name="basic"
                                            data-description="Basic Package ($5.00)"
                                            data-image="/img/Opml-icon-128x128.png">
                                    </script>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="col-xs-6 col-sm-3 pricing-box">
            <div class="widget-box widget-color-blue">
                <div class="widget-header">
                    <h5 class="widget-title bigger lighter">Starter Package</h5>
                </div>

                <div class="widget-body">
                    <div class="widget-main">
                        <ul class="list-unstyled spaced2">
                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                200 GB Disk Space
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                Unlimited Bandwidth
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                1000 Email Accounts
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                200 MySQL Databases
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                $25 Ad Credit
                            </li>

                            <li>
                                <i class="ace-icon fa fa-check green"></i>
                                Free Domain
                            </li>
                        </ul>

                        <hr />
                        <div class="price">
                            $10
                            <small>/month</small>
                        </div>
                    </div>

                    <div>
                        <div class="btn btn-block btn-primary">
                            <form action="" method="POST">
                                <input type="hidden" name="_token" value="{{ csrf_token() }}">
                                <input type="hidden" name="plan" value="starter">
                                <script
                                        src="https://checkout.stripe.com/checkout.js" class="stripe-button"
                                        data-key="pk_test_K19paoH5iQhKhLqvxt5STeRm"
                                        data-amount="1000"
                                        data-name="starter"
                                        data-description="Starter Package ($10.00)"
                                        data-image="/img/Opml-icon-128x128.png">
                                </script>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="col-xs-6 col-sm-3 pricing-box">

        </div>

        <!-- /section:pages/pricing.large -->
    </div>

@endsection

Now basic controller and view are ready. See in next screenshoot how you app would be:

LaravelCashierScreenshoot.png

We will use Stripe for checkout/payment. First create a Stripe user account:

https://stripe.com/es

Once you have an account grab the API keys at (Your Account/ Account settings/API_KEYS):

https://dashboard.stripe.com/account/apikeys

Goto:

https://dashboard.stripe.com/test/plans

In our example we offer two plans:

Basic: 10$/month
Starter: 15$/month

So create 2 plans in stripe. Let's continue... As seen on Stripe docs:

https://stripe.com/docs/tutorials/checkout

Click on Pay with card to see and example of embedded form. We will use an embedded form, something similar to:

<form action="" method="POST">
  <script
    src="https://checkout.stripe.com/checkout.js" class="stripe-button"
    data-key="pk_test_K19paoH5iQhKhLqvxt5STeRm"
    data-amount="2000"
    data-name="Demo Site"
    data-description="2 widgets ($20.00)"
    data-image="/128x128.png">
  </script>
</form>

The form have to be changed to our needs, data-key is publishable API_KEY. If you have been copied the form from Stripe docs web page Stripe will automatically fill data-key field with your public API KEY. Please change other fields at your needs:

amount (data-amount) on cents (dollars)
data-image is a 128x128 icon
data-name
data-description

Now, how we process Stripe checkout?

Form submit will be processed by Stripe but once it finishs it will return a POST HTTP request to same origin URL (in our case plans/choose). So we have to create a method in our controller to process this response, at first we cant test it with. For example add method:

public function postChoose(){
       dd(Input::all());
   }

to PlansController for testing.

As you can see on output debug a field stripeToken is returned: this is a representation of the credit card and stripeEmail is the email of the user. So to saved that:

$token = Input::get('stripeToken');

With this token now we are ready to use Laravel cashier to subscibe our user to a plan, the final subscription code will be:

$token = Input::get('stripeToken');
Auth::user()->subscription('basic')->create($token);

Moreover is important to show to the user a confirmation message, so a possible final example of PlansController will be:

<?php namespace LaravelNotificationApp\Http\Controllers;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Session;
use LaravelNotificationApp\Http\Requests;
use LaravelNotificationApp\Http\Controllers\Controller;

use Illuminate\Http\Request;

class PlansController extends Controller {

    public function __construct() {

    }

	//
    public function getChoose(){
        return view('plans.choose');
    }

    public function postChoose(){
        //Uncomment for debugging
        //dd(Input::all());
        
        $token = Input::get('stripeToken');
        $plan = Input::get('plan');
        Auth::user()->subscription($plan)->create($token);

        return view('plans.choose')->with('subscriptionresult',1)->with('plan',$plan);
    }
}

But this code will not work untill basic and starter plans will be created at Stripe page:

https://dashboard.stripe.com/test/plans

See example:

Also is necessary to customize our database and User Model. First install Laravel Cashier ([4]):

$ composer require "laravel/cashier:~4.0"

And add Service Provider to config/app.php file:

Laravel\Cashier\CashierServiceProvider

Now is time to modify user Model an database. Change your User Model and add Billable trait and implements BillableContract and also at $dates attribute as follows:

...
use Laravel\Cashier\Billable;
use Laravel\Cashier\Contracts\Billable as BillableContract;

class User extends Eloquent implements AuthenticatableContract, CanResetPasswordContract, BillableContract {

    use Authenticatable, CanResetPassword, Billable;

    protected $dates = ['trial_ends_at', 'subscription_ends_at'];

}

Aftet installation of Laravel Cashier and additional artisan command is available to create user migration table with:

$ php artisan cashier:table users

Now a new migration is available at database/migrations:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddCashierColumns extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::table('users', function(Blueprint $table)
		{
			$table->tinyInteger('stripe_active')->default(0);
			$table->string('stripe_id')->nullable();
			$table->string('stripe_subscription')->nullable();
			$table->string('stripe_plan', 100)->nullable();
			$table->string('last_four', 4)->nullable();
			$table->timestamp('trial_ends_at')->nullable();
			$table->timestamp('subscription_ends_at')->nullable();
		});
	}

	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		Schema::table('users', function(Blueprint $table)
		{
			$table->dropColumn(
				'stripe_active', 'stripe_id', 'stripe_subscription', 'stripe_plan', 'last_four', 'trial_ends_at', 'subscription_ends_at'
			);
		});
	}

}

Execute migration with:

$ homestead ssh
$ php artisan migrate

This will add to users table the following fields:

LaraveUsersTableWithcashier.png

And finally add STRIPE_API_SECRET to config/services.php:

'stripe' => [
    'model'  => 'User',
    'secret' => $_ENV['STRIPE_API_SECRET'],
],

And add key to environment file (.env):

STRIPE_API_SECRET=sk_test_deKgoerSCDlwgiWDxDefcRT1C
 

Now you can test checkout form. For testing purposes you can use test card numbers:

Credit Card: 4242 4242 4242 4242
CVC: any three digit 
A valid expiry date.

If you get error:

TokenMismatchException in VerifyCsrfToken.php ...

Check you have added CSFR token at Cashier Form:

<input type="hidden" name="_token" value="Plantilla:Csrf token()">

Once checkout is done correctly go to Stripe Dashboard and check for a new income and go to Customers section to see your new client! You can see tag laravel_cashier_stripe_0 at github to see result source code:

https://github.com/acacha/laravelnotificationsapp
https://github.com/acacha/laravelnotificationsapp/tree/laravel_cashier_stripe_0

Resources

Implementing breadcrumps

Breadcrump TODO

https://github.com/davejamesmiller/laravel-breadcrumbs

Sending push notifications with pushboots

TODO

Troubleshooting

Logs where searching for errors

Homestead use nginx. Web server errors could be found at:

$ sudo tail -f /var/log/nginx/error.log 

BUT BE CAREFUL! because Homestead could cotain multiple Virtual hosts so see your virtual hosts config, for example:

$ cat /etc/nginx/sites-available/laravelnotificationsapp.app
...
/var/log/nginx/laravelnotificationsapp.app-error.log
...

Then the log file to tail is:

$ tail -f /var/log/nginx/laravelnotificationsapp.app-error.log

Laravel specific errors could be found at folder storage/logs, for example:

$ tail -f storage/logs/laravel-2015-03-18.log 

Logs are stored by day following default configuration at main configuration file

$ cat config/app.php | grep -B 5 log
	/*
	|--------------------------------------------------------------------------
	| Logging Configuration
	|--------------------------------------------------------------------------
	|
	| Here you may configure the log settings for your application. Out of
	| the box, Laravel uses the Monolog PHP logging library. This gives
	| you a variety of powerful log handlers / formatters to utilize.
	|
	| Available Settings: "single", "daily", "syslog", "errorlog"
	|
	*/

	'log' => 'daily',

IMPORTANT: One common problem is searching for errors at a diferent Virtual Host for example default virtual host. This kind of errors are related to nginx Virtual Host configuration errors or homestead edit/configuration file errors at sites section.

In explotation environments DEBUG info could be deactivated. But in Homestead machine be sure that APP_DEBUG environment variable is set to true:

$ cat .env
APP_ENV=local
APP_DEBUG=true

And check config/app.php:

$ cat config/app.php
'debug' => env('APP_DEBUG'),

Connection to API is ok but emails are not sent

At Mailchimp see section:

Settings > API logs

A common errors is not giving an specific from email:

 Full Response
[
    {
        "email": "[email protected]",
        "status": "rejected",
        "_id": "ec0f54be30d640f9ba60582aefcaae84",
        "reject_reason": "invalid-sender"
    }
]

See how email is rejected! ("reject_reason": "invalid-sender")

Github repo. Source code

https://github.com/acacha/laravelnotificationsapp

Students repos

2DAM_2014-15:

Amposta Boquera, Andreu | repo:
Coronciuc, Liviu | repo: https://github.com/Kraissus/laravelnotificationsapp
Davila Bazalar, Paolo | repo: https://github.com/pdavila13/laravelnotificationsapp
Domenech Canalda, Marc | repo: https://github.com/markdomkan/laravelnotificationsapp
Martinez Capilla, Alexandre | https://github.com/AlexMartCap/laravelnotificationsapp
Martinez Oliver, Carlos | repo:
Mayor Rodriguez, Marc Albert | repo:
Ricote Lajusticia, Rafael | repo: https://github.com/rricote/laravelnotificationsapp
Roldan Baena, Ivan | repo: https://github.com/kriminal666/laravelnotificationsapp
Turcan, Nicolae | repo: https://github.com/nicolaeturcan/laravelnotificationsapp_v2
Ungureanu, Dorian | repo: https://github.com/cinek19/laravelnotificationsapp

See also

External links