Docker Laravel Dev Environment

This post documents building a local Laravel development environment with Docker. Included are examples for debugging Laravel’s PHP with Xdebug using the Visual Studio Code editor. Source Code available on GitHub.

Install Laravel

In this example, we will be using the Composer Dependency Manager for PHP to install Laravel. To check if Composer is installed globally and in your PATH, enter composer --version in the CLI, for example,

composer --version
Composer version 1.4.1 2017-03-10 09:29:45

Get Composer if you need to install it.

With Composer, use the create-project command and the laravel/laravel package name followed by the directory to create the project in. The optional third argument is for a version number. For example, to install Laravel version 5.5 into a local directory such as home/laravel/mysite on the host computer, open a CLI then execute the composer create-project command from the directory where you want to create your project. For example:

cd ~/laravel

composer create-project laravel/laravel mysite "5.5.*"

Docker

Once composer is finished creating the Laravel project, we are ready to create the docker containers to host it. The following examples require both Docker and Docker Compose. Head on over to Docker, select Get Docker and the platform of your choice to install Docker on your computer. This should install both Docker and Docker Compose. The examples in this post were written while using Docker Community Edition, Version 17.09.0-ce.

Start Docker as needed and test the installation. Open a CLI and issue these commands.

docker --version

docker-compose --version

For the Windows platform with IIS installed, port 80 is likely in use. To eliminate conflicts, shut down the IIS web server. Open an admin command prompt and enter net stop was

PHP Container

This dockerfile builds from the official Docker Hub PHP image. At the time of this writing, php 7.1.10 was the latest non RC version available in the library. If you want to use a different version, change the php version as needed, such as FROM php:5.6.31-fpm.

Create the app.dockerfile in the root of the laravel project. For example, home/laravel/mysite/app.dockerfile

app.dockerfile
FROM php:7.1.10-fpm

# php-fpm default WORKDIR is /var/www/html
# change it to /var/www
WORKDIR /var/www

RUN apt-get update && apt-get install -y \
    libmcrypt-dev \
    mysql-client --no-install-recommends \
    && docker-php-ext-install mcrypt pdo_mysql \
    && pecl install xdebug \
    && echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_enable=1\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_autostart=1\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_connect_back=0\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_host=10.0.75.1\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_port=9001\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.idekey=REMOTE\n" >> /usr/local/etc/php/conf.d/xdebug.ini

For Mac platforms, change the app.dockerfile xdebug remote host IP, e.g., xdebug.remote_host=10.254.254.254

Then create an alias for IP 10.254.254.254 to your existing subnet mask as follows:

sudo ifconfig en0 alias 10.254.254.254 255.255.255.0

If you want to remove the alias, use sudo ifconfig en0 -alias 10.254.254.254

Nginx Container

Create a nginx server block configuration file for php and the Laravel project root.

nginx.conf
server {
    listen 80;
    index index.php index.html;
    root /var/www/public;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

This dockerfile builds from the official Docker Hub nginx image adding the nginx.conf Nginx server block configuration file to /etc/nginx/conf.d on the container.

web.dockerfile
FROM nginx:1.12

ADD nginx.conf /etc/nginx/conf.d/default.conf

The next page covers creating the docker-compose file, container build, create, start and stop. Xdebug and launch config for VS Code PHP debugging.


Laravel Tips and Tricks

Composer

Uninstall a Package

Running the following command will remove the package and update composer.json and composer.lock. You will need to manually remove references to that package within your app.

composer remove vendor/package

Followed by composer update to remove vendor/package folder from vendor

Laravel JWT Auth with Vue.js 2

This post is a refresh of Laravel JWT Auth with Vue.js I posted in September that applies to the 1.x version of Vue.js. Evan You released vue.js version 2 shortly thereafter and this post will cover building the Laravel JSON Web Token based authentication with it.

February 11, 2017 – A lot has changed in the last 4 months with Vue.js and Webpack. Since Vue.js 2.1.x and Webpack 2.2.x were released last month, this post originally published in November has been updated for these new versions.

Included are some notes about refactoring the application from vue.js 1.x to version 2.1. Changes also include using NPM scripts with webpack version 2 for the front end build process instead of Elixir and Browserify.

Environment

For this tutorial, Laravel 5.2 has been installed locally and it is being served by XAMMP on Windows 10.

Command Line Tools
Nodejs version 6.9.4
NPM version 3.10.10
vue-cli version 2.8.1
git version 2.10.2

User Model

After setting the database connection parameters in env.php, run the database migration script to create the users, password_resets and migrations tables. In my Laravel application, I have moved the Users class into a Models folder.

php artisan migrate

mkdir app/Models

mv app/User.php app/Models

Edit app/Models/User.php, change the App namespace to App\Models.

User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    ...

  • Throughout this tutorial you will encounter an ellipsis … in the code examples. These are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. To view the entire file, examine the source code.

Edit, config/auth.php. Update the authentication drivers user provider for the App\Models\User namespace change as follows.

auth.php
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],

    ...

