Bài 3 - Laravel xử lý đăng nhập và phân quyền

Chức năng đăng nhập trong laravel

Chào các bạn, hôm này mình sẽ giới thiệu với các bạn cách tạo trang đăng ký, đăng nhập và phần quyền với Laravel.

Nếu các bạn thực hiện tạo bảng dữ liệu trong mysql bằng cách sử dụng lệnh command "php artisan migrate" thì trong cơ sở dữ liệu của các bạn đã có bảng "users". Nếu trong cơ sở dữ liệu của các bạn chưa có bảng "users" thì các bạn gõ lệnh command trên để laravel sẽ tự động tạo bảng cho các bạn.

Lavarel đã tạo sẵn các file controller cho bảng "users"  trong thư mục "app/Http/Controllers/Auth" nên chúng ta có thể sử dụng các file này để xây dựng chức năng đang nhập.

Trước tiên chúng ta định hướng đường dẫn website mặc định sẽ vào trang đăng nhập cho file web.php trong thư mục routes như sau:

// Sửa đường dẫn trang chủ mặc định
Route::get('/', 'HocsinhController@index');
Route::get('/home', 'HocsinhController@index');

// Đăng ký thành viên
Route::get('register', 'Auth\RegisterController@getRegister');
Route::post('register', 'Auth\RegisterController@postRegister');

// Đăng nhập và xử lý đăng nhập
Route::get('login', [ 'as' => 'login', 'uses' => 'Auth\LoginController@getLogin']);
Route::post('login', [ 'as' => 'login', 'uses' => 'Auth\LoginController@postLogin']);

// Đăng xuất
Route::get('logout', [ 'as' => 'logout', 'uses' => 'Auth\LogoutController@getLogout']);

Tiếp theo, để vào trang học sinh kiểm tra nếu chưa đăng nhập vào hệ thống thì sẽ tự động chuyển ra trang đăng nhập chúng ta thêm đoạn code sau trong file "HocSinhController.php":

public function __construct() {
	$this->middleware('auth');
}

 

Chúng ta sẽ tạo một trang form đăng nhập trong thư mục "resource/views/auth/" file "login.blade.php" với nội dung đoạn code sau:

