Lets Do This! Part 1

And in the beginning, lagbox created a laravel app.

Posted by lagbox on April 20, 2016 laravel tutorial

We are going to make a quick laravel application, including the setup and install.

We will setup the project using the following:

  • Nginx
  • sqlite3
  • and we are on a *nix system (Linux, Mac, Unix, Freebsd, etc …)

We will end up using 1 extra composer package:

  • Laravel Collective HTML & Form helper (laravelcollective/html)

Git hub repository for Part 1

Install and Configuration

⚡ composer create-project --prefer-dist laravel/laravel letsdothis

...
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postInstall
> php artisan optimize
Generating optimized class loader
> php artisan key:generate
Application key [base64:...this will be a generated key...] set successfully.

Hosts

sudo vim /etc/hosts

...
127.0.0.1   letsdothis.local

Nginx config

Lets create a Nginx config for our site. It is important to note that the way Laravel wants to be setup, the document root or webroot for your site should point to the public folder of the project. Laravel uses a front loader where all requests come through index.php which is in the public folder.

Side Note: The way the webserver is handling these requests, as in the case for how apache would be setup as well, is that any requests that match any files or folders that are accessible in public will get served by the webserver. Requests that don’t match will be passed to index.php. Be aware of this as you name routes and files and folders you place in public.

⚡ cd /etc/nginx/sites-available
⚡ sudo vim letsdothis.local


server {
    listen 127.0.0.1:80;

    root /var/www/sites/letsdothis/public;

    index index.php index.html index.htm;

    server_name letsdothis.local;

    # we will put the logs in laravel's storage folder
    access_log /var/www/sites/letsdothis/storage/logs/access.log;
    error_log /var/www/sites/letsdothis/storage/logs/error.log;

    charset utf-8;

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

    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt { log_not_found off; access_log off; }

    error_page 404 /index.php;

    include fastcgi.conf;

    location ~ /\.ht {
        deny all;
    }
}

Enable this site by creating a symlink in sites-enabled to the config we just created.

⚡ cd ../sites-enabled/
⚡ sudo ln -s ../sites-available/letsdothis.local .

Lets reload nginx.

⚡ sudo service nginx reload

Permissions

This is just one quick way of doing this. You can do permissions on the way you prefer and should look into how to do permissions correctly. Im just going to use group write permissions for www-data.

⚡ cd /var/www/sites/letsdothis
⚡ chgrp www-data -R bootstrap/cache storage
⚡ chmod g+w -R bootstrap/cache storage

At this point you can hit your site from your browser at http://letsdothis.local. You will see the Laravel landing page, at the time of writing it would have said “Laravel 5” centered on the page.

Setup the Database

We are going to keep this simple and quick and use a sqlite database. We will create it in storage.

⚡ sqlite3 database.sqlite
SQLite version 3.8.11.1 2015-07-29 20:00:57
Enter ".help" for usage hints.
sqlite> .databases
seq  name             file
---  ---------------  -----------------------------------------------------
0    main             /var/www/sites/letsdothis/storage/database.sqlite
sqlite> .q

Now that we have a sqlite database lets set the write permissions needed on it. (As before you should look into how permissions work on the system you are on and decide the best method needed.)

⚡ chgrp www-data database.sqlite
⚡ chmod g+w database.sqlite

Lets configure the application

Database configuration

We will make two adjustment in the .env file that has been created for us, to set the default database connection to use our sqlite database.

...
DB_CONNECTION=sqlite
...
#DB_DATABASE=homestead
...

These env values will get read by the configuration files in the config directory. The env variables, DB_CONNECTION and DB_DATABASE, are used by database.php as shown below.

'default' => env('DB_CONNECTION', 'mysql')
...
'sqlite' => [
	'driver' => 'sqlite',
	'database' => env('DB_DATABASE', storage_path('database.sqlite')),
	'prefix' => '',
],

We adjusted the database_path('database.sqlite') to storage_path('database.sqlite') this will resolve to the path where we placed our sqlite database.

Lets get on with it

Now that We have setup and configured our application we can move onto the fun stuff. There are numerous generators that are provided by Laravel via the artisan helper that you can use on the command line. (You should get to know artisan and its many commands)

Migrations

In the spirit of keeping this simple lets just create a table that can store attributes and values. This will be a simple CRUD interface to create.

⚡ php artisan make:migration create_data_table_migration
Created Migration: 2016_04_09_053441_create_data_table_migration

Lets edit that file, database/migrations/2016_04_09_053441_create_data_table_migration.php to create a table with the fields that we will need using the Schema Builder and Blueprint.

public function up()
{
    Schema::create('data', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->text('value');
        $table->timestamps();
    });
}

public function down()
{
    Schema::drop('data');
}

We use the down() method that would be called when we rollback, refresh, or reset the migrations, to make sure any changes this migration makes can be reversed, in the context of the schema.