Webpack Vue.js CLI

A simple webpack vue-loader setup is being used for the front end build process. First step is to install vue-cli globally.

npm install -g vue-cli
  • If you have an older version of vue-cli, you will need to update it to version 2.8.
npm update -g vue-cli

Using vue-cli from the laravel root directory, install the webpack-simple template into the existing laravel resources folder.

vue init webpack-simple resources

After the webpack-simple template has been installed. Change to the resources directory and run the npm install. This will install vue 2.1.x, vue-loader, vue-template-compiler, webpack 2.2.x and other dependencies. For details, inspect the install manifest resources/package.json.

cd resources

npm i
  • For example, the vue-cli 2.8.0 release from January, 2017 installs vue 2.1.10, vue-loader 10.0.2, vue-template-compiler 2.1.10 and webpack 2.2.0.

Vue Router and Components

Install the router for vue.js 2.x.

npm i vue-router --save-dev

Create a resources/assets/components folder and add an empty App.vue, Dashboard.vue, Home.vue, Register.vue and Signin.vue component.

mkdir assets/components

touch assets/components/App.vue

touch assets/components/Dashboard.vue

touch assets/components/Home.vue

touch assets/components/Register.vue

touch assets/components/Signin.vue

Add the following code to the App.vue base layout component. The new <router-link> component replaces the the deprecated v-link directive from vue.js 1.x.

App.vue
<template>
    <div class="panel panel-default">
        <div class="panel-heading">
            <nav>
                <ul class="list-inline">
                    <li>
                        <router-link :to="{ name: 'home' }">Home</router-link>
                    </li>
                    <li class="pull-right">
                        <router-link :to="{ name: 'register' }">Register</router-link>
                    </li>
                </ul>
            </nav>
        </div>
        <div class="panel-body">
            <router-view></router-view>
        </div>
    </div>
</template>

Add the following code to the Home.vue component.

Home.vue
<template>
    <h1>Laravel 5</h1>
</template>

Add the following code to the Register.vue component. Note the change from version 1.0 of this component. A div has been added at the root of the template since components now must have exactly one root element.

Register.vue
<template>
    <div>
        <div class="alert alert-danger" v-if="error && !success">
            <p>There was an error, unable to complete registration.</p>
        </div>
        <div class="alert alert-success" v-if="success">
            <p>Registration completed. You can now sign in.</p>
        </div>
        <form autocomplete="off" v-on:submit="register" v-if="!success">
            <div class="form-group" v-bind:class="{ 'has-error': error && response.username }">
                <label for="name">Name</label>
                <input type="text" id="name" class="form-control" v-model="name" required>
                <span class="help-block" v-if="error && response.name">{{ response.name }}</span>
            </div>
            <div class="form-group" v-bind:class="{ 'has-error': error && response.email }">
                <label for="email">E-mail</label>
                <input type="email" id="email" class="form-control" placeholder="gavin.belson@hooli.com" v-model="email" required>
                <span class="help-block" v-if="error && response.email">{{ response.email }}</span>
            </div>
            <div class="form-group" v-bind:class="{ 'has-error': error && response.password }">
                <label for="password">Password</label>
                <input type="password" id="password" class="form-control" v-model="password" required>
                <span class="help-block" v-if="error && response.password">{{ response.password }}</span>
            </div>
            <button type="submit" class="btn btn-default">Submit</button>
        </form>
    </div>
</template>

JavaScript

Create a resources/assets/js folder for the JavaScript.

mkdir assets/js

Create a resources/assets/js/app.js JavaScript file.

touch assets/js/app.js

Add the following code to the app.js file to import vue modules, components and define the router. Exporting will allow other modules in this project to import them.

Note the changes between version 1.0 of app.js.

  1. router.map has been replaced with an array on the new routes option.
  2. router is passed to the new Vue instance as option since starting an app with routing no longer requires a special method.
app.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from '../components/App.vue';
import Dashboard from '../components/Dashboard.vue';
import Home from '../components/Home.vue';
import Register from '../components/Register.vue';
import Signin from '../components/Signin.vue';

Vue.use(VueRouter);

export default Vue;

export var router = new VueRouter({
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home
        },
        {
            path: '/register',
            name: 'register',
            component: Register
        }
    ]
});

new Vue({
    el: '#app',
    router: router,
    render: app => app(App)
});

