Thumbnail
Category: Laravel

Kinh nghiệm laravel

Date: May 2, 2020
332 views

Contents

[
Hide
]

1. SQL

1.1. skip là OFFSET trong SQL

  • Muốn bỏ qua 5 record đầu tiên thì sử dụng: skip(5)


$products = $art->products->skip(0)->take(10)->get(); //get first 10 rows
$products = $art->products->skip(10)->take(10)->get(); //get next 10 rows

1.2. take là LIMITtrong SQL

  • Muốn lấy 5 phần tử thì sử dụng: take(5)


$products = $art->products->skip(0)->take(10)->get(); //get first 10 rows
$products = $art->products->skip(10)->take(10)->get(); //get next 10 rows

1.3. orderBy là ORDER BY trong SQL

Ví dụ: muốn sắp xếp tăng dần theo cột id


->orderBy('id', 'DESC');

1.4 join – left join


->leftJoin('task', 'task.id', '=', 'event.task_id')->select('event.*', 'event.id as eventID', 'event.description as eventDescription', 'task.*')

1.5 Get Month, Year


->whereYear('date', '=', $year)->whereMonth('date', $month)

1.6 Like


->where('name', 'like', '%abc%')

1.7 Query điều kiện lồng


DB::table('users')->where(function ($query) use ($activated) {
    $query->where('activated', '=', $activated);
})->get();
​
/*
    1. Viết một function trong where hoặc orWhere
    2. Nếu cần sử dụng biến trong function sử dụng thêm từ khóa use sau đó 
    là tên biến
*/

1.8 Cần query từ từ


$dataQuery = AnyEloquent::query();
$dataQuery->where(....);
$dataQuery->orWhere(....);
$dataQuery->get();
/*
    1. Dùng eloquent trỏ đến phương thức query()
    2. Sử dụng theo cấu trúc eloquent như bình thường
*/

2. insert, update, delete, select dữ liệu

2.1 insert

Sử dụng hàm save() để lưu vào CSDL.


public function themBoMon($request)
{
    $kt = new BomonModel();
    $kt->MaBoMon = $$request->maBoMon;
    $kt->TenBoMon = $$request->tenBoMon;
    $kt->save();
    return true;
}

2.2 Update

Lấy đối tượng đó ra, rồi update dữ liệu trường thay đổi. Sử dụng save() để lưu vào CSDL.


public function suaBoMon($maBM, $request)
{
    $kt = BomonModel::where('MaBoMon', '=', $maBM)->first();
    $kt->TenBoMon = $request->tenBoMon;
    $kt->save();
}

2.3 Delete

Sử dụng hàm delete() để xóa CSDL.


public function xoaBoMon($maBM)
{
    $kt = new BomonModel();
    $kt->where('MaBoMon', '=', $maBM)->delete();
}

2.4 Select

Khi Select dữ liệu nên chuyển sang dạng mảng để dễ xử lý, và khi trả về mảng bạn lưu ý phải thêm vị trí của phần tử cần lấy vì nó trả về mảng 2 chiều.