To follow the naming convention that Eloquent will use by default you would name your tables in the plural form and your model in the singular form. In this case, for English, that would be Datum for the model and data for the table. We are just using the plural form for the model name as Data. Since this is already the plural technically the table is also data. If you were strictly sticking to convention it would be Datum and data, but everyone knows the word data and might not know datum. So you can see why I didn't use that.

We can run our migrations to setup our database tables.

⚡ artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_04_09_053441_create_data_table_migration

Note: Laravel comes with two migrations, one for creating the users table and the other for creating the password_resets table.

Lets add a user

Another lovely artisan command is tinker, an interactive shell for your Laravel application. We are going to create our user real quick using this cli tool.

⚡ php artisan tinker
Psy Shell v0.7.2 (PHP 7.0.5-2+deb.sury.org~wily+1 — cli) by Justin Hileman
>>> $credentials = [
... 'name' => 'admin',
... 'email' => 'admin@admin',
... 'password' => bcrypt('test')
... ];
=> [
     "name" => "admin",
     "email" => "admin@admin",
     "password" => "$2y$10$uw1GOP/OwmxPxZGP.E4KOOfvXZGHld/s.V7Cr5N1IAw7j6Fpu.zNu",
   ]
>>> $user = App\User::create($credentials);
=> App\User {#639
     name: "admin",
     email: "admin@admin",
     updated_at: "2016-04-09 05:47:35",
     created_at: "2016-04-09 05:47:35",
     id: 1,
   }

As simple as that, we have a user in our database.

Authentication

At this point we really haven’t done any coding and we have come a long way. Lets add the authentication scaffolding to our application. Once again the artisan cli can be used again.

⚡ artisan make:auth
Created View: /var/www/html/sites/letsdothis/resources/views/auth/login.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/auth/register.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/auth/passwords/email.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/auth/passwords/reset.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/auth/emails/password.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/layouts/app.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/home.blade.php
Created View: /var/www/html/sites/letsdothis/resources/views/welcome.blade.php
Installed HomeController.
Updated Routes File.
Authentication scaffolding generated successfully!

This helpful little command has given us a head start on a simple frame for our application. It has provided us with view templates, a HomeController and updated our routes file for us.

If you refresh your site you will see that you have a new layout and some links. Click on the Login link in the header and enter the credentials we used to create our user and click the login button.

Still without much code written at all, we have a basic site with authentication up and running.

Models

To use our data table we will create an Eloquent Model for our table. Guess what we will use to do this? Thats right, artisan cli.

⚡ php artisan make:model Data
Model created successfully.

We will set the fillable attributes for this model. (app\Data.php)

namespace App;

use Illuminate\Database\Eloquent\Model;

class Data extends Model
{
    protected $fillable = ['name', 'value'];
}

Since Data is the plural form already, when eloquent gets the plural form of data it will come back as data. We don't have to define our table as it will assume data. Check the note in the Migrations section.

Controller

Now some code, but first … artisan cli :).

⚡ php artisan make:controller --resource DataController
Controller created successfully.

This will create a RESTful Resource Controller for us in app/Http/Controllers. Lets open this up make some adjustments and add some functionality.

namespace App\Http\Controllers;

use App\Data;
use App\Http\Requests;
use Illuminate\Http\Request;

class DataController extends Controller
{
    protected $rules = [
        'name' => 'required',
        'value' => 'required'
    ];

    public function __construct()
    {
        $this->middleware('auth', ['except' => ['index', 'show']]);
    }

    public function index()
    {
        $data = Data::paginate(20);

        return view('data.index', compact('data'));
    }

    public function create()
    {
        $data = new Data;
        $action = 'Create';
        return view('data.edit', compact('action', 'data'));
    }

    public function store(Request $request)
    {
        $this->validate($request, $this->rules);

        Data::create($request->only(['name', 'value']));

        return redirect()->route('data.index')->with('msg', 'Data was created.');
    }

    public function show(Data $data)
    {
        return view('data.show', compact('data'));
    }

    public function edit(Data $data)
    {
        $action = 'Update';
        return view('data.edit', compact('data', 'action'));
    }

    public function update(Request $request, Data $data)
    {
        $this->validate($request, $this->rules);

        $data->update($request->only(['name', 'value']));

        return redirect()->route('data.index')->with('msg', 'Data was updated.');
    }

    public function destroy(Data $data)
    {
        $data->delete();

        return redirect()->route('data.index')->with('msg', 'Data was deleted.');
    }
}

These are the CRUD methods and the functionality involved with these operations. This is a super simplified example that includes validation, dependency injection, model bindings and flash data.

Routes

Adding the routes for this controller is very simple with Laravel. We will open the routes.php file in app\Http and add the following:

Route::resource('data', 'DataController');

