/ / Laravel Framework / Comment (1)

Laravel framework – Eloquent ORM và Query Builder có thể bạn chưa biết

Trong bài viết này chúng ta sẽ cùng nhau đi vào tìm hiểu 1 số vấn đề của Eloquent ORM và Query Builder trong Laravel framework.Đây chính là 2 thành phần mà laravel cho phép chúng ta thao tác với CSDL một cách dễ dàng nhất. Có thể bạn đã rất quen thuộc với những câu lệnh Eloquent ORM nhưng không biết nguồn gốc từ cũng câu lệnh đó từ đâu hay cơ chế load thế nào thì trong bài này chúng ta sẽ cùng đi vào 1 số trường hợp cụ thể. Trước khi vào bài này các bạn có thể xem lại 1 số khái niệm cũng như cách sử dụng Query BuilderEloquent ORM từ những bài trước.
– Trong bài này tôi đang sử dụng Laravel framework 5.4

So sánh Eloquent ORM và Query Builder

Query Builder

– Query Builder sử dụng PDO nhằm bảo vệ ưng dụng và tránh các lỗi về SQL injection.Query Builder xây dựng lớp Illuminate\Support\Facades\DBđể thực hiện các câu truy vấn.
– Sử dụng thao tác trực tiếp với bảng
– Có câu lệnh viết phức tạp hơn nhưng cũng có thể thể thực hiện các truy vấn phức tạp
– Tốc độ thực hiện truy vấn nhanh hơn

Eloquent ORM

– Mỗi bảng của database sẽ được ánh xạ qua ‘Model’, mỗi Model sẽ được kế thừa từ Illuminate\Database\Eloquent\Model;
– Cung cấp ActiveRecord đầy đủ.
– Các câu lệnh của Eloquent ORM là ngắn gọn, dễ hiểu và dễ sử dụng hơn.
– Các câu hàm trong Query Builder có thể sử dụng được trong Eloquent bằng cách call Static, nhưng ngược lại Query Builder thì không sử dụng được các hàm trong Eloquent
– Tốc độ xử lý chậm hơn so với Query Builder
– Nói cách khác thì Eloquent ORM nó như là 1 bản nâng cấp từ Query Builder giúp cho các viết ngắn gọi dễ hiểu,cung cấp các phương thức tĩnh và các phương thức thêm mà Query Builder không có như softDelele, các scope, và các event boot

Những hàm trong Eloquent ORM lấy ra từ đâu?

Lưu ý: Mục này chỉ dành cho newbie.
– Như các bạn đã biết Eloquent ORM trong Laravel cung cấp ActiveRecord đầy đủ lên khi làm việc với Eloquent sẽ thấy câu lệnh cực kỳ ngắn gọn dễ hiểu, thế nhưng liệu có khi nào các bạn nghĩ những lệnh đó nó ở đâu ra? nó load ra từ đâu?và các bạn đã bao giờ mở core nó ra xem thế nào chưa? hãy xem ví dụ phía dưới đây để mổ xẻ nó ra từng phần nhé
– Giả sử tôi có 1 table users, và 1 Model User, như vậy để lấy ra thông tin thành viên có ID = 1 tôi sẽ có 2 cách lấy như sau:
+ Với Query Builder:

use Illuminate\Support\Facades\DB;
...
$user = DB::table('users')->where('id', 1)->first();

+ Với Model User (Eloquent ORM):

use App\User;
....
$user = User::find(1);

– Với 2 cách gọi trên nó đều trả về kết quả như ta mong đợi, với cách sử dụng Query Builder nó sẽ trả về cho ta 1 stdClass Object,còn Eloquent ORM nó trả về cho ta 1 App\User Object (thực thể Model) => Như vậy khi sử dụng Eloquent ORM hệ thống đã thêm thắt rất nhiều thứ chính vì vậy tốc độ xử lý nó cũng sẽ bị chậm hơn so với Query Builder.

– Và các bạn thấy với cách sử dụng Eloquent ORM thì câu lệnh rất ngắn gọn dễ hiểu phải không nào, nhưng có khi nào bạn tự hỏi vậy cái hàm find đó nó từ đâu ra không? Để trả lời câu hỏi này chúng ta sẽ mở file App\User:

<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

– Khi các bạn xem file này thì sẽ chả tìm thấy hàm find đâu đúng không nào, hãy bình tĩnh vì ta thấy nó được extends Authenticatable, lúc này chúng ta sẽ tìm tới file đó Illuminate\Foundation\Auth\User:

<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword;
}