//sau khi select nhớ toArray
$data = $this->user->where('email', '=', $email)->get()->toArray();
​
//láy tên của phần tử đầu tiên
$khach->setData($data[0]["hoTen"],

Kết các bảng lại với nhau:


$kt = new CoVanModel();
$kq = $kt->join('bomon', 'bomon.MaBoMon', '=', 'CoVanHocTap.MaBoMon')->select('MaCV','HoTen_CV','SDT_CV','TenBoMon')->get()->toArray();
/*
    Kết bảng CoVanHocTap với Bảng bomon qua trường MaBoMon
*/

2.5 try catch bắt lỗi truy vẫn


try { 
  //code
  return true;
} catch(\Illuminate\Database\QueryException $ex){ 
  //code
  return false;
}

2.6 Xử lý lỗi khi sử dụng Eloquent

Nhiều khi Eloquent lỗi insert, update, dữ liệu. (như illegal offset type chẳng hạn). Có thể do bảng có 2 khóa chính,… Thay vì ngồi sửa thì hãy sử dụng lớp DB có sẵn của Laravel.

Đầu tiên import này vào


use Illuminate\Support\Facades\DB;

2.6.1 Insert


DB::table('users')->insert(
    ['email' => 'john@example.com', 'votes' => 0]
);

Tham khảo thêm tại: https://laravel.com/docs/8.x/queries

2.7 Tồn tại


public function tonTai($maBm)
{
  $kt = new BomonModel();
  $kq = $kt->where('MaBoMon', '=', $maBm)->get()->count();
  if($kq>0)
    return true;
  else return false;
}

2.8 Get một col trong dữ liệu trả về


public function getIDByCode($code) {
  $item = Order::where("code", "=", $code)->get()->toArray();
  if(count($item) > 0)
    return $item[0]["id"];
  else return -1;
}

2.9 Join bảng


public function getAllLopAndInfo()
{
  $kt = new LopModel();
  $kq = $kt->join('bomon', 'bomon.MaBoMon', '=', 'lop.MaBoMon')->join('covanhoctap', 'covanhoctap.MaCV', '=', 'lop.MaCV')->get()->toArray();
  return $kq;
}
​
public function getAllThongTinLop()
{
  $kt = new LopModel();
  $kq = $kt->join('bomon', 'bomon.MaBoMon', '=', 'lop.MaBoMon')->join('covanhoctap', 'covanhoctap.MaCV', '=', 'lop.MaCV')->select('MaLop', 'TenLop', 'EmailLop', 'HoTen_CV', 'SDT_CV', 'Email_CV', 'TenBoMon')->get()->toArray();
  return $kq;
}

2.10 Validate Custom


$validation = Validator::make(
  $request->all(),
  [
    'title' => 'required|string|max:50',
  ],
  [
    'title.required' => trans('validation.required_custom', ['attribute' => trans('table.room.title')]),
    'title.max' => trans('validation.max.string', ['attribute' => trans('table.room.title')]),
  ]
);
​
$validation->after(
  //Check accept is_accept_policy
  function ($validation) use ($request) {
    if ($request->title === 'Test') {
      $validation->errors()->add('title', trans('validation.title_equal_test'));
    }
  }
);
​
 if ($validation->fails()) {
   return $this->jsonValidate($validation->errors());
 }

3. Sử dụng php

3.1 Sử dụng class trong php

Tôi bị lỗi này suốt mấy tháng mà không tìm ra, giờ chia sẻ lại cho các bạn kinh nghiệm.

  • Khi gọi thuộc tính của class thì đừng cho biến $ vào tên biến


$this->$thuocTinh;  //sai
$this->thuoctinh; //đúng
  • Nếu không new ra đối tượng được khi khai báo biến thì hãy bỏ nó vào contructor


//không được
class GuestController extends Controller
{
    private $thongbao_table = new ThongBaoModel();
 }
  
 //thì làm như thế này
class GuestController extends Controller
{
    private $thongbao_table;
    
    public function __construct() {
        $this->thongbao_table = new ThongBaoModel();
    }
}

3.2 Sử dụng PHP

  • Tìm chuỗi trong chuỗi


strpos($chuoi, $tuCanTim)
  
//syntax
strpos(string, find, start)
    //string:   Required. Specifies the string to search
    //find: Required. Specifies the string to find
    //start:    Optional. Specifies where to begin the search. If start is a negative number, it counts from the end of the string.
  
//Returns the position of the first occurrence of a string inside another string,
 //or FALSE if the string is not found. Note: String positions start at 0, and not 1.
  • Constructor trong kế thừa: lớp con kế thừa lớp cha muốn sử dụng lại constructor của lớp cha phải thêm lệnh dưới.


public function __construct() {
  parent::__construct();
}

3.3 PHP & Javascript


//$info là biến của php
//Cần đổ dữ liệu $info sang javascript làm như bên dưới
@if( isset($info) )
    <script>
         alert("{{ $info }}");
    </script>
@endif

4. Model

Các phần phải có trong model. Biến $fillable là các trường được phép chỉnh sửa dữ liệu.


class ThongBaoModel extends Model
{
    protected $table = 'thong-bao';
    protected $fillable = ['img', 'title', 'content', 'quote'];
    public $timestamps = false;
​
}

Nếu bảng khóa chính không phải là id

  • Thì thêm các thuộc tính sau vào model của bảng đó


    protected $primaryKey = "MaBoMon";  //trường khóa chính
    protected $keyType = 'string'; //kieur dữ liệu của trường đó

5. Truyền dữ liệu sang view

Hãy sử dụng compact thì khi truyền sang tên biến sẽ không bị thay đổi tên. Tức là truyền qua tên nào thì ở view lấy tên đó xài luôn. Và nó có thể truyền nhiều biến nữa.


$ds = new DanhSachController();
$data = $ds->getDanhSach();
​
$bm = new BomonModel(); 
$boMon = $bm->getAllBoMon();
​
$lp = new LopModel(); 
$lop = $lp->getAllLop();
​
return view('admin.trang-chu')->with( compact('data', 'boMon', 'lop') );

6. Middleware


class AdminMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if($request->session()->has('user') && $request->session()->get('user') == 'admin')
        {
            return $next($request);
        }
        else return redirect("/2");
    }
}

