12/08/2018, 16:06

Eloquent trong Laravel 5.3

Xin chào các bạn. Hôm nay mình sẽ tiếp tục về series về Laravel. Và hôm nay mình sẽ giới thiệu với các bạn về Eloquent trong laravel và cách sử dụng Elixir . 1. Giới thiệu Eloquent ORM đi kèm với Laravel cung cấp một API ActiveRecord đơn giản và tiện lợi cho giao tiếp với database. Mỗi database ...

Xin chào các bạn. Hôm nay mình sẽ tiếp tục về series về Laravel. Và hôm nay mình sẽ giới thiệu với các bạn về Eloquent trong laravel và cách sử dụng Elixir .

1. Giới thiệu

Eloquent ORM đi kèm với Laravel cung cấp một API ActiveRecord đơn giản và tiện lợi cho giao tiếp với database. Mỗi database table sẽ có một "Model" tương ứng để tương tác với table đó. Model cho phép bạn query dữ liệu trong table, cũng như chèn thêm các dữ liệu mới.

Trước khi bắt đầu, hãy đảm bảo cấu hình kết nối database trong config/database.php. Để biết thêm thông tin chi tiết cho cấu hình database, hãy xem mục cấu hình database.

2. Tạo model

Để bắt đầu, hãy cùng tạo một Eloquent model. Model về cơ bản nằm trong thư mục app, nhưng bạn có thể tuỳ ý đặt chúng ở bất cứ đâu mà được cấu hình autoload trong composer.json. Tất cả các Eloquent model đều kế thừa từ class IlluminateDatabaseEloquentModel.

Cách đơn giản nhất để tạo một model là sử dụng câu lệnh Artisan make:model:

php artisan make:model User

Nếu bạn muốn tạo migration đi kèm với model khi tạo thì sử dụng thêm cờ --migration hay -m:

php artisan make:model User --migration

php artisan make:model User -m

Quy tắc cho Eloquent Model

Bây giờ, hãy cùng nhau coi ví dụ về class model Flight, mà chúng ta sẽ dùng để lấy và lưu thông tin vào trong bảng flights:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
  //
}

Tên table

Để ý là chúng ta không hề cho Eloquent biết là bảng nào được sử dụng cho model Flight. Kiểu "snake case", tên class ở số nhiều sẽ được sử dụng như tên table trừ khi có một tên khác được khai báo. Vì thế, trong trường hợp này, Eloquent sẽ coi model Flight lưu dữ liệu vào trong table flights. Bạn có thể chỉ định tên table khác cho model bằng cách khai báo thuộc tình $table:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
  /**
   * The table associated with the model.
   *
   * @var string
   */
  protected $table = 'my_flights';
}

Timestamps

Mặc định, Eloquent cần hai column created_at và updated_at có mặt trong các table. Nếu bạn không muốn những columns này tự động được quản lý bởi Eloquent, thiết lập thuộc tinh $$imestamps thành false:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
  /**
   * Indicates if the model should be timestamped.
   *
   * @var bool
   */
  public $timestamps = false;
}

Nếu bạn muốn thay đổi định dạng của timestamp, thiết lập vào thuộc tính $dateFormat trong model. Thuộc tính này xác định cách mà các thuộc tính kiểu date được lưu trong database cũng như cách format khi được serialize thành array hay JSON:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
   /**
    * The storage format of the model's date columns.
    *
    * @var string
    */
   protected $dateFormat = 'U';
}

Kết nối database

Tất cả các Eloquent model sẽ sử dụng kết nối database mặc định được cấu hình. Nếu bạn muốn sử dụng một kết nối khác cho model, sử dụng thuộc tính $connection:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
   /**
    * The connection name for the model.
    *
    * @var string
    */
   protected $connection = 'connection-name';
}

3. Lấy nhiều model kết quả

Khi bạn đã tạo được một model và table tương ứng của nó, bạn có thể sẵn sàng truy xuất dữ liệu từ database. Hãy coi mỗi Eloquent model như một query builder mạnh mẽ cho phép bạn thực hiện query tới database một cách liền mạch. Ví dụ:

