Laravel Data Seeding

Creating a simple seeder

You’ll find your seeder files under database > seeders

Create a new one with this:

php artisan make:seeder UsersTableSeeder

Open new file and add to run function:

        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => Str::random(10).'@codingfaculty.com',
            'password' => bcrypt('secret')
        ]);

At the top of document add:

use DB;
use Str;

Now run:

php artisan db:seed

Creating a more advanced seeder with factories

Laravel Frontend

Adding Bootstrap plus Login & Registration

First require Laravel UI

composer require laravel/ui

Then you need to choose what front end to include: Bootstrap, Vue or React

php artisan ui bootstrap

For react you need first:

php artisan ui scaffolding

Do bootstrap for this version though.

Now it’ll ask you to run (You’ll need to run from Git Bash):

npm install && npm run dev
php artisan serve
php artisan serve

Create the webpack config file:

touch webpack.mix.js
npm install sass-loader@^12.1.0 resolve-url-loader@^5.0.0 --save-dev --legacy-peer-deps

Open the file and add:

let mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');

Now compile your css and js to the public folder:

npx mix

We can’t see bootstrap yet. We’ll need to add to welcome.blade.php

Add link:css and tab to the top of document and then href="{{asset('css/app.css')}}" which will link to the public folder automatically.

Now add this button anywhere to body:

<button class="btn btn-danger">Hi</button>

You should now see a red styled button which means bootstrap css is working.

Add login and registration page

php artisan ui bootstrap --auth

Then after run:

npm install && npm run dev

Now if you go to /login you’ll see a login page.

How to add templates to Laravel projects

Move js, css and vendor folder into the public folder in Laravel project.

Then copy the contents blank.html and put into layouts/admin.blade.php. Then update the <h1> tag below <!– Page Heading –> with @yield('content')

Create another template under admin/index.blade.php and paste this into it:

@extends('layouts.admin')

@section('content')

<h1>Admin</h1>

@endsection

Create a route:

Route::get('/admin', function () {
    return view('admin.index');
});

Check /admin to see if you see your Admin template.

Now update your admin.blade.php with dynamic asset links: {{asset('vendor/fontawesome-free/css/all.min.css')}}

Create admin/partials/_navbar.blade.php. Then cut out sidebar from admin.blade.php and paste into this file.

Go back to admin.blade.php and in its place paste:

@include('admin.partials._navbar')

Refresh to view

This is how to take a template and break it out into parts in Laravel.

Sending Email / Api

Sign up for Mailgun

Then find your API keys

Updating env file

Open config/mail.php to configure beginning of email:

MAIL_HOST should be smtp.mailgun.org

Update from address to something like:

    'from' => [
        'address' => env('MAIL_FROM_ADDRESS', 'myemail@domain.com'),
        'name' => env('MAIL_FROM_NAME', 'Christine'),
    ],

Open up config/services.php to find mailgun variables needed to set up env file. MAILGUN_DOMAIN, MAILGUN_SECRET

Open env file and delete all mail info and add these lines:

MAIL_DRIVER=mailgun
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@blah.mailgun.org
MAIL_PASSWORD=passwordhere
MAIL_ENCRYPTION=tls
MAILGUN_DOMAIN=sandboxa63bbae0e8f448b7a7943f800a2a166a.mailgun.org
MAILGUN_SECRET=281329ff11f62a513a665d35f7763aea-4dd50799-322083f3
Get the password and username from clicking SMTP option inside Mailgun. 

Create a new view /emails/test.blade.php. Then add to route:

Route::get('/', function () {
    // return view('welcome');
    $data = [
        'title' => 'This page will send an email',
        'content' => 'It\'s going to happen, just wait!',
    ];
    Mail::send('emails.test', $data, function($message){
        $message->to('emailhere', 'Christine')->subject('This is the subject');
    });
});

Add this to your test.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>{{$title}}</h1>
    <h1>{{$content}}</h1>
</body>
</html>

Then add a requirement:

composer require guzzlehttp/guzzle
Any changes made to email do this: php artisan config:clear

You need to go into vender > guzzlehttp > guzzle > src > Client.php and find verify.. change it to false.. this will allow without ssl enabled in dev.

Now you’ll be able to visit the homepage and you’ll get sent an email!

If you go back to Mailgun you can go to Sending > Logs to see the emails went out.

Laravel Sessions

Setting and reading sessions

Create a HomeController:

php artisan make:controller --resource HomeController

Your route /home will point to the HomeController:

Route::get('/home',[HomeController::class, 'index']);