Web Page

Edit the resources/views/welcome.blade.php template.

Replace the entire contents of the file with this markup.

welcome.blade.php
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{!! csrf_token() !!}">
    <title>Laravel</title>

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

</head>
<body>
    <div class="container">
        <div id="app"></div>
    </div>
    <script src="/js/app.js"></script>
</body>
</html>

Webpack

Edit the resources/webpack.config.js entry and output values.

REPLACE

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'build.js'
  },
  ...
}

WITH

module.exports = {
  entry: './assets/js/app.js',
  output: {
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js/',
    filename: 'app.js'
  },
  ...
}

Test drive the updated webpack configuration with the npm build script.

npm run build

Your build output should be similar to this. The laravel public folder should now contain js/app.js and a respective js/app.js.map source map.

> resources@ build C:\xampp\htdocs\laravel\resources
> cross-env NODE_ENV=production webpack --progress --hide-modules

Hash: 260fd0b01225d3f55a16
Version: webpack 2.2.1
Time: 6031ms
     Asset    Size  Chunks             Chunk Names
    app.js  118 kB       0  [emitted]  main
app.js.map  884 kB       0  [emitted]  main
Laravel JWT Auth Vue.js 2.0 Home Page

The next page covers Vue Resource for request handling, Form Request Validation, API authorization with JWT Auth, User endpoint and Sign in.


Vue.js CLI Webpack Laravel Proof of Concept

This post is a simple proof of concept for using vue-cli to scaffold a Webpack Laravel project. The production build modifies the default Laravel blade view and outputs the built assets into the public/static directory. The development workflow demonstrates hot reloading and css style extraction.

Environment

This example was created using a fresh install of Laravel 5.2 served by XAMMP on Windows 10.
Nodejs version 6.9.1
NPM version 3.10.8
vue-cli version 2.4.0
git version 2.7.0

Development

Change to the laravel directory. For example,

cd c:/xampp/htdocs/laravel

Use vue-cli to download vuejs, wepbpack and template dependencies and create the project in the laravel/resources folder.

vue init webpack resources

Change to the resources directory.

cd resources

Install node modules with npm install or npm i.

npm i

Run development server.

  • Check the dev.port in resources/config/index.js to make sure it is not the same as the port used by the XAMPP Apache server.
npm run dev

Update the /resources/src/components/Hello.vue vue commponent to see the hot reloading take place. In the script portion of the component, change the msg value. For example, ‘Welcome to Your Vue.js Laravel App’.

Hello.vue
<script>
export default {
  name: 'hello',
  data () {
    return {
      msg: 'Welcome to Your Vue.js Laravel App'
    }
  }
}
</script>
  • For development on Laravel sever generated pages, another solution is to update the package.json scripts property so the npm run dev command runs webpack development compilation with watch mode enabled. Watch mode recompiles after every change is saved.
package.json
"dev": "cross-env NODE_ENV=development webpack --watch --progress --colors"

Production

Edit /resources/config/index.js. Update the build paths so the welcome.blade.php view gets the index markup and the assets are built to the public/static folder.

index.js
build: {
    ...
    index: path.resolve(__dirname, '../views/welcome.blade.php'),
    assetsRoot: path.resolve(__dirname, '../../public'),
  • The ellipsis … in the snippet above is not a part of the code and is there only to denote lines that are skipped and not applicable to the example.

Run production build.

npm run build

Manually refresh the laravel.dev page served by XAMPP to verify the production build.

Resources

Laravel JWT Auth with Vue.js

This post documents using Laravel to build JSON Web Token based authentication with a Vue.js 1.0 user interface. UPDATE: vue.js 2.0 version published.

A fresh install of Laravel 5.2 for local development is required. Head over to the Laravel 5.2 docs to get setup with Composer and Laravel as needed. This post contains some info on how I installed Laravel 5.2 on Windows. Once Laravel 5.2 is installed, the database created and the .env file updated to connect to it, run the migration to create the users table.

php artisan migrate

User Model

One of the first things I like to do is create a Models folder and move the User model into it. To organize models within their own directory, start by creating a folder named Models in the app directory and move the User model into it.

mkdir app/Models

mv app/User.php app/Models

Edit app/Models/User.php, change the App namespace to App\Models.

User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    ...

  • Throughout this tutorial you will encounter an ellipsis … in the code examples. These are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. To view the entire file, examine the source code.

Edit, config/auth.php. Update the authentication drivers user provider for the App\Models\User namespace change as follows.

auth.php
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],

    ...

Laravel Elixir Vueify

Using NPM, install the dependencies including laravel-elixer and gulp that should already be defined in the package.json file.