<?php

namespace AppHttpControllers;

use AppFlight;
use AppHttpControllersController;

class FlightController extends Controller
{
 /**
  * Show a list of all available flights.
  *
  * @return Response
  */
 public function index()
 {
     $flights = Flight::all();

     return view('flight.index', ['flights' => $flights]);
 }
}

Truy xuất giá trị của column

Nếu bạn đã có một model instance, bạn có thể lấy giá trị của column trong model bằng cách gọi tên thuộc tính tương ứng. Ví dụ, hãy cùng lặp qua mỗi instance Flight trả về bởi query và hiển thị giá trị của column name:

foreach ($flights as $flight) {
 echo $flight->name;
}

Thêm ràng buộc bổ sung

Hàm all sẽ trả về tất cả các kết quả trong table của model. Vì mỗi Eloquent model phục vụ như một query builder, nên bạn có thể tạo ràng buộc cho các query, và cuối cùng sử dụng hàm get để lấy kết quả:

$flights = AppFlight::where('active', 1)
              ->orderBy('name', 'desc')
              ->take(10)
              ->get();

Collections

Vì các hàm của Eloquent như all và get đều trả về nhiều kết quả, một instance từ IlluminateDatabaseEloquentCollection sẽ được trả về. Class Collection cung cấp các hàm hữu ích để làm việc với tập kết quả của Eloquent. Tất nhiên, bạn có thể thực hiện lặp collection này như một array:

foreach ($flights as $flight) {
   echo $flight->name;
}

Tạo khối kết quả

Nếu bạn muốn xử lý hàng ngàn kết quả từ Eloquent, sử dụng hàm chunk. Hàm này sẽ lấy từng "khối" kết quả của Eloquent model, cung cấp chúng thông qua Closure để xử lý. Sử dụng hàm này sẽ tiết kiệm được memory khi thao tác với tập dữ liệu kết quả lớn:

Flight::chunk(200, function ($flights) {
   foreach ($flights as $flight) {
       //
   }
});

Tham số đầu truyền vào là số record bạn muốn lấy từng "khối" (chunk). Closure truyền vào ở tham số thứ hai sẽ được gọi cho mỗi chunk được lấy từ database.

4. Lấy single model / aggregate

Ngoài việc lấy tất cả dữ liệu, bạn có thể lấy một kết quả sử dụng hàm find và first. Thay vì trả về một collection model, những hàm này trả về một model instance:

// Retrieve a model by its primary key...
$flight = AppFlight::find(1);

// Retrieve the first model matching the query constraints...
$flight = AppFlight::where('active', 1)->first();

Bạn có thể gọi hàm find với một mảng các primary key, với kết quả trả về là một collection các kết quả tìm thấy:

$flights = AppFlight::find([1, 2, 3]);

Not Found Exceptions

Sẽ có lúc bạn muốn bắn ra một exception nếu một model không được tìm thấy. Điều này thực sự hữu ích khi làm việc trên route hay controller. Hàm findOrFail và firstOrFail sẽ trả lại kết quả đầu tiên của query. Tuy nhiên, nếu không có kết quả, thì IlluminateDatabaseEloquentModelNotFoundException sẽ được bắn ra:

$model = AppFlight::findOrFail(1);

$model = AppFlight::where('legs', '>', 100)->firstOrFail();

Nếu exception mà không được bắt, một HTTP response 404 sẽ tự động được gửi lại cho user, vì thế, không cần thiết phải viết code riêng để kiểm tra để trả về 404 khi sử dụng những hàm này:

Route::get('/api/flights/{id}', function ($id) {
    return AppFlight::findOrFail($id);
});

Lấy kết quả tính toán

Bạn cũng có thể sử dụng các hàm như count, sum, max hay các hàm tập hợp khác được cung cấp bởi query builder. Những hàm này trả về một kết quả thay vì một model instance:

$count = AppFlight::where('active', 1)->count();

$max = AppFlight::where('active', 1)->max('price');

