P06 Database Basics

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 32

Practical 6

Database Basics

1 Connecting View Forms to the Database


In this lesson, we are going to learn how to connect HTML front-end form with database.
Recall ourmainapp project. (Practical 3)

We have a registration form and we need to store the information.

First, we need to know where the form is submitted.


homepage.blade.php

<form action="/register" method="POST" id="registration-form">

Try clicking on the Sign up for OurApp button


It tries to post to register but received error 404
Why?

Second, create routes for post request to register.

Using Route::post(), we need two arguments:


1. URL pattern to watch out for (route)
2. Function when route in 1 is visited – can be anonymous or use controller. Use controller to stay organized.

Create UserController using artisan command:


php artisan make:controller UserController

Edit UserController.php to return string for testing.


UserController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller


{
public function register() {
return 'Hello from register function';
}
}

Now we can use UserController in our routes.

There are two ways to do this.


1. Manually
2. Using extensions – install PHP Namespace Resolver by Mehedi Hassan

Using this extension, you can import a class by right-click on the class name and choose Import Class.

routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ExampleController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', [ExampleController::class, "home"] );


Route::get('/about', [ExampleController::class, "about"] );

Route::post('/register', [UserController::Class, 'register'])

Save everything and go to homepage in browser.

Why?
Laravel do this automatically to prevent Cross site request forgery (CSRF) attack. Will be explained in detail
after we see how laravel create cookies.
For now, do the work-around by editing homepage.blade.php
homepage.blade.php

<form action="/register" method="POST" id="registration-form">
@csrf
<div class="form-group">

Open website in browser and view page source



<form action="/register" method="POST" id="registration-form">
<input type="hidden" name="_token" value="j3nbkxI78YB9Ipni8PynvmugWO34ot0OARKwXzeF">
<div class="form-group">
<label for="username-register" class="text-muted mb-1"><small>Username</small></label>

Laravel trusts incoming request when sees this token.

Next, how to access values from form in register function?

UserController.php
<?php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller


{
public function register(Request $request) {
$incomingFields = $request->validation([
'username' => 'required',
'email' => 'required',
'password' => 'required',
]);
User::create($incomingFields);
return 'Hello from register function';
}
}
This line User::create($incomingFields); inserts data into database where User is a model and
create() is a static function.

A model is an abstraction level to


i. perform CRUD operations on the data in a database.
ii. define relationship.
Will be explained in detail in future lessons.

Now, save everything and test your website.

Why?

Since we send an array with multiple data to database using this line User::create($incomingFields),
Laravel checks first that all fields exist.

User.php

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'username',
'email',
'password',
];

Now, let’s try again.

Congratulations! You successfully get the message from register function.

But, most importantly, you need to check your data is entered into database.
Voilà!!! Now you’ve successfully connected your front-end with database.

2 Form Validation Details


In this lesson, we are going to learn how to validate the values that users enter in a form.
We’re also going to learn how to enhance user experience when dealing with a form.

2.1 Data Validation


In UserController.php, let’s start with username. Instead of it just required, let’s add the following rules:
i. minimum character is 3
ii. maximum character is 20
iii. it must be unique