@extends('templates.master')
@section('title','Trang quản lý')
@section('content')
<?php //Hiển thị thông báo thành công?>
@if ( Session::has('success') )
<div class="alert alert-success alert-dismissible" role="alert">
<strong>{{ Session::get('success') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
<?php //Hiển thị thông báo lỗi?>
@if ( Session::has('error') )
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>{{ Session::get('error') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger alert-dismissible" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
<div class="container" style="margin-top: 10%">
<div class="row">
<div class="col-sm-6 col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-body">
<form role="form" action="{{ url('/login') }}" method="POST">
{!! csrf_field() !!}
<fieldset>
<div class="row">
<div class="center-block">
<img class="profile-img" src="/public/images/user-icon.png" alt="User">
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-10 col-md-offset-1 ">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
<input class="form-control" placeholder="Email" name="email" type="text" value="{{ old('email') }}" autofocus>
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input class="form-control" placeholder="Mật khẩu" name="password" type="password" value="">
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Đăng nhập">
</div>
<div class="login-help">
<a href="/{{ url('/register') }}" >Đăng ký</a> - <a href="#" >Quên mật khẩu</a>
</div>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
body{
background: #17568C;
}
.panel{
border-radius: 5px;
}
.panel-heading {
padding: 10px 15px;
}
.panel-title{
text-align: center;
font-size: 15px;
font-weight: bold;
color: #17568C;
}
.panel-footer {
padding: 1px 15px;
color: #A0A0A0;
}
.profile-img {
width: 120px;
height: 120px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
</style>
@endsection

 

Trong file web.php chúng ta đã định trang đang nhập bằng cách gọi hàm "getLogin()" trong file "LoginController.php".

Vậy nên chúng ta sẽ viết hàm hiển thị form đăng nhập trong "LoginController.php" của thư mục "app/Http/Controllers/Auth" để gọi đến file form chúng ta đã tạo.

public function getLogin() {
	return view('auth/login');
}

Tiếp theo chúng ta sẽ viết thêm hàm xử lý đăng nhập trong "LoginController.php" của thư mục "app/Http/Controllers/Auth". Chúng ta cần khai báo một số thư viện được sử dụng trong hàm xử lý.

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use Session;

Nội dung hàm xử lý đăng nhập:

public function postLogin(Request $request) {
	// Kiểm tra dữ liệu nhập vào
	$rules = [
		'email' =>'required|email',
		'password' => 'required|min:6'
	];
	$messages = [
		'email.required' => 'Email là trường bắt buộc',
		'email.email' => 'Email không đúng định dạng',
		'password.required' => 'Mật khẩu là trường bắt buộc',
		'password.min' => 'Mật khẩu phải chứa ít nhất 8 ký tự',
	];
	$validator = Validator::make($request->all(), $rules, $messages);
	
	
	if ($validator->fails()) {
		// Điều kiện dữ liệu không hợp lệ sẽ chuyển về trang đăng nhập và thông báo lỗi
		return redirect('login')->withErrors($validator)->withInput();
	} else {
		// Nếu dữ liệu hợp lệ sẽ kiểm tra trong csdl
		$email = $request->input('email');
		$password = $request->input('password');

		if( Auth::attempt(['email' => $email, 'password' =>$password])) {
			// Kiểm tra đúng email và mật khẩu sẽ chuyển trang
			return redirect('hocsinh');
		} else {
			// Kiểm tra không đúng sẽ hiển thị thông báo lỗi
			Session::flash('error', 'Email hoặc mật khẩu không đúng!');
			return redirect('login');
		}
	}
}

Đến đây chức năng đăng nhập đã hoàn thành. Vì cơ sở dữ liệu được tạo không có tài khoản nào nên chúng ta sẽ viết tiếp form đăng ký thành viên.

Chúng ta sẽ viết form đăng ký thành viên trong thư mục "resources/views/auth" tên file là "register.blade.php" với nội dung đoạn code như sau:

@extends('templates.master')
@section('title','Trang quản lý')
@section('content')
<?php //Hiển thị thông báo thành công?>
@if ( Session::has('success') )
<div class="alert alert-success alert-dismissible" role="alert">
<strong>{{ Session::get('success') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
<?php //Hiển thị thông báo lỗi?>
@if ( Session::has('error') )
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>{{ Session::get('error') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger alert-dismissible" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
<div class="container" style="margin-top: 10%">
<div class="row">
<div class="col-sm-6 col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">ĐĂNG KÝ THÀNH VIÊN</h4>
</div>
<div class="panel-body">
<form role="form" method="POST" action="{{ url('/register') }}">
{!! csrf_field() !!}
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
<input class="form-control" placeholder="Họ và tên" name="name" type="text" value="{{ old('name') }}" autofocus>
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-envelope"></i></span>
<input class="form-control" placeholder="Email" name="email" type="text" value="{{ old('email') }}">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input class="form-control" placeholder="Mật khẩu" name="password" type="password">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input class="form-control" placeholder="Xác nhận mật khẩu" name="password_confirmation" type="password">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-lg btn-primary btn-block">Đăng ký</button>
</div>
<center><a href="/{{ url('/login') }}">Quay về đăng nhập</a></center>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
body{
background: #17568C;
}
.panel{
border-radius: 5px;
}
.panel-heading {
padding: 10px 15px;
}
.panel-title{
text-align: center;
font-size: 15px;
font-weight: bold;
color: #17568C;
}
.panel-footer {
padding: 1px 15px;
color: #A0A0A0;
}
.profile-img {
width: 120px;
height: 120px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
</style>
@endsection

Trong file web.php chúng ta đã viết chức năng gọi đến đường dẫn đăng ký thành viên bằng cách gọi đến hàm getRegister() trong file "RegisterController.php" của thư mục "app/Http/Controllers/Auth" nên chúng ta sẽ viết hàm này gọi đến form đăng ký như sau:

public function getRegister() {
	return view('auth/register');
}

Tiếp theo chúng ta sẽ khai báo một số thư viện sau cho hàm hàm xử lý đăng ký như sau:

use Illuminate\Http\Request;
use Session;

Nội dung code hàm xử lý đăng ký trong file "RegisterController.php" sẽ được viết như sau:

public function postRegister(Request $request) {
    // Kiểm tra dữ liệu vào
	$allRequest  = $request->all();	
	$validator = $this->validator($allRequest);

	if ($validator->fails()) {
		// Dữ liệu vào không thỏa điều kiện sẽ thông báo lỗi
		return redirect('register')->withErrors($validator)->withInput();
	} else {   
		// Dữ liệu vào hợp lệ sẽ thực hiện tạo người dùng dưới csdl
		if( $this->create($allRequest)) {
			// Insert thành công sẽ hiển thị thông báo
			Session::flash('success', 'Đăng ký thành viên thành công!');
			return redirect('register');
		} else {
			// Insert thất bại sẽ hiển thị thông báo lỗi
			Session::flash('error', 'Đăng ký thành viên thất bại!');
			return redirect('register');
		}
	}
}

Trong đoạn code trên chúng ta sử dụng hàm kiểm tra dữ liệu nhập vào validator(), các bạn có thể chỉnh nội dung thông báo thành tiếng việt bằng cách thay đổi thành đoạn code sau:

protected function validator(array $data)
{
	return Validator::make($data,
		[
			'name' => 'required|string|max:255',
			'email' => 'required|string|email|max:255|unique:users',
			'password' => 'required|string|min:6|confirmed',
		],
		[
			'name.required' => 'Họ và tên là trường bắt buộc',
			'name.max' => 'Họ và tên không quá 255 ký tự',
			'email.required' => 'Email là trường bắt buộc',
			'email.email' => 'Email không đúng định dạng',
			'email.max' => 'Email không quá 255 ký tự',
			'email.unique' => 'Email đã tồn tại',
			'password.required' => 'Mật khẩu là trường bắt buộc',
			'password.min' => 'Mật khẩu phải chứa ít nhất 8 ký tự',
			'password.confirmed' => 'Xác nhận mật khẩu không đúng',
		]
	);
}

 

Đên đây các bạn có thể tạo một tài khoản và đăng nhập vào hệ thống. Nếu các bạn muốn phân quyền người dụng các bạn có thể thực hiện như sau:

+ Thêm một trường dữ liệu vào bảng "user" có tên là "level" miền giá trị kiểu int(10): Trường này chúng ta quy định 1 là SuperAdmin, 2 là Admin, 3 là thành viên.

+ Sửa lại hàm create() trong file "RegisterController.php" chúng ta thêm giá trị cho trường level như đoạn code sau:

protected function create(array $data)
{
	return User::create([
		'name' => $data['name'],
		'email' => $data['email'],
		'level' => '3',
		'password' => bcrypt($data['password']),
	]);
}

+  Chúng ta thêm level cho "protected $fillable " trong file "User.php" của thư mục "app" như sau:

protected $fillable = [
	'name', 'email', 'password', 'level',
];

Khi đăng ký thành viên thì level sẽ là 3 (thành viên). Chúng ta có thể hiển thị kết quả phân quyền bằng các thêm đoạn code sau trong thẻ <div class="container"></div> của file master.blade.php

@if (Auth::check())
<div>
Bạn đang đăng nhập với quyền 
@if( Auth::user()->level == 1)
	{{ "SuperAdmin" }}
@elseif( Auth::user()->level == 2)
	{{ "Admin" }}
@elseif( Auth::user()->level == 3)
	{{ "Thành viên" }}
@endif
</div>
<div class="pull-right" style="margin-top: 3px;"><a class="btn btn-primary" href="/{{ url('/logout') }}">Đăng xuất</a></div>
@endif

Trong đoạn code trên tôi có thêm nút đăng xuất và trong file web.php cũng đã tạo link đăng xuất gọi đến hàm getLogout() trong file "LogoutController.php".

Chúng ta sẽ tạo file "LogoutController.php" bằng cách gõ lệnh command sau:

php artisan make:controller Auth/LogoutController --resource

Sau khi tạo xong chúng ta vào thư mục mở file "LogoutController.php" và viết nội dung file với nội dung như sau:

<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;

class LogoutController extends Controller
{
    public function __construct() {
    	$this->middleware('auth');
    }
		
	public function getLogout() {
		Auth::logout();
		return redirect('login');
	}
}

 

Vậy là chúng ta đã hoàn thành các chức năng đăng ký, đăng nhập, phân quyền và đăng xuất.

Qua bài này, mong là có thể giúp ích cho các bạn trong lập trình php với laravel.

Ngụy kim Hưng

Nạp code không dây với ESP8266

 

Làm thế nào để cập nhật từ xa cho ESP8266 ? Hẳn là các bạn có biết một cách để nạp firmware cho ESP8266 cần có kết nối với máy tính qua cổng USB, vậy nếu bạn thấy quá trình trên phải sử dụng dây nối rườm rà quá, ESP8266 có wifi mà sao không tận dụng kết nối wifi để nạp luôn firmware cho chip mà chưa biết cách thì đây đúng là bài bạn cần rồi đấy.

OTA là gì ?

Cập nhật firmware OTA (Over the Air) là quá trình tải firmware mới vào ESP8266 thông qua mạng wifi thay vì sử dụng cổng Serial truyền thống.

Có 3 cách để cập nhật OTA cho ESP8266:

  • Dùng Arduino IDE: tận dụng Arduino IDE có sẵn, thay lựa chọn Port > COM bằng Port > IP
  • Dùng Web Browser: ESP8266 tạo một webserver để ta có thể chọn đường dẫn firmware mới
  • HTTP Server: Cập nhật firmware cho ESP8266 từ một server

Trong quá trình làm thì mình sẽ sử dụng linh hoạt giữa nạp firmware qua serial (cho lần đầu tiên và các lần có sự cố) và nạp firmware qua OTA. Ngoài ra thì có một số vấn đề về bảo mật khi nạp firmware qua OTA được đề cập trong bài viết chỉ ở mức đơn giản.

Chuẩn bị

Phần cứng

  • Node MCU hoặc ESP8266 phiển bản bất kỳ từ V7 trở lên + USB2UART modul (mình dùng NodeMCU)

Phần mềm

Có một yêu cầu nhỏ nữa là máy tính và ESP8266 phải kết nối tới cùng một mạng (nghĩa là chung wifi)

Cài đặt

Các bạn cài đặt python như bình thường. Mình chỉ có một lưu ý khi cài đặt Python nhớ tick chọn Add python.exe to Path như hình

Chỉ cần thế là xong, chúng ta sẽ đi vào luôn cách nạp OTA qua các ví dụ cụ thể

OTA qua Arduino IDE

Cơ bản

Chương trình OTA cho Arduino có sẵn tại đường dẫn File > Examples > ArduinoOTA > BasicOTA hoặc bạn có thể xem chương trình dưới.

Ở đây chỉ có một lưu ý là bạn phải chỉnh lại ten_wifi và password lại cho phù hợp

1
2
const char* ssid = "ten_wifi";
const char* password = "password";
Chương trình OTA Arduino

Kết quả

Sau khi nạp chương trình xuống và mở debug serial ra bạn sẽ thấy hiện IP address, quay lại chương trình chính điều chỉnh Port từ COM chuyển sang IP và tiếp tục complie nạp lại chương trình trên luôn, mục đích là mình kiểm tra xem nạp qua IP có được hay không

Lưu ý 1:

 

Nếu bạn không thấy hiện IP chỗ Port thì tắt Arduino đi và mở lại IP sẽ xuất hiện.

Để tăng tốc độ nạp chương trình bạn có thể thay đổi tại Tool > Upload speed thành 921600

 

Nếu thành công sẽ có thông báo như sau

Có mật khẩu

Tạm thời bước cập nhật qua firmware cơ bản đã thành công, tuy nhiên có một vấn đề phát sinh là nếu update như vậy thì cứ ai ở trong cùng một mạng, có máy tính và Arduino thì đều có thể điều chỉnh thay đổi và nạp firmware linh tinh xuống dưới ESP, nếu trong trường hợp nhà mình sử dụng có mỗi mình thì không vấn đề, nhưng nếu có ai có ý đồ đen tối muốn phá thì vô cùng nguy hiểm. Thôi thêm vào cái mật khẩu cho chắc ăn. Vậy thêm như thế nào ? Vẫn chương trình cũ, chỉ cần mở comment ra chỗ đoạn code

1
ArduinoOTA.setPassword((const char *)"123");

123 là mật khẩu mặc định, bạn có thể thay bằng mật khẩu riêng của mình

Lưu ý 2: Chương trình Arduino vẫn giữ nguyên, nếu có gặp lỗi không hiện IP chỗ port thì bạn vui lòng khởi động lại

 

Hiện tại ESP chỉ hỗ trợ nạp qua IP chứ chưa hỗ trợ debug hiện serial qua IP nhé, nên nhiều lúc ấn vào Debug bạn nhập pass và sẽ có báo sai liên tục. Vui lòng không dùng debug Serial khi nạp qua OTA.

ESPv1 không đủ bộ nhớ cho OTA, nên nếu làm sẽ có lỗi thiếu bộ nhớ

 

OTA qua WebBrowser

Từ việc up firmware qua Arduino quá bất tiện, nhiều lúc không nhận IP lại phải khởi động lại rất mất công thì có một cách khác để up firmware thông qua WebBrowser. Với cách này thì sẽ có một số ưu điểm trong các trường hợp sau:

  • Thấy Arduino rắc rối quá hoặc là vì lý do nào đó mà không nạp được firmware từ Arduino xuống ESP8266.
  • Khi không có server riêng để update OTA hoặc muốn update từ local.
  • Dùng để cập nhật firmware cho số lượng modul vừa phải.

Với chương trình thì chúng ta cần lưu ý chỉnh lại ten_wifi và password lại cho phù hợp

1
2
const char* ssid = "ten_wifi";
const char* password = "password";

ID và Mật khẩu để login mặc định là admin, admin

1
2
const char* update_username = "admin";
const char* update_password = "admin";
Chương trình OTA WebBrowser

Thực hiện nạp chương trình và debug thông qua COM ta sẽ có thông báo

Mở đường dẫn http://esp8266-webupdate.local/firmware để vào giao diện nạp firmware, hoặc http://dia_chi_ip/firmware , như trường hợp của mình là http://192.168.1.104/firmware

Chọn browse và chọn firmware Blink.ino.bin mình đã đưa ở trên, sau khi nạp xong bạn sẽ thấy kit NodeMCU tự động Blink LED

 

OTA qua HTTP Server

Với chương trình này ta có thể cập nhật và tải firmware từ một server HTTP, yêu cầu cần có là địa chỉ IP hoặc địa chỉ domain chứa firmware

Có 2 đoạn cần lưu ý thay cho đúng

1
2
3
WiFiMulti.addAP("ten_wifi", "password");//Thay ten_wifi và password
// Bạn cần thay abc.xyz bằng địa chỉ web chứa fw của bạn và ABC là fw tương ứng
t_httpUpdate_return ret = ESPhttpUpdate.update("http://abc.xyz/ABC.ino.bin");
Chương trình OTA HTTP Server

Ở đây mình sẽ up firmware blink LED lên server. Sau đó nạp chương trình xuống ESP8266, nếu thành công bạn sẽ thấy LED blink, dưới đây là log chương trình khi nạp firmware từ xa.

Lưu ý 3:

 

Firmware up mới phải khác firmware hiện có trên ESP thì mới thành công, nếu cố ý để 2 firmware giống nhau thì sẽ bị báo FAILED

Cần phải có server riêng với địa chỉ IP/ domain để chứa firmware mới, chỉ update được với server http, chưa sử dụng được với server https

 

Tạm kết

Vậy là mình đã trình bày qua 3 cách để các bạn có thể cập nhật được firmware từ xa, bỏ qua được dây nhợ kết nối rắc rối. Nhưng đây chỉ mới là mức cơ bản, còn lại phụ thuộc vào từng ứng dụng cụ thể mà các bạn có thể áp dụng. Hi vọng nó giúp các bạn trong bước đầu tìm hiểu nhanh chóng, có cơ sở để tiếp tục tìm kiếm và áp dụng thêm.

Chia sẻ: Nguyễn Tuấn Khiêm (Nguồn: hocarm)

Bài 2 - Laravel quản lý tập tin và hình ảnh

Học lập trình Laravel căn bản - Bài 2

Chào các bạn, hôm nay mình sẽ tiếp tục chia sẻ với các bạn một số chức năng thường xuyên sử dụng trong lập trình php với Laravel:

- Kiểm tra dữ liệu trước khi lưu xuống cơ sở dữ liệu (sử dụng hàm validate()).

- Upload hình ảnh hoặc file lên website và xóa file cũ khi sửa.

- Chọn giá trị từ một bảng dữ liệu khác.

Yêu cầu bài tập: 

1. Tạo một bảng dữ liệu học sinh gồm 2 thông tin (tên học sinh, số điện thoại) và viết chức năng thêm, sửa, xóa, hiển thị danh sách (Lập trình Laravel căn bản - Bài 1).

2. Thêm trường dữ liệu (hình thẻ, lý lịch, khối) cho bảng học sinh. Trong đó, trường hình thẻ là đường dẫn hình thẻ của học sinh được upload lên và lý lịch là đường dẫn file word lý lịch của của học sinh được upload lên, khối là trường dữ liệu được kết từ bảng khối (id, tenkhoi).

Sau khi chúng ta thực hiện xong yêu cầu đầu tiên từ bài tập trước, chúng ta sẽ tiếp tục với yêu cầu thứ 2.

Chúng ta thêm 3 trường dữ liệu trên như sau:

+ hinhthe: varchar(255)

+ lylich: varchar(255)

+ khoi: int (10)

Và tạo bảng dữ liệu tên "tbl_khoi" với 2 trường dữ liệu sau:

+ id: int(10)

+ tenkhoi: varchar(255)

Sau khi tạo bảng dữ liệu "tbl_khoi" xong các bạn có thể viết quản lý thêm, sửa, xóa như đã được hướng dẫn từ bài trước và thêm vào các khối như KHỐI 6, KHỐI 7, KHỐI 8, KHỐI 9.

Tiếp theo chúng ta sẽ chỉnh sửa lại một số function và form.

- Function create(): hàm hiển thị trang thêm học sinh của file "HocSinhController.php"

Chúng ta sẽ chỉnh sửa lại hàm này lại như sau:

public function create()
{
	//Lấy danh sách bảng khối
	$dskhoi = DB::table('tbl_khoi')->select('id','tenkhoi')->get();
	//Hiển thị trang thêm học sinh
	return view('hocsinh.create')->with('dskhoi',$dskhoi);
}

- Trong file "create.blade.php" các bạn thêm dòng code sau để hiển thị kết quả kiểm tra dữ liệu không thỏa điều kiện validate() được viết trong function store(Request $request) của file "HocSinhController.php".

@if ($errors->any())
<div class="alert alert-danger alert-dismissible" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

Sửa lại form thêm mới học sinh:

+ Thêm đoạn code sau để có thể upload file trong thẻ form thêm học sinh: enctype="multipart/form-data"

+ Thêm 3 trường dữ liệu nhập vào trong form thêm học sinh:

<div class="form-group">
<label for="hinhthe">Chọn hình thẻ</label>
<input type="file" class="form-control" id="hinhthe" name="hinhthe"/>
</div>
<div class="form-group">
<label for="lylich">Chọn file lý lịch</label>
<input type="file" class="form-control" id="lylich" name="lylich"/>
</div>
<div class="form-group">
<label for="khoi">Chọn khối</label>
<select class="form-control" id="khoi" name="khoi" required>
<option value="">-- Chọn khối --</option>
@foreach($dskhoi as $khoi)
<option value="{!! $khoi->id !!}">{!! $khoi->tenkhoi !!}</option>
@endforeach
</select>
</div>

- Trong file "HocSinhController.php" chúng ta chỉnh sửa nội dung function store() như sau:

public function store(Request $request)
{		
	//Kiểm tra giá trị tenhocsinh, sodienthoai, khoi
	$this->validate($request, 
		[
			//Kiểm tra giá trị rỗng
			'tenhocsinh' => 'required',
			'sodienthoai' => 'required',
			'khoi' => 'required',
		],			
		[
			//Tùy chỉnh hiển thị thông báo
			'tenhocsinh.required' => 'Bạn chưa nhập tên học sinh!',
			'sodienthoai.required' => 'Bạn chưa nhập số điện thoại!',
			'khoi.required' => 'Bạn chưa chọn khối!',
		]
	);
	
	//Lưu hình thẻ khi có file hình
	$gethinhthe = '';
	if($request->hasFile('hinhthe')){
		//Hàm kiểm tra dữ liệu
		$this->validate($request, 
			[
				//Kiểm tra đúng file đuôi .jpg,.jpeg,.png.gif và dung lượng không quá 2M
				'hinhthe' => 'mimes:jpg,jpeg,png,gif|max:2048',
			],			
			[
				//Tùy chỉnh hiển thị thông báo không thõa điều kiện
				'hinhthe.mimes' => 'Chỉ chấp nhận hình thẻ với đuôi .jpg .jpeg .png .gif',
				'hinhthe.max' => 'Hình thẻ giới hạn dung lượng không quá 2M',
			]
		);
		
		//Lưu hình ảnh vào thư mục public/upload/hinhthe
		$hinhthe = $request->file('hinhthe');
		$gethinhthe = time().'_'.$hinhthe->getClientOriginalName();
		$destinationPath = public_path('upload/hinhthe');
		$hinhthe->move($destinationPath, $gethinhthe);
	}
	
	//Lưu file lý lịch khi có file
	$getlylich = '';
	if($request->hasFile('lylich')){
		$this->validate($request, 
			[
				//Kiểm tra đúng file đuôi .doc hay .docx và dung lượng không quá 5M
				'lylich' => 'mimes:doc,docx|max:5120',
			],			
			[
				//Tùy chỉnh hiển thị thông báo không thõa điều kiện
				'lylich.mimes' => 'Chỉ chấp nhận lý lịch với đuôi .doc .docx',
				'lylich.max' => 'Lý lịch giới hạn dung lượng không quá 5M',
			]
		);
		
		//Lưu file vào thư mục public/upload/lylich
		$lylich = $request->file('lylich');
		$getlylich = time().'_'.$lylich->getClientOriginalName();
		$destinationPath = public_path('/upload/lylich');
		$lylich->move($destinationPath, $getlylich); 
	}
	
	//Lấy giá trị học sinh đã nhập
	date_default_timezone_set("Asia/Ho_Chi_Minh");
	$allRequest  = $request->all();
	$tenhocsinh  = $allRequest['tenhocsinh'];
	$sodienthoai = $allRequest['sodienthoai'];
	$khoi = $allRequest['khoi'];
	
	//Gán giá trị vào array
	$dataInsertToDatabase = array(
		'tenhocsinh'  => $tenhocsinh,
		'sodienthoai' => $sodienthoai,
		'hinhthe' => $gethinhthe,
		'lylich' => $getlylich,
		'khoi' => $khoi,
		'created_at' => date('Y-m-d H:i:s'),
		'updated_at' => date('Y-m-d H:i:s'),
	);
	
	//Insert vào bảng tbl_hocsinh
	$insertData = DB::table('tbl_hocsinh')->insert($dataInsertToDatabase);
	if ($insertData) {
		Session::flash('success', 'Thêm mới học sinh thành công!');
	}else {                        
		Session::flash('error', 'Thêm thất bại!');
	}
	
	//Thực hiện chuyển trang
	return redirect('hocsinh/create');
}

- Sửa chức năng hiển thị danh sách học sinh:

+ Sửa function index() trong file "HocSinhController.php"

public function index()
{
	//Lấy danh sách học sinh từ database
	$getData = DB::table('tbl_hocsinh as hs')
				->leftJoin('tbl_khoi as khoi', 'hs.khoi', '=', 'khoi.id')
				->select('hs.id','hs.tenhocsinh','hs.sodienthoai','hs.hinhthe','hs.lylich','khoi.tenkhoi')->get();
				
	//Gọi đến file list.blade.php trong thư mục "resources/views/hocsinh" với giá trị gửi đi tên listhocsinh = $getData
	return view('hocsinh.list')->with('listhocsinh',$getData);
}

+ Chỉnh sửa nội dung file "list.blade.php" như đoạn code sau:

@extends('templates.master')
@section('title','Quản lý học sinh')
@section('content')
<?php //Hiển thị thông báo thành công?>
<div class="page-header"><h4>Quản lý học sinh</h4></div>
@if ( Session::has('success') )
<div class="alert alert-success alert-dismissible" role="alert">
<strong>{{ Session::get('success') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
<?php //Hiển thị thông báo lỗi?>
@if ( Session::has('error') )
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>{{ Session::get('error') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif
<?php //Hiển thị danh sách học sinh?>
<style>
#myImg { border-radius: 5px; cursor: pointer; transition: 0.3s; }
#myImg:hover {opacity: 0.7;}
/* The Modal (background) */
.modal { display: none; position: fixed; z-index: 9999; padding-top: 50px; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.9);}
/* Modal Content (image) */
.modal-content { margin: auto; display: block; width: 50%; max-width: 400px; }
/* Caption of Modal Image */
#caption { margin: auto; display: block; width: 80%; max-width: 700px; text-align: center; color: #ccc; padding: 10px 0; height: 50px; }
/* Add Animation */
.modal-content, #caption { -webkit-animation-name: zoom; -webkit-animation-duration: 0.6s; animation-name: zoom; animation-duration: 0.6s; }
@-webkit-keyframes zoom { from {-webkit-transform:scale(0)} to {-webkit-transform:scale(1)} }
@keyframes zoom { from {transform:scale(0)} to {transform:scale(1)} }
/* The Close Button */
.close { position: absolute; top: 15px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; transition: 0.3s; }
.close:hover, .close:focus { color: #bbb; text-decoration: none; cursor: pointer; }
/* 100% Image Width on Smaller Screens */
@media only screen and (max-width: 700px){
.modal-content {
width: 100%;
}
}
</style>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="table-responsive">
<p><a class="btn btn-primary" href="/{{ url('/hocsinh/create') }}">Thêm mới</a></p>
<table id="DataList" class="table table-bordered table-hover">
<thead>
<tr>
<th>STT</th>
<th>Tên học sinh</th>
<th>Số điện thoại</th>
<th>Hình thẻ</th>
<th>File lý lịch</th>
<th>Khối</th>
<th>Sửa</th>
<th>Xóa</th>
</tr>
</thead>
<tbody>
<?php //Vòng lập foreach lấy giá vào bảng?>
@foreach($listhocsinh as $key => $hocsinh)
<tr>
<td style="text-align: center; vertical-align: middle;">{{ $key+1 }}</td>
<td style="vertical-align: middle;">{{ $hocsinh->tenhocsinh }}</td>
<td style="vertical-align: middle;">{{ $hocsinh->sodienthoai }}</td>
<td style="text-align: center; vertical-align: middle; width: 10%;">
@if($hocsinh->hinhthe != '')
<img onclick="MymodalImage(this);" alt="{{ $hocsinh->tenhocsinh }}" src="/public/upload/hinhthe/{{ $hocsinh->hinhthe }}" style="cursor: zoom-in;" width="60"/>
@else
<img onclick="MymodalImage(this);" alt="{{ $hocsinh->tenhocsinh }}" src="/public/upload/hinhthe/noimage.png" style="cursor: zoom-in;" width="60"/>
@endif
<td style="text-align: center; vertical-align: middle; width: 10%;">
@if($hocsinh->lylich != '')
<a class="btn btn-primary" href="/public/upload/lylich/{{ $hocsinh->lylich }}">Download về máy</a>
@else
<img onclick="MymodalImage(this);" src="/public/upload/lylich/nofile.png" alt="{{ $hocsinh->tenhocsinh }}" style="cursor: zoom-in;" width="60"/>
@endif
</td>
<td style="text-align: center; vertical-align: middle;">
{{ $hocsinh->tenkhoi }}
</td>
<td style="text-align: center; vertical-align: middle;"><a href="/hocsinh/{{ $hocsinh->id }}/edit">Sửa</a></td>
<td style="text-align: center; vertical-align: middle;"><a href="/hocsinh/{{ $hocsinh->id }}/delete">Xóa</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<div id="myModal" class="modal">
<span class="close">&times;</span>
<img class="modal-content" id="img01">
<div id="caption"></div>
</div>
<script>
function MymodalImage(e)
{
// Get the modal
var modal = document.getElementById('myModal');
// Get the image and insert it inside the modal - use its "alt" text as a caption
var modalImg = document.getElementById("img01");
var captionText = document.getElementById("caption");
modal.style.display = "block";
modalImg.src = e.src;
captionText.innerHTML = e.alt;
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = "none";
}
}
</script>
@endsection

+ Trong đoạn code trên. trường hợp không có file hình thẻ sẽ hiển thị hình ảnh mặc định có tên là "noimage.png" và nếu như không có file lý lịch sẽ hiển thị hình ảnh mặc định là "nofile.png". Các bạn có thể lấy hình ảnh noimage trên mạng mà các bạn thích vào thư mục "public/hocsinh/hinhthe" và thư mục "public/hocsinh/lylich".

- Sửa chức năng sửa học sinh:

+ Sửa nội dung function edit($id) trong file "HocSinhController.php" với nội dung code sau:

public function edit($id)
{
	//Lấy dữ liệu bảng tbl_khoi từ Database
	$dskhoi = DB::table('tbl_khoi')->select('id','tenkhoi')->get();
	
	//Lấy dữ liệu từ Database với các trường được lấy và với điều kiện id = $id
	$getData = DB::table('tbl_hocsinh')->select('id','tenhocsinh','sodienthoai','hinhthe','lylich','khoi')->where('id',$id)->get();
	
	//Gọi đến file edit.blade.php trong thư mục "resources/views/hocsinh" với giá trị gửi đi tên getHocSinhById = $getData và dskhoi = $dskhoi
	return view('hocsinh.edit', ['getHocSinhById' => $getData, 'dskhoi' => $dskhoi]);
}

+ Sửa nội dung form sửa học sinh: 

Thêm đoạn code sau để hiển thị kết quả kiểm tra của hàm validate() không thỏa điều kiện.

@if ($errors->any())
<div class="alert alert-danger alert-dismissible" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

Thêm đoạn code sau để có thể upload file trong thẻ form: enctype="multipart/form-data"

Thêm 3 trường dữ liệu nhập vào form sửa với đoạn code sau:

<div class="form-group">
<label for="hinhthe">Chọn hình thẻ mới</label>
<input type="file" class="form-control" id="hinhthe" name="hinhthe"/>
</div>
<div class="form-group">
<label for="lylich">Chọn file lý lịch mới</label>
<input type="file" class="form-control" id="lylich" name="lylich"/>
</div>
<div class="form-group">
<label for="khoi">Chọn khối</label>
<select class="form-control" id="khoi" name="khoi" required>
<option value="">-- Chọn khối --</option>
@foreach($dskhoi as $khoi)
<option value="{!! $khoi->id !!}" {!! ($getHocSinhById[0]->khoi == $khoi->id) ? 'selected="selected"' : null !!}>{!! $khoi->tenkhoi !!}</option>
@endforeach
</select>
</div>

Sửa function update(Request $request) trong file "HocSinhController.php" với nội dung code như sau:

public function update(Request $request)
{
	//Cap nhat sua hoc sinh
	date_default_timezone_set("Asia/Ho_Chi_Minh");	

	//Kiểm tra giá trị tenhocsinh, sodienthoai, khoi
	$this->validate($request, 
		[
			'tenhocsinh' => 'required',
			'sodienthoai' => 'required',
			'khoi' => 'required',
		],			
		[
			'tenhocsinh.required' => 'Bạn chưa nhập tên học sinh!',
			'sodienthoai.required' => 'Bạn chưa nhập số điện thoại!',
			'khoi.required' => 'Bạn chưa chọn khối!',
		]
	);
	
	//Thực hiện lưu thay đổi hình thẻ khi có file
	if($request->hasFile('hinhthe')){
		$this->validate($request, 
			[
				'hinhthe' => 'mimes:jpg,jpeg,png,gif|max:2048',
			],			
			[
				'hinhthe.mimes' => 'Chỉ chấp nhận hình thẻ với đuôi .jpg .jpeg .png .gif',
				'hinhthe.max' => 'Hình thẻ giới hạn dung lượng không quá 2M',
			]
		);
		
		//Xóa file hình thẻ cũ
		$getHT = DB::table('tbl_hocsinh')->select('hinhthe')->where('id',$request->id)->get();
		if($getHT[0]->hinhthe != '' && file_exists(public_path('upload/hinhthe/'.$getHT[0]->hinhthe)))
		{
			unlink(public_path('upload/hinhthe/'.$getHT[0]->hinhthe));
		}
		
		//Lưu file hình thẻ mới
		$hinhthe = $request->file('hinhthe');
		$gethinhthe = time().'_'.$hinhthe->getClientOriginalName();
		$destinationPath = public_path('upload/hinhthe');
		$hinhthe->move($destinationPath, $gethinhthe);
		$updateHinhThe = DB::table('tbl_hocsinh')->where('id', $request->id)->update([
			'hinhthe' => $gethinhthe
		]);
	}
	
	//Thực hiện lưu thay đổi lý lịch khi có file
	if($request->hasFile('lylich')){
		$this->validate($request, 
			[
				'lylich' => 'mimes:doc,docx|max:5120',
			],			
			[
				'lylich.mimes' => 'Chỉ chấp nhận lý lịch với đuôi .doc .docx',
				'lylich.max' => 'Lý lịch giới hạn dung lượng không quá 5M',
			]
		);
		
		//Xóa file lý lịch cũ
		$getLL = DB::table('tbl_hocsinh')->select('lylich')->where('id',$request->id)->get();
		if($getLL[0]->lylich != '' &&file_exists(public_path('upload/lylich/'.$getLL[0]->lylich)))
		{
			unlink(public_path('upload/lylich/'.$getLL[0]->lylich));
		}
		
		//Lưu file lý lịch mới
		$lylich = $request->file('lylich');
		$getlylich = time().'_'.$lylich->getClientOriginalName();
		$destinationPath = public_path('upload/lylich');
		$lylich->move($destinationPath, $getlylich);
		$updateLylich = DB::table('tbl_hocsinh')->where('id', $request->id)->update([
			'lylich' => $getlylich
		]);
	}
	
	//Thực hiện câu lệnh update với các giá trị $request trả về
	$updateData = DB::table('tbl_hocsinh')->where('id', $request->id)->update([
		'tenhocsinh' => $request->tenhocsinh,
		'sodienthoai' => $request->sodienthoai,
		'khoi' => $request->khoi,
		'updated_at' => date('Y-m-d H:i:s')
	]);
	
	//Kiểm tra lệnh update để trả về một thông báo
	if ($updateData) {
		Session::flash('success', 'Sửa học sinh thành công!');
	}else {                        
		Session::flash('error', 'Sửa thất bại!');
	}
	
	//Thực hiện chuyển trang
	return redirect('hocsinh/'.$request->id.'/edit');
}

Chức năng xóa không ảnh thưởng nên chúng ta không cần sửa. Bạn có thể thêm xóa file hình thẻ và file lý lịch khi xóa học sinh như đã ghi chú trong đoạn code trên.

Vậy là chúng ta đã hoàn thành yêu cầu bài số 2. Qua bài này chúng ta có thể đúc kết được những kinh nghiệm sau:

- Sử dùng hàm validate() để kiểm tra dữ liệu nhập vào trước khi lưu xuống Database, giúp tránh bị lỗi câu lệnh sql và kiểm tra file upload không đúng định dạng.

- Muốn upload file lên website từ form chúng ta cần thêm thuộc tính sau cho thẻ <form>: enctype="multipart/form-data"

- Cách xóa file cũ để tối ưu dung lượng sử dụng trên website: unlink('đường dẫn file hình')  - Lưu ý kiểm tra tồn tại file với hàm file_exists() trước khi xóa để tránh bị lỗi.

Tôi sẽ hướng dẫn các bạn thực hiện form đăng nhập và phân quyền trong bài tập tiếp theo, mong các bạn đọc giả ủng hộ.

Chúc các bạn thực hiện thành công dự án của mình!

Ngụy Kim Hưng

Bài 1 - Lavarel quản lý CRUD & tìm kiếm

Học lập trình Laravel căn bản

Trong bài này tôi sẽ hướng dẫn các bạn thực hiện một bài tập nhỏ với laravel.

Yêu cầu bài tập: Tạo một bảng dữ liệu học sinh gồm 2 thông tin (tên học sinh, số điện thoại) và viết chức năng thêm, sửa, xóa, hiển thị danh sách.

Bước 1: Tạo cơ sở dữ liệu và bảng dữ liệu, tạo link cho các chức năng.

Trước tiên các bạn cần tạo một cơ sở dữ liệu và chỉnh cấu hình kết nối với cơ sở dữ liệu.

Ví dụ: - Tôi đã tạo một cơ sở dữ liệu trong Mysql có tên là: laravel_db

         - Cấu hình lại file .env với 3 dòng: database, username, password.

DB_DATABASE=laravel_db
DB_USERNAME=root
DB_PASSWORD=vertrigo

Tiếp theo các bạn tạo bảng dữ liệu cho cơ sở dữ liệu laravel_db . Các bạn có thể tạo thẳng trong Mysql hoặc có thể tạo theo cách sau.

- Tại thư mục tổng các bạn mở Open command và gõ lệnh sau:

php artisan make:migration tenbang

- Sau đó các bạn vào thư mục "database/migrations" mở file php với tên bảng vừa tạo và sửa hàm public function up() và hàm public function dow() thành như sau:

public function up()
{
	Schema::create('tbl_hocsinh', function (Blueprint $table) {
		$table->increments('id');
		$table->string('tenhocsinh', 255);
		$table->string('sodienthoai', 15);
		$table->timestamps();
	});
}

public function down()
{
	Schema::dropIfExists('tbl_hocsinh');
}

- Tiếp theo các bạn tạo quay lại trong open command và gõ lệnh sau.

php artisan migrate

Sau khi thực hiện xong các bạn sẽ có bảng dữ liệu là "tbl_hocsinh".

Tiếp theo các bạn tạo file controller cho bảng học sinh. Trong open command các bạn gõ lệnh sau:

php artisan make:controller HocsinhController ––resource

Sau khi rõ lệnh trên trong thư mục "app/Http/Controllers" sẽ có file HocsinhController.php . Mọi thao tác xử lý dữ liệu sẽ được thực hiện trong file này.

Tiếp theo các bạn tạo link cho các chức năng thêm, sửa, xóa và hiển thị danh sách. Các bạn vào thư mục routes và mở file web.php và thêm dòng code sau:

Route::get('hocsinh', 'HocsinhController@index'); // Hiển thị danh sách học sinh
Route::get('hocsinh/create', 'HocsinhController@create'); // Thêm mới học sinh
Route::post('hocsinh/create', 'HocsinhController@store'); // Xử lý thêm mới học sinh
Route::get('hocsinh/{id}/edit', 'HocsinhController@edit'); // Sửa học sinh
Route::post('hocsinh/update', 'HocsinhController@update'); // Xử lý sửa học sinh
Route::get('hocsinh/{id}/delete', 'HocsinhController@destroy'); // Xóa học sinh

Đoạn code trên có nghĩa như sau:

- Route::get là link với giá trị trả về là phương thức get.

- Route::post là link với giá trị trả về là phương thức post.

- Trong dấu ngoặc đơn có nghĩa như sau: ('đường dẫn do người lập trình tự đặt','tên file controller được chọn@tên hàm trong controller được thực hiện')

Bước 2: Thực hiện chức năng thêm học sinh.

- Tạo giao diện master: trang master sẽ là trang có cấu trúc chung để các trang con kế thừa (nó có nghĩa như trang index).

+ Trong thư mục "resources/views" các bạn tạo thư mục tên là "templates".

+ Trong thư mục "templates" vừa tạo các bạn tạo file "master.blade.php" và chép đoạn code sau.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title> @yield('title')</title>
<link href="/{!! url('public/css/bootstrap.min.css') !!}" type="text/css" rel="stylesheet" />
<link href="/{!! url('public/css/dataTables.bootstrap.min.css') !!}" type="text/css" rel="stylesheet" />
<link href="/{!! url('public/css/bootstrap-custom.css') !!}" type="text/css" rel="stylesheet" />
</head>
<body>
<div class="container">
@section('content')
@show
</div>
<script type="text/javascript" src="/{!! url('public/js/jquery.min.js') !!}"></script>
<script type="text/javascript" src="/{!! url('public/js/bootstrap.min.js') !!}"></script>
<script type="text/javascript" src="/{!! url('public/js/jquery.dataTables.min.js') !!}"></script>
<script type="text/javascript" src="/{!! url('public/js/dataTables.bootstrap.min.js') !!}"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#DataList").DataTable({
"aLengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "Tất cả"]],
"iDisplayLength": 10,
"oLanguage": {
"sLengthMenu": "Hiển thị _MENU_ dòng mỗi trang",
"oPaginate": {
"sFirst": "<span class='glyphicon glyphicon-step-backward' aria-hidden='true'></span>",
"sLast": "<span class='glyphicon glyphicon-step-forward' aria-hidden='true'></span>",
"sNext": "<span class='glyphicon glyphicon-triangle-right' aria-hidden='true'></span>",
"sPrevious": "<span class='glyphicon glyphicon-triangle-left' aria-hidden='true'></span>"
},
"sEmptyTable": "Không có dữ liệu",
"sSearch": "Tìm kiếm:",
"sZeroRecords": "Không có dữ liệu",
"sInfo": "Hiển thị từ _START_ đến _END_ trong tổng số _TOTAL_ dòng được tìm thấy",
"sInfoEmpty" : "Không tìm thấy",
"sInfoFiltered": " (trong tổng số _MAX_ dòng)"
}
});
});
</script>
</body>
</html>

Các file .css, .js các bạn có thể tìm và download trên mạng.

Trong đoạn code trên có một số phần cần chú ý:

+ @yield('title') : dùng để tùy ý thay đổi title khi vào trang con.

+ @section('content')@show : dùng để tùy ý thay đổi nội dung <body> của trang khi vào trang con.

- Tạo giao diện thêm học sinh: 

+ Thêm đoạn code sau cho hàm create() trong file HocSinhController.php

public function create()
{
	//Hiển thị trang thêm học sinh
	return view('hocsinh.create');
}

Đoạn code trên sẽ chay file create.blade.php trong thư mục "resources/views/hocsinh".

+ Trong thư mục "resources/views" các bạn tạo thư mục tên là "hocsinh" và trong thư mục "hocsinh" các bạn tạo file "create.blade.php". Các bạn chép code mẫu sau vào file "create.blade.php":

@extends('templates.master')

@section('title','Thêm mới học sinh')

@section('content')

<div class="page-header"><h4>Quản lý học sinh</h4></div>

<?php //Hiển thị thông báo thành công?>
@if ( Session::has('success') )
<div class="alert alert-success alert-dismissible" role="alert">
<strong>{{ Session::get('success') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

<?php //Hiển thị thông báo lỗi?>
@if ( Session::has('error') )
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>{{ Session::get('error') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

<?php //Form thêm mới học sinh?>
<p><a class="btn btn-primary" href="/{{ url('/hocsinh') }}">Về danh sách</a></p>
<div class="col-xs-4 col-xs-offset-4">
<center><h4>Thêm học sinh</h4></center>
<form action="{{ url('/hocsinh/create') }}" method="post">
<input type="hidden" id="_token" name="_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="tenhocsinh">Tên học sinh</label>
<input type="text" class="form-control" id="tenhocsinh" name="tenhocsinh" placeholder="Tên học sinh" maxlength="255" required />
</div>
<div class="form-group">
<label for="sodienthoai">Số điện thoại</label>
<input type="text" class="form-control" id="sodienthoai" name="sodienthoai" placeholder="Số điện thoại" maxlength="15" required />
</div>
<center><button type="submit" class="btn btn-primary">Thêm</button></center>
</form>
</div>

@endsection

Trong đoạn code trên tôi xin giới thiệu với các bạn một số phần sử dụng trong laravel:

+ @extends('templates.master'): Kế thừa giao diện từ file "master.blade.php" trong thư mục "resources/views/templates" mà chúng ta đã tạo ở trên.

+ @section('title','Thêm mới học sinh'): Thay đổi tiêu đề trang web.

+ @section('content') Nội dung trang con @endsection: Thay đổi nội dung trong <body>

- Viết hàm xử lý thêm học sinh:

+ Thêm khai báo 2 thư viện trong file "HocSinhController.php" để có thể truy xuất dữ liệu từ database và tạo session thông báo.

use Illuminate\Support\Facades\DB;
use Session;

+ Các bạn thêm đoạn code xử lý sau cho hàm store(Request $request)

public function store(Request $request)
{
	//Them moi hoc sinh
	//Set timezone
	date_default_timezone_set("Asia/Ho_Chi_Minh");
	
	//Lấy giá trị học sinh đã nhập
	$allRequest  = $request->all();
	$tenhocsinh  = $allRequest['tenhocsinh'];
	$sodienthoai = $allRequest['sodienthoai'];
	
	//Gán giá trị vào array
	$dataInsertToDatabase = array(
		'tenhocsinh'  => $tenhocsinh,
		'sodienthoai' => $sodienthoai,
		'created_at' => date('Y-m-d H:i:s'),
		'updated_at' => date('Y-m-d H:i:s'),
	);
	
	//Insert vào bảng tbl_hocsinh
	$insertData = DB::table('tbl_hocsinh')->insert($dataInsertToDatabase);
	
	//Kiểm tra Insert để trả về một thông báo
	if ($insertData) {
		Session::flash('success', 'Thêm mới học sinh thành công!');
	}else {                        
		Session::flash('error', 'Thêm thất bại!');
	}
	
	//Thực hiện chuyển trang
	return redirect('hocsinh/create');
}

Vậy là chúng ta đã hoàn thành chức năng thêm mới học sinh.

Bước 3: Thực hiện chức năng hiển thị danh sách học sinh.

- Lấy thông tin từ Database và gửi đến tầng view hiển thị lên trang web:

Các bạn thêm đoạn code sau cho hàm index() trong file "HocSinhController.php"

public function index()
{
	//Lấy danh sách học sinh từ database
	$getData = DB::table('tbl_hocsinh')->select('id','tenhocsinh','sodienthoai')->get();
	
	//Gọi đến file list.blade.php trong thư mục "resources/views/hocsinh" với giá trị gửi đi tên listhocsinh = $getData
	return view('hocsinh.list')->with('listhocsinh',$getData);
}

- Viết tầng view hiển thị thông tin học sinh và thêm nút sửa và xóa:

Các bạn tạo file "list.blade.php" trong thư mục "resources/views/hocsinh" và chép đoạn code sau vào.

@extends('templates.master')

@section('title','Quản lý học sinh')

@section('content')

<?php //Hiển thị thông báo thành công?>
<div class="page-header"><h4>Quản lý học sinh</h4></div>

@if ( Session::has('success') )
<div class="alert alert-success alert-dismissible" role="alert">
<strong>{{ Session::get('success') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

<?php //Hiển thị thông báo lỗi?>
@if ( Session::has('error') )
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>{{ Session::get('error') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

<?php //Hiển thị danh sách học sinh?>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="table-responsive">
<p><a class="btn btn-primary" href="/{{ url('/hocsinh/create') }}">Thêm mới</a></p>
<table id="DataList" class="table table-bordered table-hover">
<thead>
<tr>
<th>STT</th>
<th>Tên học sinh</th>
<th>Số điện thoại</th>
<th>Sửa</th>
<th>Xóa</th>
</tr>
</thead>
<tbody>
<?php //Khởi tạo vòng lập foreach lấy giá vào bảng?>
@foreach($listhocsinh as $key => $hocsinh)
<tr>
<?php //Điền số thứ tự?>
<td>{{ $key+1 }}</td>
<?php //Lấy tên học sinh?>
<td>{{ $hocsinh->tenhocsinh }}</td>
<?php //Lấy số điện thoại?>
<td>{{ $hocsinh->sodienthoai }}</td>
<?php //Tạo nút sửa học sinh?>
<td><a href="/hocsinh/{{ $hocsinh->id }}/edit">Sửa</a></td>
<?php //Tạo nút xóa học sinh?>
<td><a href="/hocsinh/{{ $hocsinh->id }}/delete">Xóa</a></td>
</tr>
<?php //Kết thúc vòng lập foreach?>
@endforeach
</tbody>
</table>
</div>
</div>
</div>

@endsection

Tới đây chúng ta đã hoàn thành chức năng hiển thị danh sách học sinh.

Bước 4: Thực hiện chức năng sửa học sinh.

Trong bước 1 chúng ta đã khai báo link sửa trong file web.php là:

Route::get('hocsinh/{id}/edit', 'HocsinhController@edit');

+ {id}: là giá trị được gửi theo phương thức get có tên biến là id. Và ở bước 3 biến id được gán bằng giá trị id từ bảng dữ liệu.

+ 'HocsinhController@edit': Gọi đến hàm edit trong file "HocsinhController.php"

Chúng ta thêm đoạn code sau cho hàm edit($id) trong file "HocsinhController.php".

public function edit($id)
{
	//Lấy dữ liệu từ Database với các trường được lấy và với điều kiện id = $id
	$getData = DB::table('tbl_hocsinh')->select('id','tenhocsinh','sodienthoai')->where('id',$id)->get();
	
	//Gọi đến file edit.blade.php trong thư mục "resources/views/hocsinh" với giá trị gửi đi tên getHocSinhById = $getData
	return view('hocsinh.edit')->with('getHocSinhById',$getData);
}

Tiếp theo chúng ta tạo file edit.blade.php trong thư mục "resources/views/hocsinh" và chép đoạn code sau vào.

@extends('templates.master')

@section('title','Quản lý học sinh')

@section('content')

<div class="page-header"><h4>Quản lý học sinh</h4></div>

<?php //Hiển thị thông báo thành công?>
@if ( Session::has('success') )
<div class="alert alert-success alert-dismissible" role="alert">
<strong>{{ Session::get('success') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

<?php //Hiển thị thông báo lỗi?>
@if ( Session::has('error') )
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>{{ Session::get('error') }}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
</div>
@endif

<?php //Hiển thị form sửa học sinh?>
<p><a class="btn btn-primary" href="/{{ url('/hocsinh') }}">Về danh sách</a></p>
<div class="col-xs-4 col-xs-offset-4">
<center><h4>Sửa học sinh</h4></center>
<form action="{{ url('/hocsinh/update') }}" method="post">
<input type="hidden" id="_token" name="_token" value="{!! csrf_token() !!}" />
<input type="hidden" id="id" name="id" value="{!! $getHocSinhById[0]->id !!}" />
<div class="form-group">
<label for="tenhocsinh">Tên học sinh</label>
<input type="text" class="form-control" id="tenhocsinh" name="tenhocsinh" placeholder="Tên học sinh" maxlength="255" value="{{ $getHocSinhById[0]->tenhocsinh }}" required />
</div>
<div class="form-group">
<label for="sodienthoai">Số điện thoại</label>
<input type="text" class="form-control" id="sodienthoai" name="sodienthoai" placeholder="Số điện thoại" maxlength="15" value="{{ $getHocSinhById[0]->sodienthoai }}" required />
</div>
<center><button type="submit" class="btn btn-primary">Lưu lại</button></center>
</form>
</div>

@endsection

Tiếp theo chúng ta viết hàm xử lý sửa. Trong file "HocSinhController.php" chúng ta thêm đoạn code sau cho hàm update(Request $request)

public function update(Request $request)
{
	//Cap nhat sua hoc sinh
	//Set timezone
	date_default_timezone_set("Asia/Ho_Chi_Minh");	

	//Thực hiện câu lệnh update với các giá trị $request trả về
	$updateData = DB::table('tbl_hocsinh')->where('id', $request->id)->update([
		'tenhocsinh' => $request->tenhocsinh,
		'sodienthoai' => $request->sodienthoai,
		'updated_at' => date('Y-m-d H:i:s')
	]);
	
	//Kiểm tra lệnh update để trả về một thông báo
	if ($updateData) {
		Session::flash('success', 'Sửa học sinh thành công!');
	}else {                        
		Session::flash('error', 'Sửa thất bại!');
	}
	
	//Thực hiện chuyển trang
	return redirect('hocsinh');
}

Vậy là chúng ta đã hoàn thành chức năng sửa học sinh.

Bước 5: Thực hiện chức năng xóa học sinh.

Trong bước 1 chúng ta đã khai báo link xóa trong file web.php là:

Route::get('hocsinh/{id}/delete', 'HocsinhController@destroy');

+ {id}: là giá trị được gửi theo phương thức get có tên biến là id. Và ở bước 3 biến id được gán bằng giá trị id từ bảng dữ liệu.

+ 'HocsinhController@destroy': Gọi đến hàm destroy trong file "HocsinhController.php"

Chúng ta thêm đoạn code sau cho hàm destroy($id) trong file "HocsinhController.php".

public function destroy($id)
{
	//Xoa hoc sinh
	//Thực hiện câu lệnh xóa với giá trị id = $id trả về
	$deleteData = DB::table('tbl_hocsinh')->where('id', '=', $id)->delete();
	
	//Kiểm tra lệnh delete để trả về một thông báo
	if ($deleteData) {
		Session::flash('success', 'Xóa học sinh thành công!');
	}else {                        
		Session::flash('error', 'Xóa thất bại!');
	}
	
	//Thực hiện chuyển trang
	return redirect('hocsinh');
}

Tới đây chúng ta đã hoàn thành chức năng xóa học sinh.

Qua bài này hi vọng các bạn có thể hiểu cấu trúc lập trình cơ bản với laravel và có thể thực hiện dự án php nhỏ của mình.

Chúc các bạn thành công!

Ngụy Kim Hưng

Cách cài đặt Laravel 5.5

Laravel

Laravel là một framework PHP miễn phí được sử dụng phổ biến trong cộng đồng PHP. Laravel giúp giảm bớt khối lượng công việc trong lập trình, giúp xây dựng ứng dụng nhanh hơn. Laravel được tạo ra bởi Taylor Otwell (một lập trình viên kỳ cựu chuyển từ .NET sang PHP) giúp xây dựng những ứng dụng theo mô hình MVC (Model – View – Controller), hệ thống tài liệu đầy đủ giúp người mới lập trình PHP dễ dàng tiếp cận.

Trong bài này tôi sẽ hướng dẫn các bạn cách cài đặt Laravel trên máy tính cá nhân. Như các bạn lập trình PHP đã biết, để chạy một ứng dụng web với địa chỉ ip http://127.0.0.1 hay http://localhost chúng ta cần cài đặt một máy chủ trên Windows: VertrigoServ, Xampp,...

Để cài đặt Laravel trên localhost trước tiên bạn cần phải Download Composer và cài đặt vào PHP localhost.

Sau khi chạy file Composer-Setup.exe các bạn click Next cho tới khi chọn file như hình bên dưới.

Cài đặt Composer

Các bạn chọn php.exe trong thư mục php của máy chủ localhost (máy chủ tôi đang dùng là VertrigoServ) và click Next để cài đặt.

Sau khi cài đặt xong Composer các bạn vào thư mục localhost của máy chủ (đối với VertrigoServ là thư mục www) và click chuột phải chọn Open command như hình bên dưới. Nếu như click chuột phải không thấy các bạn nhấn thêm nút Shift + Click chuột phải sẽ thấy.

Command

Tới đây ứng dụng máy chủ (VertrigoServ) phải chạy mới đảm bảo cài đặt được. Tiếp theo các bạn gõ các lệnh sau:

// 1. Lệnh đầu tiên: Cài đặt Laravel
composer global require "laravel/installer"

// 2. Lệnh thứ hai: Tạo một project mới (từ website cuối cùng trong lệnh là tên của project)
composer create-project --prefer-dist laravel/laravel website

Cuối cùng các bạn chép 2 file .htaccess và index.php từ thư mục public ra ngoài thư mục tổng (thư mục website) sau đó sửa 2 dòng trong file index.php như sau:

// 1. Dòng đầu tiên:
require __DIR__.'/../bootstrap/autoload.php';
// Sửa lại thành
require __DIR__.'/bootstrap/autoload.php';

// 2. Dòng thứ hai: 
$app = require_once __DIR__.'/../bootstrap/app.php';
// Sửa lại thành
$app = require_once __DIR__.'/bootstrap/app.php';

Vậy là đã hoàn thành cài đặt Laravel. Chúc các bạn thực hiện thành công.

Người chia sẻ: Ngụy Kim Hưng