npm install

Install the Laravel Elixir and the Browserify Vueify plugin wrapper, laravel-elixir-vueify. This node package also includes vue.js.

For Laravel Elixer 5
npm install laravel-elixir-vueify@1.0.6 --save-dev

Laravel Elixir is a build tool wrapper for Gulp. To use laravel-elixir-vueify, require laravel-elixir-vueify and update the elixir call with mix.browserify in the gulpfile.js as follows.

REPLACE

elixir(function(mix) {
    mix.sass('app.scss');
});

WITH

require('laravel-elixir-vueify');

elixir(function(mix) {
    mix.browserify('app.js');
    mix.sass('app.scss');
});

Create a resources/assets/js folder for the JavaScript.

mkdir resources/assets/js

Create a resources/assets/js/app.js JavaScript file.

touch resources/assets/js/app.js

Test the gulp build. A browserified app.js file that includes an external source map file should get compiled and written to public/js/app.js. The pre-existing app.scss Sass file should also get compiled.

gulp
Default Gulp task output
Default Gulp task output

Vue Router and Components

Install the router for Vue.js.

npm install vue-router@0.7.13 --save-dev
  • The npm install vue-router command above has been updated to install the version supported by the 1.x version of vue.js. This is the vue.js version installed with laravel-elixir-vueify@1.0.6.

Create a resources/assets/components folder and add an empty App.vue, Dashboard.vue, Home.vue, Register.vue and Signin.vue component.

mkdir resources/assets/components

touch resources/assets/components/App.vue

touch resources/assets/components/Dashboard.vue

touch resources/assets/components/Home.vue

touch resources/assets/components/Register.vue

touch resources/assets/components/Signin.vue

Add the following code to the empty App.vue file which will be the base layout component.

App.vue
<template>
    <div class="panel panel-default">
        <div class="panel-heading">
            <nav>
                <ul class="list-inline">
                    <li><a v-link="{ name: 'home' }">Home</a></li>
                    <li class="pull-right">
                        <a v-link="{ name: 'register' }">Register</a>
                    </li>
                </ul>
            </nav>
        </div>
        <div class="panel-body">
            <router-view></router-view>
        </div>
    </div>
</template>

Add the following code to the empty Home.vue file. The content in this template will populate the App.vue router-view element by default.

Home.vue
<template>
    <h1>Laravel 5</h1>
</template>

Add the following code to the empty Register.vue file for the user account creation form.

Register.vue
<template>
    <div class="alert alert-danger" v-if="error && !success">
        <p>There was an error, unable to complete registration.</p>
    </div>
    <div class="alert alert-success" v-if="success">
        <p>Registration completed. You can now sign in.</p>
    </div>
    <form autocomplete="off" v-on:submit="register" v-if="!success">
        <div class="form-group" v-bind:class="{ 'has-error': error && response.username }">
            <label for="name">Name</label>
            <input type="text" id="name" class="form-control" v-model="name" required>
            <span class="help-block" v-if="error && response.name">{{ response.name }}</span>
        </div>
        <div class="form-group" v-bind:class="{ 'has-error': error && response.email }">
            <label for="email">E-mail</label>
            <input type="email" id="email" class="form-control" placeholder="gavin.belson@hooli.com" v-model="email" required>
            <span class="help-block" v-if="error && response.email">{{ response.email }}</span>
        </div>
        <div class="form-group" v-bind:class="{ 'has-error': error && response.password }">
            <label for="password">Password</label>
            <input type="password" id="password" class="form-control" v-model="password" required>
            <span class="help-block" v-if="error && response.password">{{ response.password }}</span>
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
    </form>
</template>

Add the following code to the empty app.js file to require vue, the vue-router, import vue components, setup the router. Export Vue and the router to allow modules to import them.

app.js
var Vue = require('vue');
var VueRouter = require('vue-router');

import App from '../components/App.vue';
import Dashboard from '../components/Dashboard.vue';
import Home from '../components/Home.vue';
import Register from '../components/Register.vue';
import Signin from '../components/Signin.vue';

Vue.use(VueRouter);

export default Vue;
export var router = new VueRouter

router.map({
    '/': {
        name: 'home',
        component: Home
    },
    '/register': {
        name: 'register',
        component: Register
    }
});

router.start(App, '#app');

Web Page

Edit the resources/views/welcome.blade.php template.

Replace the entire contents of the file with this markup.

welcome.blade.php
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{!! csrf_token() !!}">
    <title>Laravel</title>

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

</head>
<body>
    <div class="container">
        <div id="app"></div>
    </div>
    <script src="/js/app.js"></script>
</body>
</html>