Now add a session to index:

    public function __construct(){
        $this->middleware('auth');
    }
    public function index(Request $request)
    {
        $request->session()->put(['christine'=>'hello christine']);
        session(['peter'=>'hello peter']);
        echo $request->session()->get('christine');
        //return view('welcome');
    }

Above are two ways of saving sessions. We are only returning the first one when visiting the /home URL.

Global session function deleting

Add a method to forget the session:

    public function index(Request $request)
    {
        $request->session()->put(['christine'=>'hello christine']);
        session(['peter'=>'hello peter']);
        $request->session()->forget('christine');
        return $request->session()->all();
        //return view('welcome');
    }

To delete all sessions use this:

        $request->session()->flush();

Flashing data

This type of session will show user the session once (flash it to them) and then forget it.

        $request->session()->flash('message','Post has been created');
        return $request->session()->get('message');

Two more types are reflash and keep:

        $request->session()->reflash();
        $request->session()->keep('message');

Middleware Security/Protection

Open up app/Http/Kernel.php and find protected $routeMiddleware

Underneath this you’ll see the different shorthands of calling the classes like:

'auth' => \App\Http\Middleware\Authenticate::class,

Which you can find under app/Http/Middleware/Authenticate.php

Now we can create our own middleware with this command:

php artisan make:middleware RoleMiddleware

Open up app/Http/Middleware/RoleMiddleware.php

Registering a new middleware and using it

To put in maintenance mode type:

php artisan down

Then put back up

php artisan up

Now register the RoleMiddleware in Kernel.php.. add to end of this:

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'role' => \App\Http\Middleware\RoleMiddleware::class,
    ];

Create a route for the middleware that’s basic like:

Route::get('/admin/user/roles', ['middleware'=>'role', function () {
    return "Middleware role";
}]);

Now update RoleMiddleware to handle this request to the middleware:

    public function handle(Request $request, Closure $next)
    {
        return redirect('/');
        //return $next($request);
    }

Now going to the /admin/user/roles URL will simply redirect the user the homepage.

Middleware – roles, migration and relations setup

Create a Role model to store the role of user:

php artisan make:model Role -m

Open up user migration and add this:

$table->integer('role_id');

In new role migration add:

$table->string('name');

Then

php artisan migrate:refresh

Open Role model:

    protected $fillable = [
        'name',
    ];

Open User model:

    public function role(){
        return $this->belongsTo('App\Models\Role');
    }

Custom Method

Method 1

Create another middleware:

php artisan make:middleware IsAdmin

Now open up Kernel to add the middleware:

'isAdmin' => \App\Http\Middleware\IsAdmin::class,

Now we’ll need to create roles in our database to use with this case. Add admin and subscriber. Then register a user on the web part at /register.

Create a relationship in User model called isAdmin

    public function isAdmin(){
        if($this->role->name == 'admin' ){
            return true;
        }
        return false;
    }

Now add a route to check whether your current logged in user is administrator (check database to see if it is first!)

Route::get('/', function () {
    $user = Auth::user();
    if($user->isAdmin()){
        echo 'this user is an administrator';
    }
    //return view('welcome');
});

Now change the user role to see if it works when not an administrator as well.

Method 2

Return homepage route back to original:

Route::get('/', function () {
    return view('welcome');
});

Add a redirect instead to the middleware when user role is administrator for IsAdmin:

use Illuminate\Support\Facades\Auth;
    public function handle(Request $request, Closure $next)
    {
        $user = Auth::user();
        if(!$user->isAdmin()){
            return redirect('/');
        }
        return $next($request);
    }

Next create a route that calls a new Controller:

Route::get('/admin', 'AdminController@index');

Create controller

php artisan make:controller AdminController

Add to AdminController:

    public function __construct(){
        $this->middleware('isAdmin');
    }
    public function index(){
        return "you are an administrator because you are seeing this page";
    }

Now if you are an admin and visit /admin you’ll see the above message. If you aren’t, you’ll be redirected to the homepage.

Forms Login

Creating the login system

In Laravel 8 things have changed for the authentication system. 

First you need use your composer :

composer require laravel/jetstream

Then you have to create a new project:

laravel new login --jet

Which jetstream package do you prefer 0 livewire 1 inertia. Choose livewire here. Inertia when you want to use React.

Prepare database in phpmyadmin and make migrate:

php artisan migrate

Now, you can go to the registration page:

http://127.0.0.1:8000/register

Register a user and also try logging out and in again.

Login creation overview

Check the default route list it created:

php artisan route:list

