Laravel Components

Part 1

Create a bootstrap project:

Create a new view under new components folder called master.blade.php. This HTML template taken from Bootstrap:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="https://getbootstrap.com/docs/4.0/assets/img/favicons/favicon.ico">

    <title>Blog Template for Bootstrap</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.0/examples/blog/">

    <!-- Bootstrap core CSS -->
    <link href="https://getbootstrap.com/docs/4.0/dist/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="https://fonts.googleapis.com/css?family=Playfair+Display:700,900" rel="stylesheet">
    <link href="{{asset('css/app.css')}}" rel="stylesheet">
    <link href="{{asset('css/blog.css')}}" rel="stylesheet">
  </head>

  <body>

    <div class="container">
      <header class="blog-header py-3">
        <div class="row flex-nowrap justify-content-between align-items-center">
          <div class="col-4 pt-1">
            <a class="text-muted" href="#">Subscribe</a>
          </div>
          <div class="col-4 text-center">
            <a class="blog-header-logo text-dark" href="#">Large</a>
          </div>
          <div class="col-4 d-flex justify-content-end align-items-center">
            <a class="text-muted" href="#">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mx-3"><circle cx="10.5" cy="10.5" r="7.5"></circle><line x1="21" y1="21" x2="15.8" y2="15.8"></line></svg>
            </a>
            <a class="btn btn-sm btn-outline-secondary" href="#">Sign up</a>
          </div>
        </div>
      </header>

      <div class="nav-scroller py-1 mb-2">
        <nav class="nav d-flex justify-content-between">
          <a class="p-2 text-muted" href="#">World</a>
          <a class="p-2 text-muted" href="#">U.S.</a>
          <a class="p-2 text-muted" href="#">Technology</a>
          <a class="p-2 text-muted" href="#">Design</a>
          <a class="p-2 text-muted" href="#">Culture</a>
          <a class="p-2 text-muted" href="#">Business</a>
          <a class="p-2 text-muted" href="#">Politics</a>
          <a class="p-2 text-muted" href="#">Opinion</a>
          <a class="p-2 text-muted" href="#">Science</a>
          <a class="p-2 text-muted" href="#">Health</a>
          <a class="p-2 text-muted" href="#">Style</a>
          <a class="p-2 text-muted" href="#">Travel</a>
        </nav>
      </div>

      <div class="jumbotron p-3 p-md-5 text-white rounded bg-dark">
        <div class="col-md-6 px-0">
          <h1 class="display-4 font-italic">Title of a longer featured blog post</h1>
          <p class="lead my-3">Multiple lines of text that form the lede, informing new readers quickly and efficiently about what's most interesting in this post's contents.</p>
          <p class="lead mb-0"><a href="#" class="text-white font-weight-bold">Continue reading...</a></p>
        </div>
      </div>
    </div>

    <main role="main" class="container">
      <div class="row">
        <div class="col-md-8 blog-main">
          <h3 class="pb-3 mb-4 font-italic border-bottom">
            From the Firehose
          </h3>

          <div class="blog-post">
            <h2 class="blog-post-title">Sample blog post</h2>
            <p class="blog-post-meta">January 1, 2014 by <a href="#">Mark</a></p>

            <p>This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported.</p>
            <hr>
            <p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.</p>
            <blockquote>
              <p>Curabitur blandit tempus porttitor. <strong>Nullam quis risus eget urna mollis</strong> ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
            </blockquote>
            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
            <h2>Heading</h2>
            <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
            <h3>Sub-heading</h3>
            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>
            <pre><code>Example code block</code></pre>
            <p>Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa.</p>
            <h3>Sub-heading</h3>
            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
            <ul>
              <li>Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</li>
              <li>Donec id elit non mi porta gravida at eget metus.</li>
              <li>Nulla vitae elit libero, a pharetra augue.</li>
            </ul>
            <p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
            <ol>
              <li>Vestibulum id ligula porta felis euismod semper.</li>
              <li>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</li>
              <li>Maecenas sed diam eget risus varius blandit sit amet non magna.</li>
            </ol>
            <p>Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis.</p>
          </div><!-- /.blog-post -->

          <div class="blog-post">
            <h2 class="blog-post-title">Another blog post</h2>
            <p class="blog-post-meta">December 23, 2013 by <a href="#">Jacob</a></p>

            <p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.</p>
            <blockquote>
              <p>Curabitur blandit tempus porttitor. <strong>Nullam quis risus eget urna mollis</strong> ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
            </blockquote>
            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
            <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
          </div><!-- /.blog-post -->

          <div class="blog-post">
            <h2 class="blog-post-title">New feature</h2>
            <p class="blog-post-meta">December 14, 2013 by <a href="#">Chris</a></p>

            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
            <ul>
              <li>Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</li>
              <li>Donec id elit non mi porta gravida at eget metus.</li>
              <li>Nulla vitae elit libero, a pharetra augue.</li>
            </ul>
            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
            <p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
          </div><!-- /.blog-post -->

          <nav class="blog-pagination">
            <a class="btn btn-outline-primary" href="#">Older</a>
            <a class="btn btn-outline-secondary disabled" href="#">Newer</a>
          </nav>

        </div><!-- /.blog-main -->

        <aside class="col-md-4 blog-sidebar">
          <div class="p-3 mb-3 bg-light rounded">
            <h4 class="font-italic">About</h4>
            <p class="mb-0">Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
          </div>

          <div class="p-3">
            <h4 class="font-italic">Archives</h4>
            <ol class="list-unstyled mb-0">
              <li><a href="#">March 2014</a></li>
              <li><a href="#">February 2014</a></li>
              <li><a href="#">January 2014</a></li>
              <li><a href="#">December 2013</a></li>
              <li><a href="#">November 2013</a></li>
              <li><a href="#">October 2013</a></li>
              <li><a href="#">September 2013</a></li>
              <li><a href="#">August 2013</a></li>
              <li><a href="#">July 2013</a></li>
              <li><a href="#">June 2013</a></li>
              <li><a href="#">May 2013</a></li>
              <li><a href="#">April 2013</a></li>
            </ol>
          </div>

          <div class="p-3">
            <h4 class="font-italic">Elsewhere</h4>
            <ol class="list-unstyled">
              <li><a href="#">GitHub</a></li>
              <li><a href="#">Twitter</a></li>
              <li><a href="#">Facebook</a></li>
            </ol>
          </div>
        </aside><!-- /.blog-sidebar -->

      </div><!-- /.row -->

    </main><!-- /.container -->

    <footer class="blog-footer">
      <p>Blog template built for <a href="https://getbootstrap.com/">Bootstrap</a> by <a href="https://twitter.com/mdo">@mdo</a>.</p>
      <p>
        <a href="#">Back to top</a>
      </p>
    </footer>

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script>
    <script src="../../assets/js/vendor/popper.min.js"></script>
    <script src="../../dist/js/bootstrap.min.js"></script>
    <script src="../../assets/js/vendor/holder.min.js"></script>
    <script>
      Holder.addTheme('thumb', {
        bg: '#55595c',
        fg: '#eceeef',
        text: 'Thumbnail'
      });
    </script>
  </body>