5. Thêm và cập nhật model

Thao tác thêm dữ liệu cơ bản

Để thêm dữ liệu mới vào database, đơn giản hãy tạo một model instance mới, thiết lập các attributes vào model rồi gọi hàm save:

<?php

namespace AppHttpControllers;

use AppFlight;
use IlluminateHttpRequest;
use AppHttpControllersController;

class FlightController extends Controller
{
    /**
     * Create a new flight instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

Ở ví dụ này, chúng ta có thể đơn giản chỉ gán tham số name từ HTTP request vào thuộc tính name của model AppFlight. Khi gọi hàm save, một record sẽ được thêm vào database. Timestamp created_at và updated_at cũng tự động được tạo, nên không cần thiết phải thêm vào thủ công.

Thao tác cập nhật dữ liệu cơ bản

Hàm save cũng được dùng để cập nhật model đã tồn tại sẵn trong database. Để update, bạn cần lấy model instance ra trước, thay đổi các attribute ban muốn, rồi gọi hàm save. Lúc này, giá trị của updated_at cũng sẽ tự động được cập nhật, và bạn không cần thay đổi thủ công giá trị này:

$flight = AppFlight::find(1);

$flight->name = 'New Flight Name';

$flight->save();

Update cũng có thể được thực hiện cho nhiều model mà thoả mãn một điều kiện quẻy. Ở ví dụ này, tất cả các flights mà active và có destination là San Diego sẽ bị đánh dấu là delayed:

AppFlight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

Hàm update nhận một mảng các column và cặp giá trị tương ứng column nào cần được update.

Mass Assignment

Bạn cũng có thể sử dụng hàm create để tạo một model mới chỉ trong một dòng. Model instance được thêm mới sẽ được trả lại từ hàm. Tuy nhiên, để làm được điều đó, bạn cần thiết phải chỉ định thuộc tinh fillable hoặc guarded trong model, để Eloquent model được bảo vệ trước mass-assignment.

Lỗi bảo mật mass-assignment xảy ra khi một user truyền vào một tham số HTTP không mong muốn trong request, và tham số đó sẽ có thể thay đổi một column trong database mà bạn không ngờ tới. Ví dụ, một user xấu có thể gửi một tham số is_admin qua HTTP request, và khi giá trị này được map vào trong model qua hàm create, sẽ cho phép user thay đổi để biến thành một admin.

Vì thế, để bắt đầu, bạn cần khai báo thuộc tính bạn muốn cho phép mass-assignment. Bạn có thể thiết lập qua thuộc tính $fillable. Ví dụ, hãy làm cho thuộc tính name trong model Flight có thể được sử dụng qua mass-assignment:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

Sau đó, chúng ta có thể sử dụng hàm create để tạo một record mới trong database:

$flight = AppFlight::create(['name' => 'Flight 10']);

Nếu như $fillable dùng để lưu danh sách các thuộc tính "được phép" (white list) mass-assign, bạn có thể sử dụng $guarded để lưu các thuộc tính mà không được phép mass-assign. Các thuộc tính khác không lưu trong guarded sẽ được mass-assign. Vì thế, guarded được coi như là một "black list". Bạn có thể sử dụng một trong hai, hoặc fillable hoặc guarded, chứ không được dùng cả hai cùng một lúc:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;

class Flight extends Model
{
    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['price'];
}

Ở ví dụ trên, tất cả các thuộc tính trừ price sẽ được phép mass-assign.

Các hàm tạo khác

Còn hai hàm khác bạn có thể sử dụng để model bằng cách mass-assignment các attributes: firstOrCreate và firstOrNow. Hàm firstOrCreate sẽ cố gắng tìm trong database sử dụng cặp column và giá trị truyền vào. Nếu model không được tìm thấy trong database, một dòng record mới sẽ được thêm vào với các attributes được truyền vào.

Hàm firstOrNew, giống như firstOrCreate sẽ cố gắng tìm record trong database khớp với các attribute truyền vào. Tuy nhiên, nếu model không tìm tháy, một model instance mới sẽ được trả về. Chú ý là model được trả về bởi firstOrNew vẫn chưa được lưu vào database. Bạn cần gọi hàm save để lưu nó lại:

// Retrieve the flight by the attributes, or create it if it doesn't exist...
$flight = AppFlight::firstOrCreate(['name' => 'Flight 10']);

// Retrieve the flight by the attributes, or instantiate a new instance...
$flight = AppFlight::firstOrNew(['name' => 'Flight 10']);

5. Xoá model

Để thực hiện xoá model, gọi hàm delete trên model instance:

$flight = AppFlight::find(1);

$flight->delete();

Xoá model tồn tại bằng một key

Ở ví dụ trên, chúng ta lấy model từ database trước khi gọi hàm delete. Tuy nhiên, nếu bạn đã biết primary key của model, bạn có thể xoá model mà không cần lấy nó ra. Để làm được việc này, bạn chỉ cần gọi hàm destroy:

AppFlight::destroy(1);

AppFlight::destroy([1, 2, 3]);

AppFlight::destroy(1, 2, 3);

Xoá nhiều model sử dụng query

Bạn cũng có thể thực hiện gọi một query để xoá một tập hợp các model. Ở ví dụ này, chúng ta sẽ xoá tất cả các flights được đánh dấu là inactive:

$deletedRows = AppFlight::where('active', 0)->delete();

Soft Deleting

Thay vì thực sự xoá các record khỏi database, Eloquent cũng cung cấp kiểu "soft delete" (xoá mềm) model. Khi model được soft deleted, chúng chưa thực sự bị xoá khỏi database. Thay vì thế, một trường là deleted_at sẽ được thiết lập trong model và chèn vào trong database. Nếu model có giá trị deleted_at khác NULL, tức là model đã bị soft deleted. Để kích hoạt xoá mềm cho một model, sử dụng trait IlluminateDatabaseEloquentSoftDeletes trên model và thêm vào column deleted_at vào trong thuộc tính $$ates của model:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentSoftDeletes;

class Flight extends Model
{
    use SoftDeletes;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}

Tất nhiên là bạn cần phải thêm column deleted_at vào trong table. Điều này có thể thực hiện qua việc sử dụng một helper được cung cấp để tạo trên schema builder:

Schema::table('flights', function ($table) {
    $table->softDeletes();
});

Lúc này, khi bạn gọi hàm delete trên model, column deleted_at sẽ được set vào current date và time. Và, khi thực hiện query một model có sử dụng soft delete, thì model đó sẽ tự động bị loại khỏi tất cả các kết qủa query.

Để xác định nếu một model instance bị soft delete, sử dụng hàm trashed:

if ($flight->trashed()) {
    //
}

6. Events

Eloquent model bắn ra một số các events, cho phép bạn có thể hook vào nhiều điểm của model lifecycle sử dụng các hàm sau: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored.

Sử dụng cơ bản

Bất cứ khi nào một model mới được lưu lần đầu tiên, hai event creating và created sẽ được bắn ra. Nếu model đã tồn tại trong database và hàm save được gọi thì hai event updating / updated sẽ được bắn ra. Tuy nhiên, trong cả hai trường hợp thì saving / saved cũng đều được bắn ra.

Ví dụ, cùng tạo một listener cho Eloquent event trong một service provider. Bên trong event listener, chúng ta sẽ gọi hàm isValid trên model, và false sẽ được trả về nếu model không hợp lệ. Việc trả về giá trị false từ một Eloquent event listener sẽ huỷ bọ hai thao tác save / update:

<?php

namespace AppProviders;

use AppUser;
use IlluminateSupportServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::creating(function ($user) {
            if ( ! $user->isValid()) {
                return false;
            }
        });
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

Như vậy mình đã giới thiệu cho các bạn khá là chi tiết về Eloquent trong laravel. Mọi thắc mắc cần giải đáp hãy để lại comment ở phía dưới nhé! Tham Khảo https://laravel.com/docs/5.4

0