Open up your controller folder to view all the files created. Then check your database to see the user created and the remember me token created for them.

Retrieving authenticated user data

Update routes to see new message on homepage:

Route::get('/', function () {
    if(Auth::check()){
        return 'the user is logged in';
    }
    //return view('welcome');
});

And this is attempting to go back to page where you tried to access but redirects to login page:

Route::get('/', function () {
    // if(Auth::check()){
    //     return 'the user is logged in';
    // }
    //return view('welcome');

    if(Auth::attempt(['username'=>$username,'password'=>$password])){
        return redirect()->intended();
    }
});

Forms Uploading Files

Modifying our create view for file input

{!! Form::open(['method'=>'POST','route'=>'posts.store','files'=>true]) !!}
    <div class="form-group">
        {!! Form::label('title','Title:') !!}
        {!! Form::text('title',null,['class'=>'form-control']) !!}
    </div>
    <div class="form-group">
        {!! Form::file('file',['class'=>'form-control']) !!}
    </div>
    <div class="form-group">
        {!! Form::submit('Create Post',['class'=>'btn btn-primary']) !!}
    </div>
{!! Form::close() !!}

You are adding a files = true to the form tag and then adding a file field.

Retrieving File Data

Update store function to just return the file name and comment out validation from CreatePostRequest

    public function store(CreatePostRequest $request)
    {
        $file = $request->file('file');
        echo $file;
        echo '<br>';
        echo $file->getClientOriginalName();

        echo '<br>';
        echo $file->getClientSize();
    }

The first shows the temporary file name and the second shows the file name from your computer.

Persisting File Data into the Database

Create a migration to store the file info to database:

php artisan make:migration add_path_column_to_posts --table=posts

Open up and add these to up and down functions:

$table->string('path');
$table->dropColumn('path');

Then migrate:

php artisan migrate

Update your Post model to allow path column for mass migration:

    protected $fillable = [
        'title',
        'content',
        'user_id',
        'path'
    ];

Now edit your store function in PostsController:

    public function store(CreatePostRequest $request)
    {
        $input = $request->all();
        if($file = $request->file('file')){
            $name = $file->getClientOriginalName();
            $file->move('images',$name);
            $input['path'] = $name;
        }
        Post::create($input);
    }

Also if you check your public folder, you’ll see an images folder has been created with the images name.

Displaying images and using accessors to make it easy

Update your views post index page:

<ul>
    @foreach($posts as $post)
        <div class="image-container">
            <img height="100" src="/images/{{$post->path}}" alt="" />
        </div>
        <li><a href="{{route('posts.show',$post->id)}}">{{$post->title}}</a></li>
    @endforeach
</ul>

Let’s create a simple accessor that puts the directory of the images in the Post model. In Post model:

    public $directory = "/images/";public $directory = "/images/";
    public function getPathAttribute($value){
        return $this->directory.$value;
    }

Now change index.blade.php to:

<ul>
    @foreach($posts as $post)
        <div class="image-container">
            <img height="100" src="{{$post->path}}" alt="" />
        </div>
        <li><a href="{{route('posts.show',$post->id)}}">{{$post->title}}</a></li>
    @endforeach
</ul>

Database Some More Model Manipulation

Laravel includes Carbon. But to add to another project using composer, do composer require nesbot/carbon.

Dates

use Carbon\Carbon;
Route::get('/dates', function(){
    $date = new DateTime('+1 week');
    echo $date->format('m-d-Y');
    echo '<br>';
    echo Carbon::now()->addDays(10)->diffForHumans();
    echo '<br>';
    echo Carbon::now()->subMonths(5)->diffForHumans();
    echo '<br>';
    echo Carbon::now()->yesterday();
});

Accessors

Route::get('/getname', function(){
    $user = User::find(1);
    echo $user->name;
});

Update your User model to manipulate the output anytime the user’s name is used. Format here is important, needs to start with get, then the attribute name and then the word attribute.

    public function getNameAttribute($value) {
        return strtoupper($value);
    }

Mutators

Mutators will manipulate the data before its sent to the database.

    public function setNameAttribute($value) {
        $this->attributes['name'] = strtolower($value);
    }

Add to route:

Route::get('/setname', function(){
    $user = User::find(1);
    $user->name = 'William';
    $user->save();
});

Now if you check the database after visiting setname, you should see william is in lowercase.

Query Scope

The usual way to query is to do something like this (open PostController):

    public function index()
    {
        $posts = Post::orderBy('id','desc')->get();
        return view('posts.index', compact('posts'));
    }