Đăng ký Kernel


protected $routeMiddleware = [
        //'auth' => \App\Http\Middleware\Authenticate::class,
        //'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        //'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
        //'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        //'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'admin' => \App\Http\Middleware\AdminMiddleware::class,
    ];

7. Seeder – dữ liệu mẫu

7.1 Cách tạo dữ liệu mẫu

Có bao nhiêu bảng thì tạo bấy nhiêu cái DB::table(”)->insert();


use DB;
​
class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
       DB::table('bomon')->insert([
          array('MaBoMon'=>'BM01','TenBoMon'=>'Xây dựng'),
          array('MaBoMon'=>'BM02','TenBoMon'=>'Công nghệ thông tin'),
          array('MaBoMon'=>'BM03','TenBoMon'=>'Điện - Điện tử'),
          array('MaBoMon'=>'BM04','TenBoMon'=>'Cơ khí động lực'),
        ]);
      
       DB::table('user')->insert([
          array('username'=>'admin','password'=>'123456')
        ]);
    }
}

7.2 Insert dữ liệu kiểu date


DB::table('phieucham')->insert([
  array('maPhieuCham' => '01', 
        'diem' => 10, 
        'ngayCham' => '2018-09-05', 
        'nhanXet' => 'Hoàn thành tốt, có trách nhiệm'),
]);
​
//  'năm-tháng-ngày'

7.3 Sử dụng factory

Tạo factory


php artisan make:factory TenFactory
​
// ví dụ
  php artisan make:factory PostFactory

Định nghĩa file factory


<?php
​
/** @var \Illuminate\Database\Eloquent\Factory $factory */
​
use App\Post;
use Faker\Generator as Faker;
​
$factory->define(Post::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence(3),
        'content' => $faker->paragraphs(10, true),
        'lead' => $faker->text(200),
        'topic_id' => random_int(1, 20),
        'author_id' => random_int(1, 20),
    ];
});

Sử dụng trong seeder


<?php
​
use Illuminate\Database\Seeder;
use App\Post;
​
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        factory(Post::class, 20)->create();
    }
}

8. Migration

8.1 Thứ tự tạo Migration

Migration sẽ chạy theo thứ tự tạo, nên cái nào không có khóa ngoại tạo trước, cái nào có khóa ngoại tạo sau nha.

Nếu đã lỡ xa chân vào lỗi không thực hiện như trên thì.

  1. Xóa file migrate mà có khóa ngoại tạo trước đi migration không có khóa ngoại đi, tạo migration có khóa ngoại lại.
  2. Xóa toàn bộ bảng trong database
  3. Mẹo xóa nhanh: php artisan migrate:reset
  4. localhost/phpmyadmin => xóa 1,2 bảng nữa là ok.
  5. Chạy php artisan migrate

Nếu vẫn chưa được chạy cmd: composer dump-autoload

8.3 Tạo bảng không bị lỗi khóa ngoại

Do khóa ngoại sẽ tham chiếu đến khóa chính. Nên bảng cần tham chiếu khóa chính nên thêm thuộc tính unique().


Schema::create('hocvi', function (Blueprint $table) {
     $table->bigInteger('maHocVi')->unique();
     $table->string('tenHocVi');
     $table->timestamps();
});
  • Tạo khóa ngoại


public function up()
{
    Schema::create('lop', function(Blueprint $table)
    {
        //$table->String('MaLop', 10)->unique();
        //$table->String('TenLop', 70);
        //$table->String('EmailLop', 30);
        //$table->String('MaBoMon', 10);
        //$table->String('MaCV', 10);
        $table->foreign('MaBoMon')->references('MaBoMon')->on('bomon')->onDelete('cascade');
    });
}
​
//$table->foreign('CotBangHienTai')->references('CotBangForeign')->on('TenBangForeign')->onDelete('cascade');

8.4 Câu lệnh nên dùng khi tạo migration

Nên tạo bằng câu lệnh này để nó tạo luôn khai báo tên bảng


php artisan make:migration Ten_Migration --create=ten_bang

8.5 Nội dung file migration

  • Function up


public function up()
{
    Schema::create('lop', function(Blueprint $table)
    {
        $table->String('MaLop', 10)->unique();
        $table->String('TenLop', 70);
        $table->String('EmailLop', 30);
        $table->String('MaBoMon', 10);
        $table->String('MaCV', 10);
    });
}
  • Function down


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