Now is a good time to test that everything builds and you have a Bootstrap styled page with with links to Home and Register in a horizontal navigation bar and a content panel under it with “Laravel 5” heading text. Select the Register link to diplay the form in the content panel.

gulp
Laravel JWT Auth Vuejs User Registration Form
New User Registration Form

The next page covers Vue Resource for request handling, Form Request Validation, API authorization with JWT Auth, User endpoint and Sign in.


Laravel Install on Windows IIS

This post documents installing PHP 7, PHP Manager and Laravel 5.3 on Internet Information Services (IIS) Manager version 10 which ships with Windows 10 Pro. This Laravel Installation will also be configured to connect to a SQLite database.

Internet Information Services (IIS) Manager

IIS needs to be installed, open the Windows Features dialog to check the installation. One way to do this is by selecting the start button and type Windows Features to bring up a list where “Turn Windows features on or off” can be selected. Another way to get to this Control Panel app is Windows + R key combination and run appwiz.cpl. Turn Windows features on or off link should be in the upper left panel.

Windows Features - IIS and .NET Framework 3.5 enabled
Windows Features

Notice that under .NET Framework 3.5 that both Windows Communication Foundation features are enabled. This may be needed in order for a successful install of PHP Manager. Installation of PHP Manager for IIS requires .NET 3.5 to work properly.

PHP and PHP Manager

Install PHP 7 for Windows using the Microsoft Web Platform Installer. It’s an easy way to get both PHP and the PHP Manager installed and takes some of the guess work out of getting PHP up and running on Windows and IIS.

  • If PHP Manager Fails to install after confirming that .NET 3.5 is installed and enabled, this is likely due to the installer throwing an error when checking the IIS version. Change the W3SVC MajorVersion value to 7 (decimal) in the registry. After the install is completed, change the value back to 10. More info is available at the PHP Manager – Refuses to install for WTP10 view issue page.
Registry Editor - HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters]

* YMMV, I am not responsible if you hose your system when editing the registry.

PHP Extensions

In the Internet Information Services (IIS) Manager, Open PHP Manager and select the Enable or disable an extension link. Ensure that the extensions needed for Laravel are enabled. If you want to be able to connect to a SQLite database, enable the php_sqlite3.dll PHP extension. The figure below shows the PHP extensions I have enabled for a working Laravel 5 installation.

PHP Extensions provide additional functionality to PHP runtime, such as database connectivity, caching, debugging and others. Use this feature to enable or disable PHP extensions and to configure PHP extensions settings.
PHP Manager – PHP Extensions

Composer for Windows

Download and run Composer-Setup.exe Windows Installer. The installer will download Composer and set up the PATH environment variable.

Using the Windows Command Prompt, make sure Composer for Windows is installed by running the composer –version command.

composer --version

Git for Windows

Git for Windows is needed so Composer for Windows can download packages. During the installation process, Adjusting your PATH environment, make sure the option, Use Git from the Windows Command Prompt is enabled.

Using the Windows Command Prompt, make sure Git for Windows is installed by running the git –version command.

git --version

Create Laravel Project

Bring up the Adminstrator Command Prompt. One way to do this is by selecting the start button and type command to bring up a list where “Command Prompt” can be selected. Right click on Command Prompt and select Run as administrator
.

cd c:/intepub

composer create-project laravel/laravel laravel "5.3.*"

Add Website

Open Internet Information Services (IIS) Manager. Right click on the server and select Add Website. Fill out the form as follows:
Site name: Laravel
Application pool: DefaultAppPool
Physical path: C:\inetpub\laravel\public
Host name: laravel.win

Select “Test Settings” and then “OK” if successful.

Add Website
IIS – Add Website

Hosts Mapping

Since the Host name laravel.win was entered for the website, the hosts file needs to be updated. Open Notepad as an administrator. One way to do this is by selecting the start button and type Notepad to bring up a list where it can be selected. Right click on Notepad and select Run as administrator.

Select File | Open, or Ctrl + O and change the File type from Text Documents (*.txt) to All Files (*.*). Browse to C:\Windows\System32\drivers\etc and select the hosts file. Add an entry to map localhost to laravel.win as follows.

hosts
127.0.0.1   localhost
127.0.0.1   laravel.win

Laravel Storage Permissions

In File Explorer, right click on the storage folder in C:\inetpub\laravel and select Properties. Under the Security tab, grant full control of the storage folder to IUSR as shown in the figure below.

Properties Dialog - Security
Permissions for storage
Laravel web.config

Since IIS does not have an .htaccess file like Apache, create a web.config file in C:\inetpub\laravel\public as follows. *