For unique, we can use a static method unique() in Laravel class Rule with two arguments – table name in
database and field name in table.
To use this class, import the class (right click on the class name, Import Class, then choose Illuminate\
Validation\Rule.

UserController.php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class UserController extends Controller


{
public function register(Request $request) {
$incomingFields = $request->validate([
'username' => ['required','min:3','max:20', Rule::unique('users', 'username')],
'email' => 'required',
'password' => 'required'
]);
User::create($incomingFields);
return 'Hello from register function';
}
}

Proceed with rules for email and password.

Now, our UserController.php will look like this:


UserController.php
<?php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class UserController extends Controller


{
public function register(Request $request) {
$incomingFields = $request->validate([
'username' => ['required','min:3','max:20', Rule::unique('users', 'username')],
'email' => ['required','email', Rule::unique('users', 'email')],
'password' => ['required', 'min:8', 'confirmed']
]);
User::create($incomingFields);
return 'Hello from register function';
}
}

We also need to edit homepage.blade.php.

homepage.blade.php

<div class="form-group">
<label for="password-register" class="text-muted mb-1"><small>Password</small></label>
<input name="password" id="password-register" class="form-control" type="password"
placeholder="Create a password" />
</div>

<div class="form-group">
<label for="password-register-confirm" class="text-muted mb-1"><small>Confirm
Password</small></label>
<input name="password_confirmation" id="password-register-confirm" class="form-control"
type="password" placeholder="Confirm password" />
</div>

<button type="submit" class="py-3 mt-4 btn btn-lg btn-success btn-block">Sign up for


OurApp</button>

By using the postfix _confirmation, laravel will automatically check for similarity.

Now, let’s test it out.


Great, both are successful.

However, there is a major security issue here which is storing the plain password text.
So, let’s fix this issue by hashing the password using bcrypt().

UserController.php
<?php

namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class UserController extends Controller


{
public function register(Request $request) {
$incomingFields = $request->validate([
'username' => ['required','min:3','max:20', Rule::unique('users', 'username')],
'email' => ['required','email', Rule::unique('users', 'email')],
'password' => ['required', 'min:8', 'confirmed']
]);

$incomingFields['password'] = bcrypt($incomingFields['password']);

User::create($incomingFields);
return 'Hello from register function';
}
}

Now, let’s test it.

Notice the password is already hashed.

Note: hashing is not the same as encrypting and decrypting.


When you encrypt something, it can be decrypted.
When you hash something, it is a one-way operation.
2.2 User Experience Design
To improve the user experience, let’s add messages for the validation rules.
Laravel will generate the sentences automatically.

homepage.blade.php

<div class="form-group">
<label for="username-register" class="text-muted mb-1"><small>Username</small></label>
<input name="username" id="username-register" class="form-control" type="text"
placeholder="Pick a username" autocomplete="off" />
@error('username')
<p class="m-0 small alert alert-danger shadow-sm">{{$message}}</p>
@enderror
</div>

@ in blade is how you run directive or command

Situation 1 – Username not entered

Situation 2 – Username less than 3 characters


Notice the message changes according to the situation.

Now proceed with the rest of the fields.


homepage.blade.php

<div class="form-group">
<label for="email-register" class="text-muted mb-1"><small>Email</small></label>
<input name="email" id="email-register" class="form-control" type="text"
placeholder="[email protected]" autocomplete="off" />
@error('email')
<p class="m-0 small alert alert-danger shadow-sm">{{$message}}</p>
@enderror
</div>

<div class="form-group">
<label for="password-register" class="text-muted mb-1"><small>Password</small></label>
<input name="password" id="password-register" class="form-control" type="password"
placeholder="Create a password" />
@error('password')
<p class="m-0 small alert alert-danger shadow-sm">{{$message}}</p>
@enderror
</div>

<div class="form-group">
<label for="password-register-confirm" class="text-muted mb-1"><small>Confirm
Password</small></label>
<input name="password_confirmation" id="password-register-confirm" class="form-control"
type="password" placeholder="Confirm password" />
@error('password_confirmation')
<p class="m-0 small alert alert-danger shadow-sm">{{$message}}</p>
@enderror
</div>

Great, now we have added messages for the validation rules.

Next, let’s improve our user experience further.


It’s annoying when we fill in a form and we get error, we need to fill in the form all over again.
Let’s solve this issue using blade helper function called old().

homepage.blade.php

<div class="form-group">
<label for="username-register" class="text-muted mb-1"><small>Username</small></label>
<input value="{{old('username'}}" name="username" id="username-register" class="form-
control" type="text" placeholder="Pick a username" autocomplete="off" />
@error('username')
<p class="m-0 small alert alert-danger shadow-sm">{{$message}}</p>
@enderror
</div>

<div class="form-group">
<label for="email-register" class="text-muted mb-1"><small>Email</small></label>
<input value="{{old('email'}}" name="email" id="email-register" class="form-control"
type="text" placeholder="[email protected]" autocomplete="off" />
@error('email')
<p class="m-0 small alert alert-danger shadow-sm">{{$message}}</p>
@enderror
</div>

Note: Laravel does not support old() for password field.

Notice the value of username and email fields are kept when we received error.

Wonderful!! Now our users are happy!

Authentication Basics

3 Logging In & Logging Out


In this lesson, we’re going to learn about authentication in Laravel.

Before we start with this lesson, let’s clean up our database first since it contains unwanted records like those
with password that is not being hashed.
This can be done by
i. manually
ii. using migrate:fresh

php artisan migrate:fresh

Note: Do not use this command if you have records in your database that you don’t want to lose.
Great!

Now, let’s start.

First, create a user record that we’re going to use for the rest of this lesson.

Next, we’re going to work on the login form.

3.1 Logging In

First, we need to know the location where the form is posted to.

layout.blade.php

<form action="/login" method="POST" class="mb-0 pt-2 pt-md-0">
@csrf
<div class="row align-items-center">
<div class="col-md mr-0 pr-md-0 mb-3 mb-md-0">
<input name="loginusername" class="form-control form-control-sm input-dark" type="text"
placeholder="Username" autocomplete="off" />
</div>
<div class="col-md mr-0 pr-md-0 mb-3 mb-md-0">
<input name="loginpassword" class="form-control form-control-sm input-dark"
type="password" placeholder="Password" />
</div>
<div class="col-md-auto">
<button class="btn btn-primary btn-sm">Sign In</button>
</div>
</div>
</form>

web.php

<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ExampleController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', [ExampleController::class, "home"] );


Route::get('/about', [ExampleController::class, "about"] );

Route::post('/register', [UserController::Class, 'register']);


Route::post('/login', [UserController::Class, 'login']);

UserController.php

class UserController extends Controller
{
public function login(Request $request) {
$incomingFields = $request->validate([
'loginusername' => 'required',
'loginpassword' => 'required'
]);

if(auth()->attempt([
'username'=>$incomingFields['loginusername'],
'password'=>$incomingFields['loginpassword']])) {
return 'Congrats!!!';
} else {
return 'Sorry!!!';
}
}

Laravel has a globally available function auth() (do not need to import the namespace) that returns an object.
Inside the object, we’re using a method called attempt() that will try login using the given username and
password.

Let’s test with a wrong login.

Now, let’s test using a correct login.

$request is an object. Inside the object, there is a method called session() that returns an object. Inside that
object regenerate().
Login again go to Inspect – Application. Notice the laravel-session. This cookies is what prove to Laravel that
we are the user that we just logged in with.
Note: Cookies are automatically sent along from browser to server on every request

This authentication is important for the next step.

3.2 Different Homepage for Logged In Users


Next, we’re going to create a different homepage for logged-in users.

First, let’s work on the header.

Laravel provide a simple way for this using directive @auth with the following format:

@auth
// logged in
@else
// not logged in
@endauth

Copy the header code from html-templates\home-logged-in-no-results.html


layout.blade.php

<header class="header-bar mb-3">
<div class="container d-flex flex-column flex-md-row align-items-center p-3">
<h4 class="my-0 mr-md-auto font-weight-normal"><a href="/" class="text-
white">OurApp</a></h4>
@auth
<div class="flex-row my-3 my-md-0">
<a href="#" class="text-white mr-2 header-search-icon" title="Search" data-
toggle="tooltip" data-placement="bottom"><i class="fas fa-search"></i></a>
<span class="text-white mr-2 header-chat-icon" title="Chat" data-toggle="tooltip" data-
placement="bottom"><i class="fas fa-comment"></i></span>
<a href="#" class="mr-2"><img title="My Profile" data-toggle="tooltip" data-
placement="bottom" style="width: 32px; height: 32px; border-radius: 16px"
src="https://gravatar.com/avatar/f64fc44c03a8a7eb1d52502950879659?s=128" /></a>
<a class="btn btn-sm btn-success mr-2" href="#">Create Post</a>
<form action="#" method="POST" class="d-inline">
<button class="btn btn-sm btn-secondary">Sign Out</button>
</form>
</div>
@else
<form action="/login" method="POST" class="mb-0 pt-2 pt-md-0">
@csrf
<div class="row align-items-center">
<div class="col-md mr-0 pr-md-0 mb-3 mb-md-0">
<input name="loginusername" class="form-control form-control-sm input-dark"
type="text" placeholder="Username" autocomplete="off" />
</div>
<div class="col-md mr-0 pr-md-0 mb-3 mb-md-0">
<input name="loginpassword" class="form-control form-control-sm input-dark"
type="password" placeholder="Password" />
</div>
<div class="col-md-auto">
<button class="btn btn-primary btn-sm">Sign In</button>
</div>
</div>
</form>
@endauth

</div>
</header>

Next, let’s work on the body.
This is a little bit trickier from header because it is not controlled in layout.blade.php file.
To control the body of a page based on user logged in or not, let’s create a method in UserController.php.
But first, let’s edit the route.

web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ExampleController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', [UserController::class, "showCorrectHomepage"] );


Route::get('/about', [ExampleController::class, "about"] );

Route::post('/register', [UserController::Class, 'register']);


Route::post('/login', [UserController::Class, 'login']);

auth()->check() will return true or false depending on whether you are logged in or not.
So we can use it in our if-else statement.

UserController.php

class UserController extends Controller
{
public function showCorrectHomepage() {
if (auth()->check()) {
return 'You are logged in';
} else {
return view('homepage');
}
}

Create a new view called homepage-feed.blade.php

homepage-feed.blade.php
<x-layout>
<div class="container py-md-5 container--narrow">
<div class="text-center">
<h2>Hello <strong>username</strong>, your feed is empty.</h2>
<p class="lead text-muted">Your feed displays the latest posts from the people you
follow. If you don&rsquo;t have any friends to follow that&rsquo;s okay; you can use the
&ldquo;Search&rdquo; feature in the top menu bar to find content written by people with
similar interests and then follow them.</p>
</div>
</div>
</x-layout>
Finally, let’s personalize this page for the user using property username of object from method user() in
global function auth().

homepage-feed.blade.php
<x-layout>
<div class="container py-md-5 container--narrow">
<div class="text-center">
<h2>Hello <strong>{{auth()->user()->username}}</strong>, your feed is empty.</h2>
<p class="lead text-muted">Your feed displays the latest posts from the people you
follow. If you don&rsquo;t have any friends to follow that&rsquo;s okay; you can use the
&ldquo;Search&rdquo; feature in the top menu bar to find content written by people with
similar interests and then follow them.</p>
</div>
</div>
</x-layout>
3.3 Logging Out
First, let’s work on the Sign Out button. This button is supposed to destroy a user’s session so that the user is
no longer logged in.

layout.blade.php

@auth
<div class="flex-row my-3 my-md-0">
<a href="#" class="text-white mr-2 header-search-icon" title="Search" data-toggle="tooltip"
data-placement="bottom"><i class="fas fa-search"></i></a>
<span class="text-white mr-2 header-chat-icon" title="Chat" data-toggle="tooltip" data-
placement="bottom"><i class="fas fa-comment"></i></span>
<a href="#" class="mr-2"><img title="My Profile" data-toggle="tooltip" data-
placement="bottom" style="width: 32px; height: 32px; border-radius: 16px"
src="https://gravatar.com/avatar/f64fc44c03a8a7eb1d52502950879659?s=128" /></a>
<a class="btn btn-sm btn-success mr-2" href="#">Create Post</a>
<form action="/logout" method="POST" class="d-inline">
@csrf
<button class="btn btn-sm btn-secondary">Sign Out</button>
</form>
</div>
@else

web.php
...
Route::post('/register', [UserController::Class, 'register']);
Route::post('/login', [UserController::Class, 'login']);
Route::post('/logout', [UserController::Class, 'logout']);

UserController.php

class UserController extends Controller
{
public function logout() {
auth()->logout();
return 'You are now logged out';
}

public function showCorrectHomepage() {


Go to homepage and click on Sign Out button.

Now we’re logged out. Go to homepage again.


Notice that we get the header and body of guest.

Having to manually go back to the homepage is awkward. Let’s fix that using redirect.

3.4 Redirect
UserController.php

public function logout() {
auth()->logout();
return redirect('/');
}

public function login(Request $request) {
$incomingFields = $request->validate([
'loginusername' => 'required',
'loginpassword' => 'required'
]);

if(auth()-
>attempt(['username'=>$incomingFields['loginusername'],'password'=>$incomingFields['loginpass
word']])) {
$request->session()->regenerate();
return redirect('/');
} else {
return redirect('/');
}
}

3.5 Flash Message
It is good to give a message to user when redirect. This can be done using a flash message. A flash message
is a message that only appears for one request then it gets cleared from session data. Laravel makes this very
easy to set up.

UserController.php

public function logout() {
auth()->logout();
return redirect('/')->with('success','You have sucessfully logged out');
}

public function login(Request $request) {
$incomingFields = $request->validate([
'loginusername' => 'required',
'loginpassword' => 'required'
]);

if(auth()-
>attempt(['username'=>$incomingFields['loginusername'],'password'=>$incomingFields['loginpass
word']])) {
$request->session()->regenerate();
return redirect('/')->with('success','You have sucessfully logged in');
} else {
return redirect('/')->with('failure','Invalid login');
}
}

Next, we need to tell Laravel how and where to display this message.

layout.blade.php

<!-- header ends here -->

@if (session()->has('success'))
<div class="container container--narrow">
<div class="alert alert-success text-center">
{{session('success')}}
</div>
</div>
@endif

@if (session()->has('failure'))
<div class="container container--narrow">
<div class="alert alert-danger text-center">
{{session('failure')}}
</div>
</div>
@endif

{{ $slot }}

<!-- footer begins -->


Great! We’re done with login form. Let’s work on registration form. Instead of returning a message 'Hello
from register function', let’s do the following:
i. redirect to homepage
ii. automatically login the user after successfully register before redirect to homepage

UserController.php

public function register(Request $request) {
$incomingFields = $request->validate([
'username' => ['required','min:3','max:20', Rule::unique('users', 'username')],
'email' => ['required','email', Rule::unique('users', 'email')],
'password' => ['required', 'min:8', 'confirmed']
]);

$incomingFields['password'] = bcrypt($incomingFields['password']);

$user = User::create($incomingFields);
auth()->login($user);
return redirect('/')->with('success','Thank you for creating an account.');
}

Now, register a new user.

Wonderful! Now we’re automatically logged in and redirected to homepage with a flash message.

3.6 Miscellaneous
Before we finish, let’s do some touch ups on our website.
1. Make copyright year to be dynamic
layout.blade.php

<!-- footer begins -->
<footer class="border-top text-center small text-muted py-3">
<p class="m-0">Copyright &copy; {{date('Y')}} <a href="/" class="text-muted">OurApp</a>.
All rights reserved.</p>
</footer>

2. Clean up routes file


web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ExampleController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', [UserController::class, "showCorrectHomepage"] );


Route::get('/about', [ExampleController::class, "about"] );

Route::post('/register', [UserController::Class, 'register']);


Route::post('/login', [UserController::Class, 'login']);
Route::post('/logout', [UserController::Class, 'logout']);
Delete ExampleController.php

End

You might also like