</html>

In the welcome view replace with a component like this:

<x-master>
</x-master>

Part 2

Cut everything inside blog-main class of master.blade.php and add inside these tags of welcome.blade.php

<x-master>
    @section('main-content')
    Code here
    @endsection
</x-master>

Between blog-main class will now look like:

        <div class="col-md-8 blog-main">
          @yield('main-content')
        </div>

You can further make the main content contain another component by creating a new component called <x-test></x-test> inside the new section. Then in components folder create a template called test.blade.php and add the content you want.

Part 3

Pass data to components

Open routes to add proper controller to the route:

Route::get('/', [App\Http\Controllers\HomeController::class, 'index']);

In home.blade.php add this:

<x-test :users="$users"></x-test>

In your test.blade.php add this:

@foreach($users as $user)
    {{$user->name}}
@endforeach

And finally add this to the HomeController.php

use App\Models\User;
    public function index()
    {
        $users = User::all();
        return view('home', ['users' => $users]);
        
    }

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

Now look at your database to see new user created.

Creating a more advanced seeder with factories

Go to database > factories to see the default one for users. We’ll use this one inside our DatabaseSeeder.php to create many users at once. Under run function:

<?php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        User::factory()->count(10)->create(); 
    }
}

Then run:

php artisan db:seed

Check your database and you’ll see 10 new users with real names.

Create Factories for all Database Tables

Edit user factory:

    public function definition()
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
            'country_id' => fake()->numberBetween(1,3),
        ];
    }
php artisan make:factory PostFactory

Inside new factory add to definition function:

    public function definition()
    {
        return [
            'user_id' => 1,
            'title' => fake()->sentence(7,11),
            'content' => fake()->paragraphs(rand(10,1),true),
            'path' => fake()->randomElement(['first.jpg','second.jpg','third.jpg']),
        ];
    }

Create one for Role:

php artisan make:factory RoleFactory
    public function definition()
    {
        return [
            'name' => fake()->randomElement(['administrator','author','subscriber']),
        ];
    }

Create one for photo:

php artisan make:factory PhotoFactory
    public function definition()
    {
        return [
            'path' => 'placeholder.jpg',
            'imageable_id' => fake()->numberBetween(1,3),
            'imageable_type' => fake()->randomElement(['App\Models\User','App\Models\Post']),
        ];
    }

Create one for country:

php artisan make:factory CountryFactory
    public function definition()
    {
        return [
            'name' => fake()->randomElement(['Canada','United States','United Kingdom']),
        ];
    }

Add to DatabaseSeeder.php:

<?php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;
use App\Models\Post;
use App\Models\Role;
use App\Models\Photo;
use App\Models\Country;
use DB;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0');
        DB::table('users')->truncate();
        DB::table('posts')->truncate();
        DB::table('roles')->truncate();
        DB::table('photos')->truncate();
        DB::table('countries')->truncate();

        User::factory()
            ->count(3)
            ->has(Post::factory()->count(2)->has(Photo::factory()->count(1)))
            ->has(Photo::factory()->count(1))
            ->create();
        Role::factory()->count(3)->create(); 
        Country::factory()->count(3)->create();
    }
}

Then run:

php artisan db:seed

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:

npm install laravel-mix@latest
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');
    }