web.config
<configuration>
    <system.webServer>
        <defaultDocument>
            <files>
                <clear />
                <add value="index.php" />
                <add value="default.aspx" />
                <add value="Default.htm" />
                <add value="Default.asp" />
                <add value="index.htm" />
                <add value="index.html" />
            </files>
        </defaultDocument>
        <rewrite>
            <rules>
                <rule name="Imported Rule 1" stopProcessing="true">
                    <match url="^(.*)/$" ignoreCase="false" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Redirect" redirectType="Permanent" url="/{R:1}" />
                </rule>
                <rule name="Imported Rule 2" stopProcessing="true">
                    <match url="^" ignoreCase="false" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="index.php" />
                </rule>
            </rules>
        </rewrite>
        <httpErrors errorMode="Detailed" />
    </system.webServer>
</configuration>

* The rewrite rule definitions in the web.config require the URL Rewite 2.0 extension. For easy installation, use the Free Web Platform Installer.

Laravel SQLite Database

Create a SQLite database * in C:\inetpub\laravel\database. Edit C:\inetpub\laravel\.env database values to configure the database connection.

.env
DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database/laravel.sqlite
DB_USERNAME=
DB_PASSWORD=

* DB Browser for SQLite is an open source, freeware tool used to create, design and edit SQLite database files.

Restart IIS

In an Administrative Command Prompt, restart IIS so all of the changes get applied.

iisreset /restart

In IIS, make sure that the Larvel web site has been started. If the W3SVC service is not running, it can be started with the following command.

net start w3svc

Once all is said and done, load http://laravel.win in a web browser. Laravel 5.3 default page should look similar to the screen shot below.

Laravel 5.3 Default Screen
Laravel 5.3

Installing Laravel on XAMPP

This post documents how to install Laravel locally using XAMPP. When you are developing with Laravel on an older computer or one with limited resources, using XAMPP for your local PHP and MySQL development server works well on Windows.

Laravel PHP Requirements

This table shows the minimum PHP version requirements for the latest versions of Laravel.

Laravel PHP
5.3 5.6.4
5.1, 5.2 5.5.9
5.0 * 5.4

* PHP 7 not supported in this version

Head over to the Apache Freinds site to download the version of XAMPP that meets the minimum PHP requirements for the version of Laravel you are supporting. If you are developing in Laravel 5.2 currently and also want to be able to support Laravel 5.3, get the XAMPP version that includes PHP 7.

For Windows, I have xampp installed at the root directory on C:\ drive. here is the virtual hosts configuration I am using for Laravel. More information for setting up XAMPP on Windows.

httpd-vhosts.conf
<VirtualHost *:8080>
    DocumentRoot "C:/xampp/htdocs/laravel/public"
    ServerName laravel.dev
    ServerAlias www.laravel.dev
    SetEnv APPLICATION_ENV development
    <Directory "C:/xampp/htdocs/laravel">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

Edit C:\Windows\System32\drivers\etc\hosts file, mapping laravel.dev hostname to 127.0.0.1 localhost IP address.

hosts
127.0.0.1   laravel.dev

Install Laravel

This example will install laravel 5.2 into C:/xampp/htdocs/laravel using Composer. Use the create-project command and the laravel/laravel package name followed by the directory to create the project in. The optional third argument is for a version number.

cd c:/xampp/htdocs

composer create-project laravel/laravel laravel "5.2.*"

Laravel User Registration with Email Activation

This post documents how to add an e-mail confirmation to the Laravel User registration that is generated by the Artisan console. In this example, the registered user will not be able to login until the activation link that is sent to the users registered e-mail account has been loaded into the browser.

Requirements

An instance of Laravel 5.2 setup for local development with authentication scaffolding is required. Refer to the first page of my recent post on Laravel User Authentication with Ajax Validation for more information on installing Laravel 5.2 using Composer and generating the authentication scaffolding with artisan.

User Model

I like placing model classes in their own directory. This can be done with just a few minor changes after moving the User class into a new app/Models folder.

mkdir app/Models

mv app/User.php app/Models

Edit app/Models/User.php, update the App namespace to App\Models.

User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    ...

  • Throughout this tutorial you will encounter an ellipsis … in the code examples. These are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. To view the entire file, checkout the email-activation branch in the source code.

Edit app/Http/Controllers/Auth/AuthController.php, update use App\User to use App\Models\User.

AuthController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Models\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