Hàm Schema

  • Tạo bảng


//tạo bảng
Schema::create('categories', function (Blueprint $table) {
//
}
​
//sửa bảng
Schema::table('categories', function (Blueprint $table) {
//
}
 
//đổi tên bảng
Schema::rename('category', 'categories');
               
//Xóa bảng
Schema::dropIfExists('categories');

8.6 Khóa chính là kiểu string

Nếu khóa chính là kiểu String thì phải khai báo độ dài cho khóa chính và cho cả các khóa ngoại tham chiếu tới nó.

8.7 Tạo Foreign Key cho database


<?php
​
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
​
class AddForeignKeysToPostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->unsignedBigInteger('topic_id')->index()->nullable();
            $table->foreign('topic_id')->references('id')->on('topics');
            
            $table->unsignedBigInteger('author_id')->index()->nullable();
            $table->foreign('author_id')->references('id')->on('users');
        });
    }
​
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropForeign(['topic_id']);
            $table->dropColumn('topic_id');
​
            $table->dropForeign(['author_id']);
            $table->dropColumn('author_id');
        });
    }
}
​

9. Kiểm tra dữ liệu bằng tinker

Ở thư mục gốc => cmd


php artisan tinker

Nhập câu lệnh và sẽ trả kết quả về cho bạn

10. Controller

10.1 Request

Request gửi tới nhưng bạn không muốn xử lý tại function đó mà muốn chuyển request đó sang funtion khác để xử lý thì làm như bên dưới nhé! Request bỏ zô một biến rồi truyền sang.


<?php
​
public function veryComplexMethod(Request $request)
{
    $data = $request->all();
    return $this->veryComplexMethodProcessing($data);
}
​
public function veryComplexMethodProcessing($data)
{
    //process data....
    return $answer;
}

10.2 Upload file


$fileName = "";
$fileSize = "";
if ($rq->hasFile('fileToUpload')) {
  $file = $rq->fileToUpload;
​
  //Lấy Tên files
  $fileName = $file->getClientOriginalName();
​
  //Lấy kích cỡ của file đơn vị tính theo bytes
  $fileSize = $file->getSize();
}
​
if($fileName != "" && $fileSize < 10485760)
{
  $file = $rq->fileToUpload;
  $fileName = url("public/img/upload/".$fileName);
  $file->move("public/img/upload", $file->getClientOriginalName());
  //tham số đầu tiên là đường dẫn thư mục server, lưu ý không sử dụng asset ở đây
}
else
{
    //code
}

Tham khảo thêm tại: https://www.techblog.vn/bai-19-upload-files-trong-laravel

11. View

11.1 Bỏ tham số vào href, action hoặc src


<form action=" {{ asset('thong-tin-can-bo') }}/{{ $email }}" method="post">

12. Authentication

Bài viết này https://agitech.com.vn/vn/bai-viet/website/77-lap-trinh-laravel-dang-nhap-va-phan-quyen có trình bày rõ về cách hoạt động và code của Authentication Laravel. Các bạn xem và tham khảo nó nhé.

13. Hàm chuyển kiểu Date

Ví dụ: Chuỗi “25122020” => “2020-12-25”


private function convertDate($date) {
  $date_return = substr($date,4,4);
  $date_return .= substr($date,2,2);
  $date_return .= substr($date,0,2);
  return date("Y-m-d", strtotime($date_return));
}

14. Route

14.1 Group


Route::group([
  'prefix' => 'bao-hiem-vien-phi',
  'namespace'=>'Api'
], function () { 
  Route::post('/add', 'BaoHiemVienPhiController@themBaoHiemVienPhi');
  Route::post('/addPerson', 'BaoHiemVienPhiController@themNguoiDuocBaoHiemVienPhi');
  Route::put('/edit', 'BaoHiemVienPhiController@editBaoHiemVienPhi');
  Route::get('/cal-fee/{type}/{data}', 'BaoHiemVienPhiController@tinhPhi');
});

14.2 Tạo Route Controller đi vào thêm một cấp thư mục

Thêm vào namespace


Route::group([
  'prefix' => 'bao-hiem-vien-phi',
  'namespace'=>'Api'
], function () { 
  Route::post('/add', 'BaoHiemVienPhiController@themBaoHiemVienPhi');
  Route::post('/addPerson', 'BaoHiemVienPhiController@themNguoiDuocBaoHiemVienPhi');
  Route::put('/edit', 'BaoHiemVienPhiController@editBaoHiemVienPhi');
  Route::get('/cal-fee/{type}/{data}', 'BaoHiemVienPhiController@tinhPhi');
});