Instead we are going to make orderBy shorter by adding a function to our Post model for queries.

    public static function scopeMeek($query) {
        return $query->orderBy('id','desc')->get();
    }

Update PostController:

    public function index()
    {
        $posts = Post::meek();
        return view('posts.index', compact('posts'));
    }

Laravel Forms Package and Validation

Installing the package

You need to get the “laravel illuminate/html collective” which you can google.

First we’ll add it manually. Go here:

https://laravelcollective.com/docs/6.x/html

Edit your project’s composer.json file to require laravelcollective/html. Find your require block and add:

"laravelcollective/html": "6.*"

In terminal:

composer update

Now you need to add to any Model that’s going to use it. In this case add to the Post model:

use Collective\Html\Eloquent\FormAccessible;

Modifying the create form

@extends('layouts.app')

@section('content')
<h1>Create Post</h1>
{!! Form::open() !!}
    <input type="text" name="title" placeholder="Enter title" />
    <input type="submit" name="submit" />
{!! Form::close() !!}

@endsection
Remove the token as it'll create it for you. If you inspect your page form, you should see the token is added automatically. However if you submit the form you'll get an error. Its sending to the wrong URL.

Okay make these edits:

@extends('layouts.app')

@section('content')
<h1>Create Post</h1>
{!! Form::open(['method'=>'POST','route'=>'posts.store']) !!}
    <input type="text" name="title" placeholder="Enter title" />
    <input type="submit" name="submit" />
{!! Form::close() !!}

@endsection

Next add fields using the package like this:

@extends('layouts.app')

@section('content')
<h1>Create Post</h1>
{!! Form::open(['method'=>'POST','route'=>'posts.store']) !!}
    <div class="form-group">
        {!! Form::label('title','Title:') !!}
        {!! Form::text('title',null,['class'=>'form-control']) !!}
    </div>
    <div class="form-group">
        {!! Form::submit('Create Post',['class'=>'btn btn-primary']) !!}
    </div>
{!! Form::close() !!}
@endsection

Modifying the edit/delete form

@extends('layouts.app')

@section('content')
<h1>Edit Post</h1>
{!! Form::model($post,['method'=>'PATCH','route'=>['posts.update',$post->id]]) !!}
    {!! Form::label('title','Title:') !!}
    {!! Form::text('title',null,['class'=>'form-control']) !!}
    
    {!! Form::submit('Update Post',['class'=>'btn btn-info']) !!}
{!! Form::close() !!}

<!-- <form method="post" action="/posts/{{$post->id}}"> -->
{!! Form::open(['method'=>'DELETE','route'=>['posts.destroy',$post->id]]) !!}
    {!! Form::submit('Delete Post',['class'=>'btn btn-danger']) !!}
{!! Form::close() !!}

@endsection

Basic Validation

Update the store function in PostController like this:

    public function store(Request $request)
    {
        $this->validate($request,[
            'title'=>'required|max:4'
        ]);
        $post = new Post;
        $post->title = $request->title;
        $post->save();
        return redirect('/posts');
    }

Now the form won’t enter without a value.

Displaying Errors

The validate function will create an errors variable when it fails which can be used in the views. So in our create.blade.php add this part:

@extends('layouts.app')

@section('content')
<h1>Create Post</h1>
{!! Form::open(['method'=>'POST','route'=>'posts.store']) !!}
    <div class="form-group">
        {!! Form::label('title','Title:') !!}
        {!! Form::text('title',null,['class'=>'form-control']) !!}
    </div>
    <div class="form-group">
        {!! Form::submit('Create Post',['class'=>'btn btn-primary']) !!}
    </div>
{!! Form::close() !!}

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

You should see errors added to bottom of form now.

Advance validation

Instead of creating the validation inside the store function, we are going to create a validation class that we’ll call inside the store function.

To see all your functions available type:

php artisan

You’ll see make:request Create a new form request class which is what we want to do. So next type this command:

php artisan make:request CreatePostRequest

You’ll find this file under App\Http\Requests. Open it and change authorize function to return true since we aren’t creating something that needs it.

Update rules function like so:

    public function rules()
    {
        return [
            'title'=>'required|max:4',
        ];
    }

That’s it! Very simple. Now we need to use it in our PostsController.

use App\Http\Requests\CreatePostRequest;
    public function store(CreatePostRequest $request)
    {
        $post = new Post;
        $post->title = $request->title;
        $post->save();
        return redirect('/posts');
    }

Laravel Forms and Validation

Create 4 views under a new folder called “posts”

create.blade.php
edit.blade.php
index.blade.php
show.blade.php