class AuthController extends Controller
{
    ...

Edit, config/auth.php, update the authentication drivers user provider.

'users' => [
    'driver' => 'eloquent',
    'model' => App\User::class,
],

REPLACE

'model' => App\User::class,

WITH

'model' => App\Models\User::class,

Database Migration Files

Generate a database migration file for creating the user_activations table. This table stores the activation codes until they are used.

create_user_activations_table
php artisan make:migration create_user_activations_table --create=user_activations

Edit the migration file up function to create these three fields in the user_activations table.

public function up()
{
    Schema::create('user_activations', function (Blueprint $table) {
        $table->integer('user_id')->unsigned();
        $table->string('token')->index();
        $table->timestamp('created_at');
    });
}
alter_users_table_add_activated_col

Generate a database migration file for adding a column to the users table.

php artisan make:migration alter_users_table_add_activated_col

Edit the migration file up function to add the activated field to the users table.

public function up()
{
    Schema::table('users', function($table) {
        $table->boolean('activated')->default(false);
    });
}

Run the migration

php artisan migrate

ActivationRepository Class

Create a Repositories folder in the app directory

mkdir app/Repositories

Create the ActivationRepository class file in app/Repositories. This class contains functions for generating activation tokens and updating the tables in the database.

ActivationRepository.php
<?php

namespace App\Repositories;

use Carbon\Carbon;
use Illuminate\Database\Connection;

class ActivationRepository
{
    protected $db;
    protected $table = 'user_activations';

    public function __construct(Connection $db)
    {
        $this->db = $db;
    }

    public function getActivation($user)
    {
        return $this->db->table($this->table)->where('user_id', $user->id)->first();
    }

    public function getActivationByToken($token)
    {
        return $this->db->table($this->table)->where('token', $token)->first();
    }

    public function deleteActivation($token)
    {
        $this->db->table($this->table)->where('token', $token)->delete();
    }

    public function createActivation($user)
    {
        $activation = $this->getActivation($user);

        if (!$activation) {
            return $this->createToken($user);
        }
        return $this->regenerateToken($user);
    }

    protected function getToken()
    {
        return hash_hmac('sha256', str_random(40), config('app.key'));
    }

    private function regenerateToken($user)
    {
        $token = $this->getToken();
        $this->db->table($this->table)->where('user_id', $user->id)->update([
            'token' => $token,
            'created_at' => new Carbon()
        ]);
        return $token;
    }

    private function createToken($user)
    {
        $token = $this->getToken();
        $this->db->table($this->table)->insert([
            'user_id' => $user->id,
            'token' => $token,
            'created_at' => new Carbon()
        ]);
        return $token;
    }
}

ActivationFactory Class

Create a Factories folder in the app directory

mkdir app/Factories

Create the ActivationFactory class file in app/Factories.

ActivationFactory.php
<?php

namespace App\Factories;

use App\Models\User;
use App\Repositories\ActivationRepository;
use Illuminate\Mail\Mailer;
use Illuminate\Mail\Message;

class ActivationFactory
{
    protected $activationRepo;
    protected $mailer;
    protected $resendAfter = 24;

    public function __construct(ActivationRepository $activationRepo, Mailer $mailer)
    {
        $this->activationRepo = $activationRepo;
        $this->mailer = $mailer;
    }

    public function sendActivationMail($user)
    {
        if ($user->activated || !$this->shouldSend($user)) {
            return;
        }

        $token = $this->activationRepo->createActivation($user);

        $link = route('user.activate', $token);
        $message = sprintf('Activate account %s', $link, $link);

        $this->mailer->raw($message, function (Message $m) use ($user) {
            $m->to($user->email)->subject('Activation mail');
        });
    }

    public function activateUser($token)
    {
        $activation = $this->activationRepo->getActivationByToken($token);

        if ($activation === null) {
            return null;
        }

        $user = User::find($activation->user_id);

        $user->activated = true;

        $user->save();

        $this->activationRepo->deleteActivation($token);

        return $user;
    }

    private function shouldSend($user)
    {
        $activation = $this->activationRepo->getActivation($user);
        return $activation === null || strtotime($activation->created_at) + 60 * 60 * $this->resendAfter < time();
    }
}

Route

Create a route for the e-mail activation link in app/Http/routes.php. This user.activate route should be added after Route::auth()

routes.php
<?php
...
Route::auth();

Route::get('user/activation/{token}', 'Auth\AuthController@activateUser')->name('user.activate');

AuthController

Make the following updates to app/Http/Controllers/Auth/AuthController.php

REPLACE

use App\Models\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

WITH

use App\Factories\ActivationFactory;
use App\Models\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;

REPLACE

public function __construct()
{
    $this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}

WITH

protected $activationFactory;

public function __construct(ActivationFactory $activationFactory)
{
    $this->middleware($this->guestMiddleware(), ['except' => 'logout']);
    $this->activationFactory = $activationFactory;
}

At the bottom of the controller, add these three functions after the create function. The register function we are adding will override the register method inherited from the AuthenticatesAndRegistersUsers trait.

...

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

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