14.3 Namespace route


Route::namespace('App\Http\Controllers\api')->group(function () {
    Route::group(['middleware' => 'auth:sanctum'], function() {
        Route::post('create-event', 'CalendarController@createEvent');
        Route::delete('delete-event/{id}', 'CalendarController@deleteEvent');
        Route::get('event/{date}', 'CalendarController@getEvent');
        Route::get('event-single/{id}', 'CalendarController@getSingleEvent');
        Route::post('update-event/{id}', 'CalendarController@updateEvent');
        Route::post('create-task', 'TaskController@createTask');
        Route::post('change-color-task/{id}', 'TaskController@updateColorTask');
        Route::delete('delete-task/{id}', 'TaskController@deleteTask');
        Route::get('task', 'TaskController@getAllTask');
    }); 
    Route::post('login', 'UserController@login');  
});

14.3 Middleware


Route::group(['prefix'=>'admin', 'middleware' => 'admin'], function () {
    Route::get('home/{id?}', 'AdminController@getTrangChu');
});

15. Kết nối với vuejs

15.1 Thiết lệp webpack.mix.js


const mix = require('laravel-mix');
​
/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel applications. By default, we are compiling the CSS
 | file for the application as well as bundling up all the JS files.
 |
 */
​
mix.js('resources/js/app.js', 'public/js').vue()
    .sass('resources/css/app.scss', 'public/css');

15.2 Template kết nối vuejs


<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <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">
        <link rel="icon" href="{{ asset('/img/calendar_logo_icon.ico') }}">
        <link rel="stylesheet" href="{{ asset('/css/reset.css') }}">
        <link rel="stylesheet" href="{{ asset('/css/app.css') }}">
        <!-- <link rel="stylesheet" href="./css/bootstrap.min.css"> -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" />
​
        <title>Calendar</title>
    </head>
    <body>
        <div id="app">
        </div>
         <script>
           window.Laravel = <?php echo json_encode([
               'csrfToken' => csrf_token(),
                    ]); ?>
          </script>
        
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="{{ mix('js/app.js') }}"></script>
    </body>
</html>
​

15.3 install vue and vue router


npm install --save vue vue-router

15.4 install mix


npm install laravel-mix@latest --save-dev

15.5 app.js


import Vue from 'vue'
import App from './App.vue'
​
Vue.config.productionTip = false;
​
new Vue({
    render: h => h(App)
  }).$mount('#app')

15.6 App.vue component


<template>
    <div>
        abc
    </div>
</template>
​
<script>
    export default {
        name: 'App'
    }
</script>

15.7 Chạy lệnh “npm run watch”

Chạy lệnh:


npm run watch

Trường hợp gặp lỗi:

Cannot find module ‘webpack/lib/rules/DescriptionDataMatcherRulePlugin’ Require stack:

Hãy chạy lệnh dưới


npm update vue-loader
​
// nếu chưa install chạy lệnh dưới
npm i vue-loader

Nếu gặp lỗi hãy thay phần script ở file package.json:


// before
"scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "npm run development -- --watch",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
​
​
// after
"scripts": {
    "dev": "npm run development",
    "development": "mix",
    "watch": "mix watch",
    "watch-poll": "mix watch -- --watch-options-poll=1000",
    "hot": "mix watch --hot",
    "prod": "npm run production",
    "production": "mix --production"
},

15.8 Lệnh build file ngôn ngữ của laravel sang vuejs


php artisan vue-i18n:generate

16. Token Api sanctum

Tạo API và Authenticate nhanh chóng với package Laravel Sanctum

17. Password Hash


//import hash
use Illuminate\Support\Facades\Hash;
​
//hash
Hash::make('chuoiPassword');
​
//check
Hash::check('chuoiPassword', 'Chuoi hash lưu trong database')

18. Run project

Mặc định laravel sẽ run project ở cổng 8000.

Để run mặc định bạn chạy lệnh sau:


php artisan serve

Để thay đổi cổng theo ý muốn bạn sử dụng lệnh sau:


php artisan serve --port=...   // ... thay thanh port (8001, 8002, ...) 

19. Phân trang laravel


$membe = Member::->where('winner', 1)->orderBy('created_at', 'DESC')->paginate(10);