– Thật tiếc là trong file này cũng không có hàm find nào, bắt đầu thấy phức tạp rồi đây, hãy bình tĩnh vì nó extends Model lên chúng ta sẽ tiếp tục vào file này xem có gì nhé Illuminate\Database\Eloquent\Model;, Oh bắt đầu thấy có tí niềm tin, file này khá nhiều hàm mong rằng sẽ có hàm find , nhưng WTF? chả thấy có hàm find nào cả, vậy nó lấy ra từ đâu đây? Trong khi đó file này chỉ implements 1 số interface khác như ArrayAccess, Arrayable, Jsonable... và sử dụng thêm 1 số traint như Concerns\HasAttributes,Concerns\HasEvents,Concerns\HasGlobalScopes...
và tất nhiên bạn có thể mỏi mắt cũng không ra được hàm find ở đâu.Vấn đề lại trở lên phức tạp rồi đây, nếu bạn không suy nghĩ thêm chắc chắn tới đây bạn sẽ thấy nản và chả biết rất nhiều hàm trong Eloquent ORM nó được lấy ra từ đâu.
– Hãy nhớ lại chút kiến thức cơ bản nhé, trong PHP có các hàm magic method 1 hàm được gọi ra từ 1 lớp nào đó mà trong lớp đó không có hàm đó chúng ta hãy vào xem ngay hàm __call hoặc __callStatic:
+ Hàm __call: được gọi ra khi khi bạn gọi tới một phương thức nào đó không tồn tại trong đối tượng
+ Hàm __callStatic: Giống như __call nhưng chỉ dùng trong trường hợp bạn gọi đến phương thức tĩnh không tồn tại.
– Quay trở lại với câu lệnh:

$user = User::find(1);

– Chúng ta thấy nó đang gọi dưới dạng 1 phương thức tĩnh lên ta sẽ tìm ngay tới hàm __callStatic, và đúng như vậy nó có ngay hàm __callStatic với nội dung:

    public static function __callStatic($method, $parameters)
    {
        return (new static)->$method(...$parameters);
    }

– Có 2 tham số trong hàm này: $method là phương thức được gọi, $parameters là các tham số truyền vào.

return (new static)->$method(...$parameters);

– Tại đây 1 thực thể được tạo ra và hàm này sẽ được chuyển tới __call(), và với kiểu gắn parameters như trên đó chính là cách mà từ phiên bản php 5.6 trở lên cho phép chúng ta sử dụng, các bạn có thể tìm hiểu thêm tại đây
– Chúng ta sẽ tiếp tục xem hàm __callcó gì nhé:

    public function __call($method, $parameters)
    {
        if (in_array($method, ['increment', 'decrement'])) {
            return $this->$method(...$parameters);
        }

        return $this->newQuery()->$method(...$parameters);
    }

– Hàm này nó sẽ kiểm tra nếu bạn gọi tới 1 trong các phương thức 'increment', 'decrement' thì nó sẽ gọi tới ngay phương thức đó trong lớp Model này.
– Còn lại nó sẽ trả về 1 phương được gọi từ 1 đối tượng $this->newQuery(), lúc này chúng ta lại xem trong hàm newQuery() này nó trả về cho chúng ta cái gì nhé:

    /**
     * Get a new query builder for the model's table.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function newQuery()
    {
        $builder = $this->newQueryWithoutScopes();

        foreach ($this->getGlobalScopes() as $identifier => $scope) {
            $builder->withGlobalScope($identifier, $scope);
        }

        return $builder;
    }

– Như các bạn thấy hàm trên trả về cho chúng ta một EloquentBuilder \Illuminate\Database\Eloquent\Builder, mà lớp Builder này khi được khởi tạo đã được inject trực tiếp lớp QueryBuilder Illuminate\Database\Query\Builder thông qua cơ chế Dependency Injection mà mình đã giới thiệu trong buổi trước.
– Từ đó, EloquentBuilder có thể dễ dàng thực hiện các hàm của QueryBuilder. Nó gọi đến QueryBuilder, nhận kết quả là một mảng rồi chuyển đổi mảng đó về dạng Collections.

Kết luận

– Vậy đó các bạn thấy cơ chế load 1 phương thức của Eloquent ORM trong Laravel framework cũng khá phức tạp phải không nào.Qua đây phần nào cũng giúp cho các bạn hiểu thêm cơ chế load của Eloquent ORM và sự khác nhau giữa Eloquent ORMQuery Builder, thực tế các kiến thức về Laravel framework rất rộng để hiểu sâu về Core Concepts của nó (như Service Container, Service Provider, Facades, Contracts) chắc chắn sẽ làm bạn mất rất nhiều thời gian đó.Mong là sẽ có thời gian để có thể chém gió với các bạn trong những bài viết tiếp theo về series Laravel framework 5.x có sai sót gì a e nhẹ tay góp ý nhé :D.



11/03/2017
Written by admin

Hiện do a Tuyền (nguyên adminsite hocphp.info) không còn thời gian để phát triển site, nên do có cơ duyên và được a tin tưởng để cho mình tiếp tục phát triển. Hi vọng mọi người cùng nhau phát triển và chia sẽ nhiều hơn nữa, ngoài ra mình cũng cố gắng chia sẽ các seri về web cũng như các ngôn ngữ khác chất nhất có thể. có gì thiếu sót hi vọng mọi người bỏ qua.

Bài viết chùng chuyên mục

One comment

  1. Trung dũng says:
     /  Reply

    Đúng thật làm việc với laravel mà không chú ý tới những cái này chỉ biết sử dụng thôi

Gửi bình luận

Giới thiệu

Mình tạo ra blog này với mong muốn chia sẻ và học hỏi kinh nghiệm trong quá trình thiết kế website. Website đang trong quá trình phát triển chân thành cảm ơn mọi sự góp ý của các bạn để làm cho website ngày càng hoàn thiên.

DMCA.com Protection Status
Liên hệ
Theo dõi qua Email

Tổng hợp các bài viết về

Hoc php - CodeIgniter Framework - Laravel Framework - PHP va MYSQL