To our create.blade.php:

@extends('layouts.app')

@section('content')
<h1>Create Post</h1>
<form method="post" action="/posts">
    @csrf <!-- {{ csrf_field() }} -->
    <input type="text" name="title" placeholder="Enter title" />
    <input type="submit" name="submit" />
</form>

@endsection
You need to match the name of the column in posts.. is it named title? If so then the input name is correct. To save this to the posts method you just need to use the action /posts. @csrf is needed to create a token so form will work.

Find the posts routes methods by typing this:

php artisan route:list

You’ll see that the name store deals with the method POST. So find the store method in your posts controller.. this will be the function that will deal with the data when the form is submitted.

Now to see your form find the create (GET) function and add to this function:

    public function create()
    {
        return view('posts.create');
    }

The dot notation gets converted to slash in the browser.. where you can view it at base.com/posts/create.

Now get the store function to show the data submitted from the form:

    public function store(Request $request)
    {
        //return $request->all();
        //return $request->get('title');
        return $request->title;
    }

Update this to save to the database in different ways and redirect to index page like this:

    public function store(Request $request)
    {
        Post::create($request->all());
        return redirect('/posts');
    }
    public function store(Request $request)
    {
        $input = $request->all();
        $input['title'] = 'yo';
        Post::create($input);
        return redirect('/posts');
    }
    public function store(Request $request)
    {
        $post = new Post;
        $post->title = $request->title;
        $post->save();
        return redirect('/posts');
    }

Index controller should now show the index view and pass the post info:

    public function index()
    {
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }

In your index view update to show all the posts information:

@extends('layouts.app')

@section('content')

<ul>
    @foreach($posts as $post)
        <li>{{$post->title}}</li>
    @endforeach
</ul>

@endsection

Now if you submit the form you’ll be redirected to the index view which will show all the posts in the database including the last one added.

To see just one post, let’s add this to our show function in PostController:

    public function show($id)
    {
        $post = Post::findOrFail($id);
        return view('posts.show',compact('post'));
    }

In your show view write this:

@extends('layouts.app')

@section('content')

<h1>{{$post->title}}</h1>

@endsection

Now if you visit url /posts/1, you’ll see the first post. Other numbers will show other posts. It will have to match the id in the database of course.

Update your index view to link to the show view like so:

@extends('layouts.app')

@section('content')

<ul>
    @foreach($posts as $post)
        <li><a href="{{route('posts.show',$post->id)}}">{{$post->title}}</a></li>
    @endforeach
</ul>

@endsection

Now let’s create our Edit controller and view.

The view:

@extends('layouts.app')

@section('content')
<h1>Edit Post</h1>
<form method="post" action="/posts/{{$post->id}}">
    <input type="hidden" name="_method" value="PUT" />
    @csrf <!-- {{ csrf_field() }} -->
    <input type="text" name="title" placeholder="Enter title" value="{{$post->title}}" />
    <input type="submit" name="submit" />
</form>

@endsection
The action needs to send the post ID and a hidden field is needed called PUT which sends it to the update function in the controller.

The controller:

    public function edit($id)
    {
        $post = Post::findOrFail($id);
        return view('posts.edit',compact('post'));
    }

Now edit the update function in the controller:

    public function update(Request $request, $id)
    {
        $post = Post::findOrFail($id);
        $post->update($request->all());
        return redirect('/posts');
    }

Now update the show view to have links to edit form:

@extends('layouts.app')

@section('content')

<h1><a href="{{route('posts.edit',$post->id)}}">{{$post->title}}</a></h1>

@endsection

Lastly we are going to use a form to delete some of this data.

Find the destroy function in PostContoller and add this:

    public function destroy($id)
    {
        $post = Post::whereId($id)->delete();
        return redirect('/posts');
    }

Since everything already links nicely.. just edit you edit view like this:

@extends('layouts.app')

@section('content')
<h1>Edit Post</h1>
<form method="post" action="/posts/{{$post->id}}">
    <input type="hidden" name="_method" value="PUT" />
    @csrf <!-- {{ csrf_field() }} -->
    <input type="text" name="title" placeholder="Enter title" value="{{$post->title}}" />
    <input type="submit" name="submit" value="UPDATE" />
</form>

<form method="post" action="/posts/{{$post->id}}">
    <input type="hidden" name="_method" value="DELETE" />
    @csrf <!-- {{ csrf_field() }} -->
    <input type="submit" name="submit" value="DELETE" />
</form>

@endsection

Notice that your method is now DELETE and we still pass the post ID through the form action tag.