<div class="ot-pagination clearfix">
    <div class="text-center">
            <!-- Previous Page Link -->
        <a href="{{$memberExams->onFirstPage() ? 'javascript:void(0)' : $memberExams->previousPageUrl()}}" rel="prev"><strong class="prev active"></strong></a>
        <!-- Element Page Link -->
        @for($page = 1; $page <= $memberExams->lastPage(); $page++)
            @if(($memberExams->currentPage() == 1 && $page <= $memberExams->currentPage() + 4) || ($memberExams->currentPage() == 2 && $page <= $memberExams->currentPage() + 3) || (($memberExams->currentPage() == $memberExams->lastPage() || $memberExams->currentPage() == $memberExams->lastPage() - 1) && ($page - 3 >= 1 || $page - 4 >= 1)) || ($page >= $memberExams->currentPage() - 2 && $page <= $memberExams->currentPage() + 2))
                <strong>
                    <a href="{{$memberExams->url($page)}}" 
                    class="page-link {{$page == $memberExams->currentPage() ? 'active' : ''}}">
                        <span>{{$page}}</span>
                    </a>
                </strong>
        @endif
        @endfor
        <!-- Next Page Link -->
        <a href="{{$memberExams->hasMorePages() ? $memberExams->nextPageUrl() : 'javascript:void(0)'}}" rel="next"><strong class="next active"></strong></a>
    </div>
</div>

19.1 Cần chỉnh sửa rồi mới paginate


use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;


$member = Member::->where('winner', 1)->orderBy('created_at', 'DESC')->get();


public function paginate($items, $perPage = 5, $page = null, $options = [])
{
    $page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
    $items = $items instanceof Collection ? $items : Collection::make($items);
    return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, ['path'=>url('thi-thu-nhan-qua')]);
}

20. Giữ giá trị ô input blade template – old input (Retrieving old input)


<input type="text" name="username" value="{{ old('username') }}">


public function post(Request $request)
{
    return back()->withInput(
        $request->only('username')
    );
}

Tham khảo: https://viblo.asia/p/tap-16-request-laravel-aWj534Y8K6m  mục 8c

21. Validate số điện thoại


'phone' => ['required', 'max:191', 'regex:((09|03|07|08|05)+([0-9]{8})\b)'],

22. Thực tập – Replace content – keyword


//https://regex101.com/r/cD8nG4/1
$content = preg_replace(
    "/<a[\S\s]+?<\/a>(*SKIP)(*FAIL)|<img[\S\s]+?\>(*SKIP)(*FAIL)|($keyword)/i",
    $linkReplace,
    $content,
    $limit - $keywordLinkCount,
    $count
);