    $user = $this->create($request->all());

    $this->activationFactory->sendActivationMail($user);

    return redirect('/login')->with('activationStatus', true);
}

public function activateUser($token)
{
    if ($user = $this->activationFactory->activateUser($token)) {
        auth()->login($user);
        return redirect($this->redirectPath());
    }
    abort(404);
}

public function authenticated(Request $request, $user)
{
    if (!$user->activated) {
        $this->activationFactory->sendActivationMail($user);
        auth()->logout();
        return back()->with('activationWarning', true);
    }
    return redirect()->intended($this->redirectPath());
}

Authentication Language Lines

For the activation status messages that we need to display to the user, update the resources/lang/en/auth.php file.

en/auth.php
<?php

return [

...

    'activationStatus' => 'We sent you an activation code. Please check your e-mail.',
    'activationWarning' => 'You need to confirm your account. We have sent you an activation code, please check your e-mail.',

...

];

Add the following inside the form at resources/views/auth/login.blade.php Blade view to render the activation messages.

login.blade.php
...

@if (session('activationStatus'))
    <div class="alert alert-success">
        {{ trans('auth.activationStatus') }}
    </div>
@endif

@if (session('activationWarning'))
    <div class="alert alert-warning">
        {{ trans('auth.activationWarning') }}
    </div>
@endif

Source Code

Laravel User Authentication with Ajax Validation

This post documents how to add Ajax form validation to Laravel User Authentication that is generated by the Artisan console.

Requirements

An instance of Laravel 5.2 setup for local development is required. Head over to the Laravel 5.2 docs to get setup as needed. For my setup on Windows 10, I am using XAMPP 5.6.12 which meets all of the requirements. Note that your document root will need to be the laravel/public directory. Here is the virtual hosts configuration I am using for Laravel.

httpd-vhosts.conf
<VirtualHost *:8080>
    DocumentRoot "C:/xampp/htdocs/laravel/public"
    ServerName laravel.dev
    ServerAlias www.laravel.dev
    SetEnv APPLICATION_ENV development
    <Directory "C:/xampp/htdocs/laravel">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

More info on setting up XAMPP for Windows

New Laravel Site

Using Composer, install Laravel 5.2 into htdocs/laravel

cd c:/xampp/htdocs

composer create-project laravel/laravel laravel "5.2.*"
Generate Authentication Scaffolding
cd laravel

php artisan make:auth

Now the Laravel site has routes and views to allow a user to register, login, logout, and reset their password. A HomeController has also been added to allow authenticated users to enter its view.

User Model

I prefer to organize models within their own directory. Therfore, let’s create a folder named Models in the app directory and move the User model into it.

mkdir app/Models

mv app/User.php app/Models

Edit app/Models/User.php, update the App namespace to App\Models.

User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    ...

  • Throughout this tutorial you will encounter an ellipsis … in the code examples. These are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. To view the entire file, examine the source code.

Edit app/Http/Controllers/Auth/AuthController.php, update use App\User to use App\Models\User.

AuthController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Models\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

class AuthController extends Controller
{
    ...

Edit, config/auth.php, update the authentication drivers user provider.

'users' => [
    'driver' => 'eloquent',
    'model' => App\User::class,
],

REPLACE

'model' => App\User::class,

WITH

'model' => App\Models\User::class,

Database

Edit the .env configuration file to access the database. I have already created a database named laravel on the MySQL Server that is part of XAMPP.

.env
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

The settings above are the only ones I needed to update for my setup, I do not have a password set for MySQL Server and will just use the root account for local development. Now run php artisan migrate to create the tables for users.

Run Migration
php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

Validation Rules

The validation rules are currently located in the AuthController, I would like to be able to share them with the new controller we will create later for ajax validation. Since these rules apply to the User, and that model is already being used by the AuthController, it seeems to be the best place for them.

Edit app/Models/User.php, adding the public function rules() to the class.

User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    /**
     * Get the validation rules that apply to the user.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|min:6|confirmed',
        ];
    }

    ...

Edit app/Http/Controllers/Auth/AuthController.php. Locate the validator function and replace the array of rules being passed to the Validator make method with a call to the newly created rules method in our User model.

REPLACE

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

WITH

return Validator::make($data, (new User)->rules());

Before going further, now would be a good time to test registering a new user, login, logout etc.. to make sure everyting works. All we have done so far aside from the code that artisan generated is move the User model and move the validation rules from the AuthController into it.

The next page covers using the api middleware in routes, creating the Validation\UserController for handling the XHR request and finally updating the templates and adding the JavaScript for Ajax.