Thats it. We now have 7 routes for us that correspond to the 7 methods of our DataController.

Note: At any time you can check to see how your routes are registered with the application via the artisan cli command route:list.

Several of these routes take parameters which will be passed to our controller methods. These parameter names correspond to the name of the parameter we used in those method signatures. Because we type-hinted the $data variables as Data the router will actually try to find a Data model by id that corresponds to the parameter that is passed via the URL, and pass that to our Controller method instead of the actual passed parameter from the URL.

Views

Almost done, believe it or not.

We have the model, we have the controller, we just dont have any views for our CRUD for data. We are going to need a index, show and edit/new view.

Create a folder in resources/views named data and create 3 files, index.blade.php, show.blade.php, edit.blade.php.

We are going to quickly install the Laravel Collective’s HTML & Form package to help with our forms.

⚡ composer require laravelcollective/html

When this finishes downloading we will add the Service Provider and alias the Facades.

Open up config/app.php and add the following to the $providers array:

Collective\Html\HtmlServiceProvider::class,

and add the following to the $aliases array:

'Form' => Collective\Html\FormFacade::class,
'Html' => Collective\Html\HtmlFacade::class,

That package is now installed and ready to use.

index.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">
                        Data
                </div>

                <div class="panel-body">

                    @if (Session::has('msg'))
                        <div class="alert alert-info">
                            <p>
                                {{ Session::get('msg') }}
                            </p>
                        </div>
                    @endif


                    @if (Auth::check())
                    <span class="pull-right">
                        <a href="{{ route('data.create') }}" class="btn btn-success">Add New Data</a>
                    </span>
                    @endif

                    <table class="table table-striped">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Name</th>
                                <th>Value</th>
                                <th>Created At</th>
                                @if (Auth::check())
                                <th>Actions</th>
                                @endif
                            </tr>
                        </thead>
                        <tbody>
                            @forelse($data as $item)
                            <tr>
                                <th scope="row"><a href="{{ route('data.show', [$item]) }}">{{ $item->id }}</a></th>
                                <td><a href="{{ route('data.show', [$item]) }}">{{ $item->name }}</a></td>
                                <td>{{ $item->value }}</td>
                                <td>{{ $item->created_at->diffForHumans() }}</td>
                                @if (Auth::check())
                                <td>
                                    <a href="{{ route('data.edit', [$item->id]) }}" class="btn btn-primary">Edit</a>
                                    <form method="POST" action="{{ route('data.destroy', [$item->id]) }}" style="display:inline">
                                        <input type="hidden" name="_method" value="DELETE">
                                        {{ csrf_field() }}
                                        <input type="submit" value="Delete" class="btn btn-danger">
                                    </form>
                                </td>
                                @endif
                                <td></td>
                            </tr>
                            @empty
                            <tr>
                                <td colspan="{{ 4 + Auth::check() }}" align="center">No Data to display.</td>
                            </tr>
                            @endforelse
                        </tbody>
                    </table>

                    {{ $data->render() }}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

edit.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">{{ $action }} Data</div>

                <div class="panel-body">
                    @if (count($errors) > 0)
                        <div class="alert alert-danger">
                            <ul>
                                @foreach ($errors->all() as $error)
                                    <li>{{ $error }}</li>
                                @endforeach
                            </ul>
                        </div>
                    @endif

                    {{ Form::model($data, [
                        'route' => $data->exists ? ['data.update', $data] : 'data.store',
                        'class' => 'form',
                        'role' => 'form',
                        'method' => $data->exists ? 'PUT' : 'POST'
                    ]) }}
                    <div class="form-group {{ $errors->has('name') ? 'has-error' : '' }}">
                        <label for="name">Name</label>
                        {{ Form::text('name', null, [
                                'class' => 'form-control',
                                'placeholder' => 'Name'
                        ]) }}
                    </div>
                    <div class="form-group {{ $errors->has('value') ? 'has-error' : '' }}">
                        <label for="value">Value</label>
                        {{ Form::textarea('value', null, [
                                'class' => 'form-control',
                                'placeholder' => 'Value'
                        ]) }}
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-primary">Submit</button>
                    </div>

                    {{ Form::close() }}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

show.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">
                    Data
                </div>
                <div class="panel-body">
                    <div>
                        <strong>Name:</strong>
                        <p>
                            {{ $data->name }}
                        </p>
                    </div>
                    <div>
                        <strong>Value:</strong>
                        <p>
                            {{ $data->value }}
                        </p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Adjust the layout to add our link to index

Open up resources/views/layouts/app.blade.php and find the left side of the navbar section and replace it as so:

<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
    <li><a href="{{ url('/home') }}">Home</a></li>
    <li><a href="{{ route('data.index') }}">Data</a></li>
</ul>

Finish

You have now finished your basic run through of simple CRUD with authentication on Laravel 5.2.

Link to Part 2