23. Các khái niệm học thêm được

  • soft-delete: thêm field deleted_at để khi xóa chỉ thay đổi trạng thái mà không xóa hẳn khỏi database
  • Relationship:
  • – One to one: 1-1
  • – One to many: 1- n
  • n – 1 => 1 – n ((gọi là inverse relationship sử dụng belongTo)
  • Accessors: định dạng dữ liệu lấy từ db trước khi sử dụng

  • Mutators: định dạng dữ liệu trước khi cập nhật vào db.
  • Query Scope – global scope: add 1 truy vấn vào tất cả các truy vấn.
  • VD: soft-delete thay vì lúc nào cũng -> where(‘deleted_at’, null) thì sử dụng global scope sẽ không cần phải thêm câu lệnh đó vào truy vấn mà nó tự động thêm vào.

23.1 Accessors

Giả sử, bảng User có field first_name và last_name.

Bên ngoài hiển thị cần hiển thị dạng full_name (first_name + space + last_name).

Trong Model User viết hàm như sau:


public function getFullNameAttribute() // get[property_name]Attribute
{
    return $this->first_name . " " . $this->last_name;
}

Khi cần sử dụng full_name, thì sử dụng nó như thuộc tính bình thường


$data = \App\Models\User::all();
foreach($data as $item) {
  echo $item->full_name . "<br />";  // get full_name như thuộc tính
}

23.2 Mutators

Giả sử, bảng User có field first_name và last_name.

Khi insert hoặc update vào database thì in hoa first_name rồi mới insert vào.

Trong model User:


public function setCompanyNameAttribute($value)   // camelCase => function set[property_name]Attribute
{
    $this->attributes['company_name'] = strtoupper($value);
}

Khi đó, insert và update như bình thường, nó sẽ apply function đó vào thuộc tính first_name.


$item = new \App\Models\User();
$item->first_name = "Do";  // => Database sẽ lưu => DO
$item->last_name = "Hao";
$item->save();

23.3 Scope

23.3.1 Global scope

Apply cho toàn bộ query của eloquent.

Giả sử, bảng User có field role.

Khi truy vấn bạn muốn toàn bộ query không lấy giá trị null của role

Trong model User:


// nhớ import Builder
use Illuminate\Database\Eloquent\Builder;
​
...
protected static function booted()
{
  static::addGlobalScope('filterRole', function (Builder $builder) { // filterRole => ten global scope
    $builder->whereNotNull('role'); // thay thành câu điều kiện cho toàn bộ truy vấn
  });
}

Nếu một truy vấn nào đó không muốn apply global scope vào


// Xóa một global scope
User::withoutGlobalScope('role')->get(); // thay role thành tên global scope cần xóa
​
// Xóa nhiều global scope
  // Xóa toàn bộ
  User::withoutGlobalScopes()->get();
​
  // Xóa một vài global scope
  User::withoutGlobalScopes([
      FirstScope::class, SecondScope::class
  ])->get();

23.3.2 Local Scope

Apply cho một query nào đó của eloquent.

Giả sử, bảng User có field role.

Câu truy vấn bạn muốn query lấy role >= 2 (Và câu điều kiện này sử dụng ở nhiều nơi)

Trong model User:


public function scopeActive($query) // scope[scopeName]
{
  return $query->where('role', '>=', 2);
}

Khi sử dụng.


$data = \App\Models\User::active()->get(); // gọi scopeName
Sử dụng local scope với tham số:

Giả sử bạn muốn truyền tham số để query role, có thể thay đổi linh hoạt hơn.

Trong User model:


public function scopeActive($query, $roleUp) // Thêm param sau $query để sử dụng
{
  return $query->where('role', '>=', $roleUp);
}

Khi sử dụng:


$data = \App\Models\User::active(3)->get();  // truyển param vào scopeName

23.4 Relationship

23.4.1 One has many

Lưu ý:
+ Relationship dựa trên tên model
+ Khóa ngoại được xác định bằng cách: ten_model_id (tên model dạng snakeCase).
+ Nếu khóa ngoại không như cấu trúc trên, sử dụng tham số thứ 2 sau hasOne để xác định khóa ngoại của bảng cần tham chiếu
+ Tương tự khóa chính là id, nếu khác sử dụng tham số thứ 2 để xác định id của bảng cần tham chiếu
Ví dụ: Post và Comment

Vd1: Bảng posts (Model: Post) và comments (Model: Comment).

Post hasOne Comment => function sẽ có s phía sau tên (tên function là tên model liên kết dạng snakeCase) comments

Comment belongTo => function sẽ không có s phía sau tên (tên function là tên model liên kết dạng snakeCase) post

Lưu ý: Relationship chỉ dựa vào tên model.


<?php
​
namespace App;
​
use Illuminate\Database\Eloquent\Model;
​
class Post extends Model
{
    /**
     * Get the comments for the blog post.
     */
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}


<?php
​
namespace App;
​
use Illuminate\Database\Eloquent\Model;
​
class Comment extends Model
{
    /**
     * Get the post that owns the comment.
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}
Ví dụ: MailTemplate và SendmailCvSetting

Ví dụ 2: cho bảng có tên dài. Có bảng

mail_templates => Model: MailTemplate

và bảng

sendmail_cv_settings => Model: SendmailCvSetting

Lưu ý: Relationship chỉ dựa vào tên model.

MailTemplate hasOne SendmailCvSetting=> function sẽ có s phía sau tên (tên function là tên model liên kết dạng snakeCase) sendmailCvSettings

SendmailCvSetting belongTo => function sẽ không có s phía sau tên (tên function là tên model liên kết dạng snakeCase) mailTemplate


class MailTemplate extends BaseModel
{
    //Declare table name
    protected $table = 'mail_templates';
    use SoftDeletes;
    protected $fillable = ['id'];
  
    public function sendmailCvSettings()
    {
        return $this->hasMany(\App\Models\SendmailCvSetting::class, 'mail_template_id');
    }
}


class SendmailCvSetting extends BaseModel
{
    //Declare table name
    protected $table = 'sendmail_cv_settings';
    use SoftDeletes;
    protected $fillable = ['id'];
​
    public function mailTemplate()
    {
        return $this->belongsTo(\App\Models\MailTemplate::class);
    }
​
 }
Sử dụng


$sendmailCvData = SendmailCvSetting::query();
$sendmailCvData->with('mailTemplate');
​
// sử dụng with, param là tên model tham chiếu.
   //=> data khi query sẽ có mục relation chứa data tham chiếu.

24. Gửi mail


let param = {
    YOUR_EMAIL: 'your@gmail.com',
    YOUR_PASS: 'apppassword', 
    ADDRESS_FROM_USER_SEE: 'your@gmail.com', 
    NAME_USER_SEE: 'your name', 
    TEMPLATE_MAIL: 'emails.create-job', // file blade trong resource/view
    SUBJECT_TEXT: 'Send mail from ....',
    EMAIL_SEND: [
      'abc@gmail.com'
    ]
};
​
console.log('Cấu hình .env');
let data = `
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=YOUR_EMAIL
MAIL_PASSWORD=YOUR_PASS
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=ADDRESS_FROM_USER_SEE
MAIL_FROM_NAME="NAME_USER_SEE"
`;
data = data.replaceAll('YOUR_EMAIL', param.YOUR_EMAIL);
data = data.replaceAll('YOUR_PASS', param.YOUR_PASS);
data = data.replaceAll('ADDRESS_FROM_USER_SEE', param.ADDRESS_FROM_USER_SEE);
data = data.replaceAll('NAME_USER_SEE', param.NAME_USER_SEE);
console.log(data);
​
console.log('Tại nơi cần xử lý gửi mail');
console.log('use Mail;');
​
data = `
Mail::send('TEMPLATE_MAIL', [
    // 'name' => 'admin',
    // 'clientJob' => $clientJob,
    // 'clientUser' => $clientUser,
    // 'client' => $client,
    // 'dataJoin' => (object) $dataJoin
​
  // add variable vào để sử dụng trong view
], function($message) /* use ($clientJob, $clientUser)  => Phần này khai báo các biến sử dụng cho function ở dưới*/ {`
    
  param.EMAIL_SEND.forEach( (item) => {
​
    data += `
    $message->to('${item}')->subject('SUBJECT_TEXT');`;
  });
data += `
});
`;
​
data = data.replaceAll('TEMPLATE_MAIL', param.TEMPLATE_MAIL);
data = data.replaceAll('SUBJECT_TEXT', param.SUBJECT_TEXT);
​
console.log(data);

Xem thêm: https://viblo.asia/p/huong-dan-gui-mail-voi-laravel-5-XQZkxZEbGwA

25. Resize image when upload


// $item->thumbnail: File upload gửi lên server
$pathImage = 'uploads/cv-sample/images';
​
$resourceType = imagecreatefrompng($item->thumbnail);
$sourceProperties = getimagesize($item->thumbnail);
$sourceImageWidth = $sourceProperties[0];
$sourceImageHeight = $sourceProperties[1];
$newWidth = 224;
$newHeight = 316;
$imageLayer = ImageService::resizeImage($resourceType, $sourceImageWidth, $sourceImageHeight, $newWidth, $newHeight);
imagepng($imageLayer,  $pathImage . '/' . $newSample->id . '.png');
​


public static function resizeImage($resourceType, $image_width, $image_height, $resizeWidth, $resizeHeight)
    {
        // $resizeWidth = 100;
        // $resizeHeight = 100;
        $imageLayer = imagecreatetruecolor($resizeWidth, $resizeHeight);
        imagecopyresampled($imageLayer, $resourceType, 0, 0, 0, 0, $resizeWidth, $resizeHeight, $image_width, $image_height);
        return $imageLayer;
    }
​
// hàm này t bỏ vô ImageService.php file

Tham khảo: https://www.devopsschool.com/blog/how-to-upload-and-resize-an-image-using-php/

26. Core anh Tân

26.1 Tailwindcss backend không ăn nhập

https://tailwindcss.com/docs/guides/laravel


// Run lệnh
npm install -D tailwindcss postcss autoprefixer
  
// sau đó
npm run dev

26.2 Lỗi Cannot set properties of undefined (setting ‘exclude’) webpack.frontend.mix


1. Vào file webpack.frontend.mix
2. Change
  png|jpe?g|gif|webp => png|jpe?g|gif|webp|avif
3. Chạy lại thử

26.2 Bỏ qua check console.log khi build


Thêm dòng bên dưới ở trên console.log
// eslint-disable-next-line no-console

26.3 Reset lại database


//==== Reset database ====
// 1. Backup db
// 2. Migrate
php artisan migrate
// 3. Seeding
php artisan db:seed
// 4. delete table
delete table generates, auth_clients
// 5. import table từ db cũ
import table generates cũ qua mới
import table auth_clients cũ qua mới

27. Encrypt & Decrypt


use Illuminate\Support\Facades\Crypt;
​
Crypt::encryptString($encryptKey);
Crypt::decryptString($encryptKey);

Khác

Còn nhiều kinh nghiệm ở các bài viết khác:

  • Làm gì với một project mới, các lỗi thường gặp laravel. Link
  • Phân tích project blog. Link
  • Phân tích project website âm nhạc. Link


Copyright © 2025 All Right Reserved