精华内容
下载资源
问答
  • eloquent

    2018-06-02 15:44:39
    //建立一个模型1.如果表名 和模型名字不符合 或者 主键不是ID 就要定义变量2.timestamps 表中会有created_time 和 update_time 字段3. 调用created_time 时 asDateTime 会返回日期data格式4.getDateFormat 返回...

    //建立一个模型

    1.如果表名 和模型名字不符合 或者 主键不是ID 就要定义变量

    2.timestamps 表中会有created_time 和 update_time 字段

    3. 调用created_time 时  asDateTime 会返回日期data格式

    4.getDateFormat 返回时间戳


    5.指定是否允许批量赋值的字段



    6.firstOrCreate

    //如果没有查找的字段就创建一个

    Table::firstOrCreate(['name'=>'ss'];

    展开全文
  • Eloquent Integration

    2020-12-09 15:23:06
    <div><p>İs there any examples with eloquent ?</p><p>该提问来源于开源项目:timgws/QueryBuilderParser</p></div>
  • Eloquent support

    2020-12-26 07:26:03
    <div><p>I would like to create a pull request to add these traits for eloquent models. I would change it a bit to use this lib instead of hashid directly, but you get the idea. <p>I use these two alot...
  • eloquent-sortable, Eloquent模型的可以排序行为 Eloquent模型的排序行为 这个包提供了一个特性,它向一个有说服力的模型添加了可以排序的行为。模型新记录的order列的值由模型+ 1的所有记录的order列的最大值决定。...
  • eloquent-uuid, Laravel 5.1和 5.2使用的Eloquent UUID特性 eloquent-uuidLaravel 5.1 - 5.4使用的Eloquent UUID特性 It应该与 Laravel 5.0一起工作,但还没有测试。trait覆盖 static boot 方法并侦听 creating
  • eloquent-javascript-translation:Marijn Haverbeke在线书籍Eloquent Javascript的法语翻译
  • Laravel开发-eloquent

    2019-08-28 02:39:04
    Laravel开发-eloquent 扩展了Laravel雄辩的ORM。
  • Eloquent Release Plans

    2020-12-29 06:22:59
    <div><p>What are your plans for making an ROS2 Eloquent release? <p>Navigation2 is currently discussing the possibility of making an Eloquent release in the next few weeks since OSRF has enabled it in...
  • Eloquent ORM

    2018-03-22 13:58:09
    简介Laravel 自带的 Eloquent ORM 为您的数据库提供了一个优雅的、简单的 ActiveRecord 实现。每一个数据库的表有一个对应的 "Model" 用来与这张表交互。在开始之前,确认已在 app/config/database.php ...

    简介

    Laravel 自带的 Eloquent ORM 为您的数据库提供了一个优雅的、简单的 ActiveRecord 实现。每一个数据库的表有一个对应的 "Model" 用来与这张表交互。

    在开始之前,确认已在 app/config/database.php 文件中配置好数据库连接。

    基本用法

    首先,创建一个 Eloquent 模型。模型通常在 app/models 目录,但是您可以自由地把它们放在任何地方,只要它能根据您的 composer.json 文件自动加载。

    定义一个 Eloquent 模型

    class User extends Eloquent {}

    注意我们并没有告诉 Eloquent 我们为 User 模型使用了哪一张表。类名的小写、复数的形式将作为表名,除非它被显式地指定。所以,在这种情况下,Eloquent 将假设 User 模型在 users 表中保存记录。您可以在模型中定义一个 table 属性来指定一个自定义的表名:

    class User extends Eloquent {
    
        protected $table = 'my_users';
    
    }

    注意: Eloquent 将假设每张表有一个名为 id 的主键。您可以定义 primaryKey 属性来覆盖这个约定。同样,您可以定义一个 connection 属性来覆盖在使用这个模型时所用的数据库连接。

    一旦模型被定义,您可以开始在表中检索和创建记录。注意在默认情况下您将需要在表中定义 updated_atcreated_at 字段。如果您不希望这些列被自动维护,在模型中设置 $timestamps 属性为 false

    获取所有记录

    $users = User::all();

    根据主键获取一条记录

    $user = User::find(1);
    
    var_dump($user->name);

    注意: 所有在 [查询构建器] 中适用的函数在 Eloquent 模型的查询中同样适用。

    根据主键获取一条记录或者抛出一个异常

    有时您可能希望当记录没有被找到时抛出一个异常,允许您使用 App::error 处理器捕捉这些异常并显示404页面。

    $model = User::findOrFail(1);
    
    $model = User::where('votes', '>', 100)->firstOrFail();

    注册错误处理器,请监听 ModelNotFoundException

    use Illuminate\Database\Eloquent\ModelNotFoundException;
    
    App::error(function(ModelNotFoundException $e)
    {
        return Response::make('Not Found', 404);
    });

    使用 Eloquent 模型查询

    $users = User::where('votes', '>', 100)->take(10)->get();
    
    foreach ($users as $user)
    {
        var_dump($user->name);
    }

    当然,您也可以使用查询构建器的统计函数。

    Eloquent 统计

    $count = User::where('votes', '>', 100)->count();

    如果您无法通过通过连贯的接口产生查询,可以使用 whereRaw

    $users = User::whereRaw('age > ? and votes = 100', array(25))->get();

    Chunking Results

    If you need to process a lot (thousands) of Eloquent records, using the chunk command will allow you to do without eating all of your RAM:

    User::chunk(200, function($users)
    {
        foreach ($users as $user)
        {
            //
        }
    });

    The first argument passed to the method is the number of records you wish to receive per "chunk". The Closure passed as the second argument will be called for each chunk that is pulled from the database.

    指定查询的数据库连接

    您可能需要在运行一个 Eloquent 查询的时候指定数据库连接,只需要使用 on 函数:

    $user = User::on('connection-name')->find(1);

    集体赋值

    当创建一个新的模型,您可以传递属性的数组到模型的构造函数。这些属性将通过集体赋值分配给模型。这是很方便的,但把用户的输入盲目地传给模型可能是一个严重的安全问题。如果把用户输入盲目地传递给模型,用户可以自由地修改任何或者全部模型的属性。基于这个原因,默认情况下所有 Eloquent 模型将防止集体赋值。

    首先,在模型中设置 fillableguarded 属性。

    fillable 属性指定哪些属性可以被集体赋值。这可以在类或接口层设置。

    在模型中定义 Fillable 属性

    class User extends Eloquent {
    
        protected $fillable = array('first_name', 'last_name', 'email');
    
    }

    在这个例子中,只有三个被列出的属性可以被集体赋值。

    fillable 的反义词是 guarded,将做为一个黑名单而不是白名单:

    在模型中定义 Guarded 属性

    class User extends Eloquent {
    
        protected $guarded = array('id', 'password');
    
    }

    在这个例子中,idpassword 属性将被允许集体赋值。所有其他属性将被允许集体赋值。您可以使用 guard 方法阻止所有属性被集体赋值:

    阻止所有属性集体赋值

    protected $guarded = array('*');

    插入、更新、删除

    为了从模型中向数据库中创建一个新的记录,简单地创建一个模型实例并调用 save 函数。

    保存一个新的模型

    $user = new User;
    
    $user->name = 'John';
    
    $user->save();

    注意: 通常您的 Eloquent 模型将有自动递增的键。然而,如果您希望指定您自定义的键,在模型中设置 incrementing 属性为 false

    您也可以使用 create 函数在一行代码中保存一个新的模型。被插入的模型实例将从函数中返回。但是,在您这样做之前,您需要在模型中指定 fillable 或者 guarded 属性,因为所有 Eloquent 模型默认阻止集体赋值。

    在保存或创建一个使用自增ID的新模型之后,可以通过对象的 id 属性获取此自增ID:

    $insertedId = $user->id;

    在模型中设置 Guarded 属性

    class User extends Eloquent {
    
        protected $guarded = array('id', 'account_id');
    
    }

    使用模型的 Create 函数

    // Create a new user in the database...
    $user = User::create(array('name' => 'John'));
    
    // Retrieve the user by the attributes, or create it if it doesn't exist...
    $user = User::firstOrCreate(array('name' => 'John'));
    
    // Retrieve the user by the attributes, or instantiate a new instance...
    $user = User::firstOrNew(array('name' => 'John'));

    为了更新一个模型,您可以检索它,改变一个属性,然后使用 save 函数:

    更新一个检索到的模型

    $user = User::find(1);
    
    $user->email = 'john@foo.com';
    
    $user->save();

    有时您可能希望不仅保存模型,还有它的所有关系。为此,您可以使用 push 函数:

    保存一个模型和关系

    $user->push();

    您也可以在一组模型上运行更新:

    $affectedRows = User::where('votes', '>', 100)->update(array('status' => 2));

    删除一个模型,在实例中调用 delete 函数:

    删除一个存在的模型

    $user = User::find(1);
    
    $user->delete();

    根据主键删除一个模型

    User::destroy(1);
    
    User::destroy(array(1, 2, 3));
    
    User::destroy(1, 2, 3);

    当然,您可以在一组模型中运行删除查询:

    $affectedRows = User::where('votes', '>', 100)->delete();

    如果您希望简单的在一个模型中更新时间戳,可以使用 touch 函数:

    只更新模型的时间戳

    $user->touch();

    软删除

    当软删除一个模型,它并没有真的从数据库中删除。相反,一个 deleted_at 时间戳在记录中被设置。为一个模型开启软删除,在模型中指定 softDelete 属性:

    class User extends Eloquent {
    
        protected $softDelete = true;
    
    }

    为了在您的表中添加一个 deleted_at 字段,您可以在迁移中使用 softDeletes 函数:

    $table->softDeletes();

    现在,当您在一个模型中调用 delete 函数,deleted_at 字段将被设置为当前的时间戳。在使用软删除的模型中查询,被软删除的模型将不被包含进查询结果中。为了强制已删除的模型出现在结果集中,在查询中使用 withTrashed 函数:

    强制软删除的模型到结果集中

    $users = User::withTrashed()->where('account_id', 1)->get();

    如果您希望在结果集中包含软删除的模型,您可以使用 onlyTrashed 函数:

    $users = User::onlyTrashed()->where('account_id', 1)->get();

    恢复一个已被软删除的记录,使用 restore 函数:

    $user->restore();

    您也可以在查询中使用 restore 函数:

    User::withTrashed()->where('account_id', 1)->restore();

    restore 函数也可以在关系中被使用:

    $user->posts()->restore();

    如果您希望从数据库中真正删除一个模型,您可以使用 forceDelete 函数:

    $user->forceDelete();

    forceDelete 函数也可以在关系中被使用:

    $user->posts()->forceDelete();

    检测一个给定的模型实例是否被软删除,可以使用 trashed 函数:

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

    时间戳

    默认情况下,Eloquent 在数据的表中自动地将维护 created_atupdated_at 字段。只需简单的添加这些 timestamp 字段到表中,Eloquent 将为您做剩余的工作。如果您不希望 Eloquent 维护这些字段,在模型中添加以下属性:

    禁止自动时间戳

    class User extends Eloquent {
    
        protected $table = 'users';
    
        public $timestamps = false;
    
    }

    如果您希望定制时间戳的格式,可以在模型中重写 getDateFormat 函数:

    提供一个定制的时间戳格式

    class User extends Eloquent {
    
        protected function getDateFormat()
        {
            return 'U';
        }
    
    }

    查询范围

    范围允许您容易在模型中重用查询逻辑。定义一个范围,简单的用 scope 为模型添加前缀:

    定义一个查询范围

    class User extends Eloquent {
    
        public function scopePopular($query)
        {
            return $query->where('votes', '>', 100);
        }
    
        public function scopeWomen($query)
        {
            return $query->whereGender('W');
        }
    
    }

    使用一个查询范围

    $users = User::popular()->women()->orderBy('created_at')->get();

    动态范围

    有时您可能希望定义一个接受参数的范围。只需要添加您的参数到您的范围函数:

    class User extends Eloquent {
    
        public function scopeOfType($query, $type)
        {
            return $query->whereType($type);
        }
    
    }

    然后在范围函数调用中传递参数:

    $users = User::ofType('member')->get();

    关系

    当然,您的数据库可能是彼此相关的。比如,一篇博客文章可能有许多评论,或者一个订单与下订单的用户相关。Eloquent 使得管理和处理这些关系变得简单。Laravel 提供了四种类型的关系:

    一对一

    一个一对一关系是一种非常基础的关系。比如,一个 User 模型可以有一个 Phone`。我们可以在 Eloquent 中定义这个关系:

    定义一个一对一关系

    class User extends Eloquent {
    
        public function phone()
        {
            return $this->hasOne('Phone');
        }
    
    }

    传递给 hasOne 函数的第一个参数是相关模型的名字。一旦这个关系被定义,我们可以使用 Eloquent 的 [动态属性] 获取它:

    $phone = User::find(1)->phone;

    这条语句所产生的 SQL 语句如下:

    select * from users where id = 1
    
    select * from phones where user_id = 1

    注意 Eloquent 假设关系的外键基于模型的名字。在这个例子中假设 Phone 模型使用一个 user_id 外键。如果您希望覆盖这个惯例,您可以为传递 hasOne 函数传递第二个参数。Furthermore, you may pass a third argument to the method to specify which local column that should be used for the association:

    return $this->hasOne('Phone', 'foreign_key');
    
    return $this->hasOne('Phone', 'foreign_key', 'local_key');

    Phone 模型定义逆向关系,我们使用 belongsTo 函数:

    定义一个逆向关系

    class Phone extends Eloquent {
    
        public function user()
        {
            return $this->belongsTo('User');
        }
    
    }

    在上面的例子中,Eloquent 将在 phones 表中寻找 user_id 字段。如果您想定义一个不同的外键字段,您可以通过 belongsTo 函数的第二个参数传递它:

    class Phone extends Eloquent {
    
        public function user()
        {
            return $this->belongsTo('User', 'local_key');
        }
    
    }

    Additionally, you pass a third parameter which specifies the name of the associated column on the parent table:

    class Phone extends Eloquent {
    
        public function user()
        {
            return $this->belongsTo('User', 'local_key', 'parent_key');
        }
    
    }

    一对多

    一个一对多关系的例子是一篇博客文章有许多评论。我们可以像这样定义关系模型:

    class Post extends Eloquent {
    
        public function comments()
        {
            return $this->hasMany('Comment');
        }
    
    }

    现在我们可以通过 动态属性 访问文章的评论:

    $comments = Post::find(1)->comments;

    如果您需要添加进一步的约束检索哪些评论,我们可以调用 comments 函数连接链式条件:

    $comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();

    再次,如果您想覆盖默认的外键,可以给 hasMany 函数传递第二个参数。And, like the hasOne relation, the local column may also be specified:

    return $this->hasMany('Comment', 'foreign_key');
    
    return $this->hasMany('Comment', 'foreign_key', 'local_key');

    Comment 模型中定义逆向关系,我们使用 belongsTo 函数:

    定义逆向关系

    class Comment extends Eloquent {
    
        public function post()
        {
            return $this->belongsTo('Post');
        }
    
    }

    多对多

    多对多关系更复杂的关系类型。这种关系的一个例子是一个用户和角色,这个角色也可被其他用户共享。比如,许多用户有 "Admin" 的角色。这个关系需要三个表:users, roles 以及 role_userrole_user 表来源于相关的角色名,并且有 user_idrole_id 字段。

    我们可以使用 belongsToMany 函数定义一个多对多关系:

    class User extends Eloquent {
    
        public function roles()
        {
            return $this->belongsToMany('Role');
        }
    
    }

    现在,我们可以通过 User 模型获取角色:

    $roles = User::find(1)->roles;

    如果您想使用一个非常规的表名作为关系表,您可以传递它作为第二个参数给 belongsToMany 函数:

    return $this->belongsToMany('Role', 'user_roles');

    您也可以覆盖相关的键:

    return $this->belongsToMany('Role', 'user_roles', 'user_id', 'foo_id');

    当然,您也可以定义在 Role 模型中定义逆向关系:

    class Role extends Eloquent {
    
        public function users()
        {
            return $this->belongsToMany('User');
        }
    
    }

    Has Many Through

    The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation. For example, a Country model might have many Posts through a Users model. The tables for this relationship would look like this:

    countries
        id - integer
        name - string
    
    users
        id - integer
        country_id - integer
        name - string
    
    posts
        id - integer
        user_id - integer
        title - string

    Even though the posts table does not contain a country_id column, the hasManyThrough relation will allow us to access a country's posts via $country->posts. Let's define the relationship:

    class Country extends Eloquent {
    
        public function posts()
        {
            return $this->hasManyThrough('Post', 'User');
        }
    
    }

    If you would like to manually specify the keys of the relationship, you may pass them as the third and fourth arguments to the method:

    class Country extends Eloquent {
    
        public function posts()
        {
            return $this->hasManyThrough('Post', 'User', 'country_id', 'user_id');
        }
    
    }

    多态关系

    多态关系允许在一个关联中一个模型属于多于一个的其他模型。比如,您可能有一个 photo 模型属于一个员工模型或者一个订单模型。我们可以定义像这样定义这个关系:

    class Photo extends Eloquent {
    
        public function imageable()
        {
            return $this->morphTo();
        }
    
    }
    
    class Staff extends Eloquent {
    
        public function photos()
        {
            return $this->morphMany('Photo', 'imageable');
        }
    
    }
    
    class Order extends Eloquent {
    
        public function photos()
        {
            return $this->morphMany('Photo', 'imageable');
        }
    
    }

    现在,我们可以为一个员工或者一个订单获取照片:

    获取一个多态关系

    $staff = Staff::find(1);
    
    foreach ($staff->photos as $photo)
    {
        //
    }

    然而,"polymorphic" 的真正魔力是当您从 Photo 模型中访问员工或者订单的时候:

    获取多态关系的属主

    $photo = Photo::find(1);
    
    $imageable = $photo->imageable;

    Photo 模型的 imageable 关系将返回一个 Staff 或者 Order 实例,依赖于那种类型的模型拥有这个照片。

    帮助理解这是怎样工作的,让我们探讨一个多态关系的数据库结构:

    多态关系的数据库结构

    staff
        id - integer
        name - string
    
    orders
        id - integer
        price - integer
    
    photos
        id - integer
        path - string
        imageable_id - integer
        imageable_type - string

    这里需要注意的键字段是表 photosimageable_idimageable_type 字段。ID 将包含所属员工或订单的 ID,类型将包含所属模型的类名。当访问 imageable 关系的时候将允许 ORM 检测所属模型的类型。

    查询关系

    当为一个模型获取记录的时候,您可能希望基于一个已存在的关系限制结果集的数目。比如,您希望获取至少有一条评论的文章。为此,您可以使用 has 函数:

    Querying Relations When Selecting

    $posts = Post::has('comments')->get();

    您也可以指定一个操作符和数量:

    $posts = Post::has('comments', '>=', 3)->get();

    If you need even more power, you may use the whereHas and orWhereHas methods to put "where" conditions on your has queries:

    $posts = Post::whereHas('comments', function($q)
    {
        $q->where('content', 'like', 'foo%');
    
    })->get();

    动态属性

    Eloquent 允许您通过动态属性访问关系。Eloquent 将为您自动加载关系,并且足够聪明到知道是否调用 get (为一对多关系)或者 first (为一对一关系)。然后,它将通过动态属性访问作为关系。比如,使用下面的 Phone 模型:

    class Phone extends Eloquent {
    
        public function user()
        {
            return $this->belongsTo('User');
        }
    
    }
    
    $phone = Phone::find(1);

    不需要像这样打印用户的电子邮件:

    echo $phone->user()->first()->email;

    它可以简化为:

    echo $phone->user->email;

    注意: 返回多个结果的关系,其本质是返回了 Illuminate\Database\Eloquent\Collection 类的一个实例。

    预先加载

    预先加载的存在是为了缓解 N + 1 查询问题。比如,考虑一个 Author 相关的 Book 模型。关系定义是这样的:

    class Book extends Eloquent {
    
        public function author()
        {
            return $this->belongsTo('Author');
        }
    
    }

    现在,考虑下面的代码:

    foreach (Book::all() as $book)
    {
        echo $book->author->name;
    }

    此循环将执行一个查询获取表中的所有书籍,然后另一个查询为每本书获取作者。所以,如果我们有 25 本书,这个循环将允许 26 个查询。

    值得庆幸的是,我们可以使用预先加载来减少查询的数量。这个关系应该被预先加载通过 with 函数指定:

    foreach (Book::with('author')->get() as $book)
    {
        echo $book->author->name;
    }

    在上面的循环中,只有两个查询被执行:

    select * from books
    
    select * from authors where id in (1, 2, 3, 4, 5, ...)

    明智的使用预先加载可以大大提高应用程序的性能。

    当然,您可以一次性预先加载多个关系:

    $books = Book::with('author', 'publisher')->get();

    您甚至可以预先加载嵌套关系:

    $books = Book::with('author.contacts')->get();

    在上面的例子中,author 关系将被预先加载,而且作者的 contacts 关系也将被加载。

    预先加载约束

    有时您可能希望预先加载一个关系,但也为预先加载指定一个条件。这里是一个例子:

    $users = User::with(array('posts' => function($query)
    {
        $query->where('title', 'like', '%first%');
    }))->get();

    在这个例子中,我们预先加载用户的文章,但只限于文章的 title 字段中包含单词 "first" 的文章:

    延迟预先加载

    从一个已存在的模型中直接预先加载相关的模型也是可能的。这在动态的决定是否加载相关模型或与缓存结合时可能有用。

    $books = Book::all();
    
    $books->load('author', 'publisher');

    插入相关模型

    您会经常需要插入新的相关模型。比如,你可能希望为一篇文章插入一条新的评论。不需要在模型中手动设置 post_id 外键,您可以直接从它的父模型 Post 中插入新的评论:

    附加一个相关的模型

    $comment = new Comment(array('message' => 'A new comment.'));
    
    $post = Post::find(1);
    
    $comment = $post->comments()->save($comment);

    在这个例子中,所插入评论的 post_id 字段将自动设置。

    相关模型 (属于)

    当更新一个 belongsTo 关系,您可以使用 associate 函数,这个函数将为子模型设置外键:

    $account = Account::find(10);
    
    $user->account()->associate($account);
    
    $user->save();

    插入相关模型 (多对多)

    当处理多对多关系时,您也可以插入相关模型。让我们继续使用我们的 UserRole 模型作为例子。我们能够轻松地使用 attach 函数为一个用户附加新的角色:

    附加多对多模型

    $user = User::find(1);
    
    $user->roles()->attach(1);

    您也可以传递一个属性的数组,存在关系的表中:

    $user->roles()->attach(1, array('expires' => $expires));

    当然,attach 的反义词是 detach

    $user->roles()->detach(1);

    您也可以使用 sync 函数附加相关的模型。sync 函数接受一个 IDs 数组。当这个操作完成,只有数组中的 IDs 将会为这个模型存在关系表中:

    使用 Sync 附加多对多关系

    $user->roles()->sync(array(1, 2, 3));

    您也可以用给定的 IDs 关联其他关系表中的值:

    同步时添加关系数据

    $user->roles()->sync(array(1 => array('expires' => true)));

    有时您可能希望创建一个新的相关的模型,并且在一行中附加它。对于此操作,您可以使用 save 函数:

    $role = new Role(array('name' => 'Editor'));
    
    User::find(1)->roles()->save($role);

    在这个例子中,新的 Role 模型将被保存并附加到用户模型。您也可以传递一个属性的数组加入到在这个操作中所连接的表:

    User::find(1)->roles()->save($role, array('expires' => $expires));

    触发父模型时间戳

    当一个模型 belongsTo 另一个模型,比如一个 Comment 属于一个 Post,当更新子模型时更新父模型的时间戳通常是有用的。比如,当一个 Comment 模型更新,您想自动更新所属 Postupdated_at 时间戳。Eloquent 使之变得容易,只需要在子模型中添加 touches 属性包含关系的名字:

    class Comment extends Eloquent {
    
        protected $touches = array('post');
    
        public function post()
        {
            return $this->belongsTo('Post');
        }
    
    }

    现在,当您更新一个 Comment,所属的 Postupdated_at 字段也将被更新:

    $comment = Comment::find(1);
    
    $comment->text = 'Edit to this comment!';
    
    $comment->save();

    与数据透视表工作

    正如您已经了解到,使用多对多关系需要一个中间表的存在。Eloquent 提供了一些非常有用与这个表交互的方式。比如,让我们假设我们的 User 对象有很多相关的 Role 对象,在访问这个关系时,我们可以在这些模型中访问数据透视表:

    $user = User::find(1);
    
    foreach ($user->roles as $role)
    {
        echo $role->pivot->created_at;
    }

    注意每一个我们检索的 Role 模型将自动赋值给一个 pivot 属性。这个属性包含一个代表中间表的模型,并且可以像其他 Eloquent 模型使用。

    默认情况下,在 pivot 对象中只有键,如果您的数据透视表包含其他的属性,您必须在定义关系时指定它们:

    return $this->belongsToMany('Role')->withPivot('foo', 'bar');

    现在,foobar 属性将可以通过 Role 模型的 pivot 对象中访问。

    如果您想您的数据透视表有自动维护的 created_atupdated_at 时间戳,在关系定义时使用 withTimestamps 方法:

    return $this->belongsToMany('Role')->withTimestamps();

    为一个模型在数据透视表中删除所有记录,您可以使用 detach 函数:

    在一个数据透视表中删除记录

    User::find(1)->roles()->detach();

    注意这个操作并不从 roles 删除记录,只从数据透视表中删除。

    Defining A Custom Pivot Model

    Laravel also allows you to define a custom Pivot model. To define a custom model, first create your own "Base" model class that extends Eloquent. In your other Eloquent models, extend this custom base model instead of the default Eloquent base. In your base model, add the following function that returns an instance of your custom Pivot model:

    public function newPivot(Model $parent, array $attributes, $table, $exists)
    {
        return new YourCustomPivot($parent, $attributes, $table, $exists);
    }

    集合

    所有通过 get 方法或一个relationship由Eloquent返回的多结果集都是一个集合对象。这个对象实现了 IteratorAggregate PHP 接口,所以可以像数组一样进行遍历。然而,这个对象也有很多其他的函数来处理结果集。

    比如,我们可以使用 contains 检测一个结果集是否包含指定的主键:

    检测一个集合是否包含一个键

    $roles = User::find(1)->roles;
    
    if ($roles->contains(2))
    {
        //
    }

    集合也可以被转换为一个数组或JSON:

    $roles = User::find(1)->roles->toArray();
    
    $roles = User::find(1)->roles->toJson();

    如果一个集合被转换为一个字符转,它将以JSON格式返回:

    $roles = (string) User::find(1)->roles;

    Eloquent 集合也包含一些方法来遍历和过滤所包含的项:

    遍历集合

    $roles = $user->roles->each(function($role)
    {
        //
    });

    过滤集合

    过滤集合时,你所提供的回调函数将被作为 array_filter 的回调函数使用。

    $users = $users->filter(function($user)
    {
        if($user->isAdmin())
        {
            return $user;
        }
    });

    注意: 当过滤集合并转化为JSON格式时,应首先调用 values 函数重置数组内所有的键(key)。

    对每个集合项应用回调函数

    $roles = User::find(1)->roles;
    
    $roles->each(function($role)
    {
        //  
    });

    根据一个值排序集合

    $roles = $roles->sortBy(function($role)
    {
        return $role->created_at;
    });

    有时,您可能希望返回一个自定义的集合以及自己添加的函数。您可以通过在 Eloquent 模型中重写 newCollection 函数指定这些:

    返回一个自定义集合类型

    class User extends Eloquent {
    
        public function newCollection(array $models = array())
        {
            return new CustomCollection($models);
        }
    
    }

    访问器和调整器

    Eloquent 为获取和设置模型的属性提供了一种便利的方式。简单的在模型中定义一个 getFooAttribute 函数声明一个访问器。记住,这个函数应该遵循 "camel-casing" 拼写格式,即使您的数据库使用 "snake-case":

    定义一个访问器

    class User extends Eloquent {
    
        public function getFirstNameAttribute($value)
        {
            return ucfirst($value);
        }
    
    }

    在上面的例子中,first_name 字段有一个访问器。注意属性的值被传递到访问器。

    调整器以类似的方式声明:

    定义一个调整器

    class User extends Eloquent {
    
        public function setFirstNameAttribute($value)
        {
            $this->attributes['first_name'] = strtolower($value);
        }
    
    }

    日期调整器

    默认情况下,Eloquent 将转换 created_atupdated_at 以及 deleted_at 字段为 Carbon 的实例,它提供了各种有用的函数,并且继承自原生 PHP 的 DateTime 类。

    您可以指定哪些字段自动调整,设置禁用调整,通过在模型中重写 getDates 函数:

    public function getDates()
    {
        return array('created_at');
    }

    当一个字段被认为是一个日期,您可以设置它的值为一个 UNIX 时间戳,日期字符串 (Y-m-d),日期时间字符串,当然也可以是一个 DateTimeCarbon 实例。

    完全禁用日期调整,从 getDates 函数中返回一个空数组:

    public function getDates()
    {
        return array();
    }

    模型事件

    Eloquent 模型触发一些事件,允许您使用以下函数在模型的生命周期内的许多地方插入钩子:creatingcreatedupdatingupdatedsavingsaveddeletingdeletedrestoringrestored

    每当一个新的项目第一次被保存,creatingcreated 事件将被触发。如果一个项目不是新的并且 save 函数被调用, updating/updated 事件将被触发。在这两种情况下,saving/saved 事件都将被触发。

    如果从 creating, updating 或者 saving 返回 false ,该操作将被取消:

    通过事件取消保存操作

    User::creating(function($user)
    {
        if ( ! $user->isValid()) return false;
    });

    Eloquent 模型也包含一个静态的 boot 函数,它可以提供一个方便的地方注册事件绑定。

    设置一个模型 Boot 函数

    class User extends Eloquent {
    
        public static function boot()
        {
            parent::boot();
    
            // Setup event bindings...
        }
    
    }

    模型观察者

    为加强处理模型事件,您可以注册一个模型观察者。一个观察者类可以包含很多函数对应于很多模型事件。比如,creating, updating, saving 函数可以在一个观察者中,除其他模型事件名之外。

    因此,比如,一个模型观察者可以像这样:

    class UserObserver {
    
        public function saving($model)
        {
            //
        }
    
        public function saved($model)
        {
            //
        }
    
    }

    您可以使用 observe 函数注册一个观察者实例:

    User::observe(new UserObserver);

    转为数组或JSON

    当构建JSON APIs,您可能经常需要转换您的模型和关系为数组或JSON字符串。所以,Eloquent 包含这些方法。转换一个模型和它加载的关系为一个数组,您可以使用 toArray 函数:

    转换一个模型为数组

    $user = User::with('roles')->first();
    
    return $user->toArray();

    注意模型的全部集合也可以被转为数组:

    return User::all()->toArray();

    转换一个模型为JSON字符串,您可以使用 toJson 函数:

    转换一个模型为JSON字符串

    return User::find(1)->toJson();

    注意当一个模型或集合转换为一个字符串,它将转换为JSON,这意味着您可以直接从应用程序的路由中返回 Eloquent 对象!

    从路由中返回一个模型

    Route::get('users', function()
    {
        return User::all();
    });

    有时您可能希望限制包含在模型数组或JSON中的属性,比如密码,为此,在模型中添加一个隐藏属性:

    从数组或JSON转换中隐藏属性

    class User extends Eloquent {
    
        protected $hidden = array('password');
    
    }

    注意: 如果关系被隐藏了,请使用关系的 method 名称,而不是动态的访问名称。

    或者,您可以使用 visible 属性定义一个白名单:

    protected $visible = array('first_name', 'last_name');

    某些时候,你可能需要向数据库添加一组属性,而这组属性并在数据库中没有对应的字段。为了能够实现这一需求,可以简单的为每个属性定义一个访问器:

    public function getIsAdminAttribute()
    {
        return $this->attributes['admin'] == 'yes';
    }

    一旦创建了访问器,只需将属性添加到模型的appends属性中:

    protected $appends = array('is_admin');

    一旦将属性添加到appends列表中,它就将被包含在模型和JSON表单中。

    展开全文
  • eloquent-sluggable, 在 Laravel 中,为你的Eloquent模型轻松创建 slugs eloquent的sluggable轻松创建 slugs,使你在 Laravel 中的模型更加清晰。英镑注释:这些说明用于 Laravel 5.5. 如果你使用的是 Laravel,请...
  • Wordpress Laravel雄辩的模型 ... **这是Eloquent之上的其他功能的文档。 有关Eloquent所有功能的请访问。 总览 安装 composer require drewjbartlett/wordpress-eloquent 建立 require_once ( 'vendor/autoload.php'
  • Eloquent not booted

    2020-12-31 20:51:21
    <p>After looking around for a while I figured out Eloquent has not booted! And after adding a $capsuleDefinition->addMethodCall('bootEloquent');</code> inside the <code>WouterJ\...
  • <p>These are the same updates we made in master already but need to be updated in Eloquent and released again to make the builds succeed ( see red in README chart). </p><p>该提问来源于开源项目:...
  • 在官方文档当中,数据库的使用说明文档占据了两个大章节,分别是【数据库】与【Eloquent ORM】,为什么针对同一功能,官方要出两个文档呢?是因为它重要?复杂?对此我无从猜测,不过可以从源码中窥知一二。一、 ...

    在web应用中,与数据库的交互可以说是最常用且最重要的操作。作为当前最流行的php框架之一,laravel对数据库操作的封装,可以说是非常优秀了。在官方文档当中,数据库的使用说明文档占据了两个大章节,分别是【数据库】与【Eloquent ORM】,为什么针对同一功能,官方要出两个文档呢?是因为它重要?复杂?对此我无从猜测,不过可以从源码中窥知一二。

    一、 Eloquent的生命周期

    在laravel应用的生命周期里,数据库部分出现在第二阶段,容器启动阶段。更精确的说,是容器启动阶段的服务提供者注册/启动阶段。数据库服务的入口,是数据库的服务提供者,即IlluminateDatabaseDatabaseServiceProvider

    DatabaseServiceProvider的注册方法如代码所示:

    public function register()
    {
        Model::clearBootedModels();
    
        $this->registerConnectionServices();
    
        $this->registerEloquentFactory();
    
        $this->registerQueueableEntityResolver();
    }

    其中,registerConnectionServices()方法注册了三个别名服务,分别是db.factor/db/db.connection。db用于管理数据库连接;db.factor用于创建数据库连接;而db.connection绑定了一个可用的连接对象。值得一提的是,db.connection是通过bind方法绑定闭包到容器当中,所以在注册阶段并未实例化,而是在真正 需要进行数据连接时实例化连接对象,然后替换原来的闭包。

    registerEloquentFactory()方法注册了数据填充功能中的数据工厂,用于生成模拟数据。registerQueueableEntityResolver()方法注册了队列的数据库实现。

    接着,在DatabaseServiceProvider的启动方法中:

    public function boot()
    {
        Model::setConnectionResolver($this->app['db']);
    
        Model::setEventDispatcher($this->app['events']);
    }

    分别调用了Model的两个静态方法setConnectionResolver()/setEventDispatcher(),加上注册方法中的clearBootedModels(),完成了Eloquent ORM的Model类的全局设置。

    Model::clearBootedModels();
    Model::setConnectionResolver($this->app['db']);
    Model::setEventDispatcher($this->app['events']);

    二、 楔子 - Eloquent ORM的使用

    我们先回顾一下官方文档中,关于ORM的用法:

    // 1. 静态调用
    User::all();
    User::find(1);
    User::where();
    
    // 2. 对象调用
    $flight = AppFlight::find(1);
    $flight->name = 'New Flight Name';
    $flight->save();
    $filght->delete();

    Eloquent ORM既可以通过静态调用执行方法,也可以先获取到模型对象,然后执行方法。但他们实质是一样的。在Model中定义的静态方法如下:

    protected static function boot()
    protected static function bootTraits()
    public static function clearBootedModels()
    public static function on($connection = null)
    public static function onWriteConnection()
    public static function all($columns = ['*'])
    public static function with($relations)
    public static function destroy($ids)
    public static function query()
    public static function resolveConnection($connection = null)
    public static function getConnectionResolver()
    public static function setConnectionResolver(Resolver $resolver)
    public static function unsetConnectionResolver()
    public static function __callStatic($method, $parameters)

    可以看到,形如User::find(1)/User::where()的静态调用方法,本身不在类中有定义,而是转发到__callStatic魔术方法:

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

    也就是先实例化自身,然后在对象上执行调用。所以,在使用Eloquent的过程中,模型基本上都会有实例化的过程,然后再对象的基础上进行方法的调用。那么我们看看Model的构造方法中,都做了哪些动作:

    public function __construct(array $attributes = [])
    {
        $this->bootIfNotBooted();
    
        $this->syncOriginal();
    
        $this->fill($attributes);
    }

    bootIfNotBooted()是模型的启动方法,标记模型被启动,并且触发模型启动的前置与后置事件。在启动过程中,会查询模型使用的trait中是否包含boot{Name}形式的方法,有的话就执行,这个步骤可以为模型扩展一些功能,比如文档中的软删除:

    要在模型上启动软删除,则必须在模型上使用 IlluminateDatabaseEloquentSoftDeletes trait 并添加 deleted_at 字段到你的 $dates 属性上。

    就是在启动SoftDeletestraits的时候,给模型添加了一组查询作用域,来新增Restore()/WithTrashed()/WithoutTrashed()/OnlyTrashed()四个方法,同时改写delete方法的逻辑,从而定义了软删除的相关行为。

    syncOriginal()方法的作用在于保存原始对象数据,当更新对象的属性时,可以进行脏检查。

    fill($attributes)就是初始化模型的属性。

    在实际运用中可能会注意到,我们很少会用new的方法、通过构造函数来实例化模型对象,但在后续我们要说道的查询方法中,会有一个装载对象的过程,有这样的用法。为什么我们很少会new一个Model,其实原因两个方面:首先从逻辑上说,是先有一条数据库记录,然后才有基于该记录的数据模型,所以在new之前必然要有查询数据库的动作;其次是因为直接new出来的Model,它的状态有可能并不正确,需要手动进行设置,可以查阅Model的newInstance()/newFromBuilder()两个方法来理解“状态不正确”的含义。

    三、 深入 - Eloquent ORM的查询过程

    我们以User::all()的查询过程来作为本小节的开始,Model的all()方法代码如下:

    public static function all($columns = ['*'])
    {
        return (new static)->newQuery()->get(
            is_array($columns) ? $columns : func_get_args()
        );
    }

    这个查询过程,可以分成三个步骤来执行:

    • new static: 模型实例化,得到模型对象。
    • $model->newQuery(): 根据模型对象,获取查询构造器$query。
    • $query->get($columns): 根据查询构造器,取得模型数据集合。

    Eloquent ORM的查询过程,就可以归纳成这三个过程:

    [模型对象]=>[查询构造器]=>[数据集合]

    数据集合也是模型对象的集合,即使是做first()查询,也是先获取到只有一个对象的数据集合,然后取出第一个对象。但数据集合中的模型对象,与第一步中的模型对象不是同一个对象,作用也不一样。第一步实例化得到的模型对象,是一个空对象,其作用是为了获取第二步的查询构造器,第三步中的模型对象,是经过数据库查询,获取到数据后,对数据进行封装后的对象,是一个有数据的对象,从查询数据到模型对象的过程,我称之为装载对象,装载对象,正是使用的上文提及的newFromBuilder()方法。

    newQuery()的调用过程很长,概括如下:

    newQuery()
    -->newQueryWithoutScopes()  // 添加查询作用域
    -->newModelQuery() // 添加对关系模型的加载
    -->newEloquentBuilder(newBaseQueryBuilder()) // 获取查询构造器
    --> return new Builder(new QueryBuilder()) // 查询构造器的再封装

    它引出了Eloquent ORM中的一个重要概念,叫做$query,查询构造器,虽然官方文档中,有大篇幅关于Model的使用说明,但其实很多方法都会转发给$query去执行。从最后的一次调用可以看出,有两个查询构造器,分别是:

    • 数据库查询构造器:IlluminateDatabaseQueryBuilder
    • Eloquent ORM查询构造器:IlluminateDatabaseEloquentBuilder
    备注: 1. 由于两个类名一致,我们约定当提到Builder时,我们指的是IlluminateDatabaseQueryBuilder;当提到EloquentBuilder时,我们指的是IlluminateDatabaseEloquentBuilder。 2. 在代码中,Builder或EloquentBuilder的实例一般用变量$query来表示

    这两个查询构造器的存在,解释了本文开头提到的问题:为什么关于数据库的文档说明,会分为两个章节?因为一章是对IlluminateDatabaseQueryBuilder的说明,另一章是对IlluminateDatabaseEloquentBuilder的说明(直观的理解为对Model的说明)。

    数据库查询构造器Builder定义了一组通用的,人性化的操作接口,来描述将要执行的SQL语句(见官方文档【数据库 - 查询构造器】一章。)在这一层提供的接口更接近SQL原生的使用方法,比如:where/join/select/insert/delete/update等,都很容易在数据库的体系内找到相应的操作或指令;EloquentBuilder是对Builder的再封装,EloquentBuilder在Builder的基础之上,定义了一些更复杂,但更便捷的描述接口(见官方文档【Eloquent ORM - 快速入门】一章。),比如:first/firstOrCreate/paginator等。

    3.1 EloquentBuilder

    EloquentBuilder是Eloquent ORM查询构造器,是比较高级的能与数据库交互的对象。一般在Model层面的与数据库交互的方法,都会转发到Model的EloquentBuilder对象上去执行,通过下列方法可以获取到一个Eloquent对象:

    $query = User::query();
    $query = User::select();

    每个EloquentBuilder对象都会有一个Builder成员对象。

    3.2 Builder

    Builder是数据库查询构造器,在Builder层面已经可以与数据库进行交互了,如何获取到一个Builder对象呢?下面展示两种方法:

    // 获取Builder对象
    $query = DB::table('user');
    $query = User::query()->getQuery();
    
    // Builder对象与数据库交互
    $query->select('name')->where('status', 1)->orderBy('id')->get();

    Builder有三个成员对象: - ConnectionInterface - Grammar - Processor

    ConnectionInterface ConnectionInterface对象是执行SQL语句、对读写分离连接进行管理的对象,也就是数据库连接对象。是最初级的、能与数据交互的对象:

    DB::select('select * from users where active = ?', [1]);
    DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);
    虽然DB门面指向的是IlluminateDatabaseDatabaseManager的实例,但是对数据库交互上的操作,都会转发到connection上去执行。

    回头看本文中 Eloquent的生命周期 关于DatabaseServiceProvider的启动方法的描述,DatabaseServiceProvider的启动方法中执行的代码 Model::setConnectionResolver($this->app['db']); ,这个步骤就是为了后续获取Builder的第一个成员对象ConnectionInterface,数据库连接对象。前文提到过,数据库的连接并不是在服务提供者启动时进行的,是在做出查询动作时才会连接数据库:

    // IlluminateDatabaseEloquentModel::class
    protected function newBaseQueryBuilder()
    {
        // 获取数据库连接
        $connection = $this->getConnection();
    
        // Builder实例化时传入的三个对象,ConnectionInterface、Grammar、Processor
        return new QueryBuilder(
            $connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
        );
    }
    
    public function getConnection()
    {
        return static::resolveConnection($this->getConnectionName());
    }
    
    public static function resolveConnection($connection = null)
    {
        // 使用通过Model::setConnectionResolver($this->app['db'])注入的resolver进行数据库的连接
        return static::$resolver->connection($connection);
    }

    Grammar

    Grammar对象是SQL语法解析对象,我们在Builder对象中调用的方法,会以Builder属性的形式将调用参数管理起来,然后在调用SQL执行方法时,先通过Grammar对象对这些数据进行解析,解析出将要执行的SQL语句,然后交给ConnectionInterface执行,获取到数据。

    Processor

    Processor对象的作用比较简单,将查询结果数据返回给Builder,包括查询的行数据,插入后的自增ID值。

    3.3 SELECT语句的描述

    在Builder对象中,关于数据库查询语句的描述,被分成12个部分:

    • aggregate: 聚合查询列描述,该部分与columns互斥
    • columns: 查询列描述
    • from: 查询表描述
    • joins: 聚合表描述
    • wheres: 查询条件描述
    • groups: 分组描述
    • havings: 分组条件描述
    • orders: 排序条件描述
    • limit: 限制条数描述
    • offset: 便宜了描述
    • unions: 组合查询描述
    • lock: 锁描述

    其中,关于wherers的描述提供了相当丰富的操作接口,在实现这部分的接口时,在查询构造器Builder中将where操作分成了以下类型: Basic/Column/In/NotIn/NotInSub/InSub/NotNull/Null/between/Nested/Sub/NotExists/Exists/Raw。wheres条件的组装在 IlluminateDatabaseQueryGrammarsGrammar::compileWheres() 方法中完成,每种类型都由两个部分组成:逻辑符号 + 条件表达式。逻辑符号包含and/or多个where条件直接连接后,通过Grammar::removeLeadingBoolean去掉头部的逻辑符号,组装成最终的条件部分。如果有 Nested 的wheres描述,对Nested的部分单独执行compileWheres后,用括号包装起来形成一个复合的 条件表达式

    wheresTable:

    最终组合成的Sql语句就是 id = 1 and table1.column1 = table2.column2 and (...)

    where用法的一些注意事项:

    • where的第一个参数是数组或闭包时,条件被描述为Nested类型,也就是参数分组。
    • where的第二个参数,比较符号是等于号时,可以省略。
    • where的第三个参数是闭包时,表示一个子查询

    3.4 join语句的描述

    每次对Builder执行join操作时,都会新建一个JoinClause对象,在文档中关于高级 Join 语法的说明中,有非常类似于where参数分组的用法,就是由闭包导入查询条件:

    // join高级用法
    DB::table('users')
        ->join('contacts', function ($join) {
            $join->on('users.id', '=', 'contacts.user_id')->orOn(...);
        })
        ->get();
    
    // where参数分组
    DB::table('users')
        ->where('name', '=', 'John')
        ->orWhere(function ($query) {
            $query->where('votes', '>', 100)
                  ->where('title', '<>', 'Admin');
        })
        ->get();

    实际上JoinClause继承自Builder,所以上述代码中的闭包参数$join,后面也是可以链式调用where系列函数的。与Builder对象的区别在于扩展了一个on方法,on方法类似于whereColumn,条件的两边是对表字段的描述。

    Builder调用join方法时传入的条件,会以Nested的类型添加到JoinClause对象当中,然后将JoinClause对象加入到Builder的joins部分。join结构的组装与wheres类似,会单独对JoinClause对象进行一次compileWheres,然后组装到整体SQL语句中:"{$join->type} join {$table} {$this->compileWheres($join)}"

    四、 高级 - 读写分离的实现

    读写分离的问题在connection的范畴。当模型实例化Builder的时候,会先去获取一个connection,如果有配置读写分离,先获取一个writeConnection,然后获取一个readConnection,并绑定到writeConnection上去。

    IlluminateDatabaseConnectorsConnectionFactory
    public function make(array $config, $name = null)
    {
        $config = $this->parseConfig($config, $name);
    
        if (isset($config['read'])) {
            return $this->createReadWriteConnection($config);
        }
    
        return $this->createSingleConnection($config);
    }
    
    protected function createReadWriteConnection(array $config)
    {
        $connection = $this->createSingleConnection($this->getWriteConfig($config));
    
        return $connection->setReadPdo($this->createReadPdo($config));
    }

    注意此时的writeConnection与readConnection并不会真正的连接数据库,而是一个闭包,保存了获取连接的方法,当第一次需要连接数据时,执行闭包获取到连接,并将该连接替换掉闭包,后续执行SQL语句时直接使用该连接即可。在实际使用过程中,可能读写连接的使用并不能简单的按照定义而来,有时需要主动设置要使用的连接。

    4.1 读连接的使用判定

    在配置读写分离后,默认查询会使用readConnection,以下情况会使用writeConnection:

    • 对select操作指定为write:
    // connection 级别指定
    IlluminateDatabaseConnection::select($query, $bindings = [], $useReadPdo = true);
    
    // Builder 级别指定
    DB::table('user')->useWritePdo()->get();
    
    // Model 级别指定
    Model::onWriteConnection()->get()
    • 查询时启用锁
    • 启用事务
    • 启用sticky配置且前文有写操作
    • 在队列执行时,读取SerializesModels的模型数据时

    关于其判定逻辑的代码如下:

    // IlluminateDatabaseConnection::getReadPdo():
    public function getReadPdo()
    {
        if ($this->transactions > 0) {
            return $this->getPdo();
        }
    
        if ($this->getConfig('sticky') && $this->recordsModified) {
            return $this->getPdo();
        }
    
        if ($this->readPdo instanceof Closure) {
            return $this->readPdo = call_user_func($this->readPdo);
        }
    
        return $this->readPdo ?: $this->getPdo();
    }

    五、 进阶 - 关系模型

    关于关系模型的定义,其操作接口全部定义在IlluminateDatabaseEloquentConcernsHasRelationships::trait中。每个关系定义方法,都是对一个关系对象的定义。

    5.1 关系对象

    关系对象全部继承自 IlluminateDatabaseEloquentRelationsRelation::abstract 虚拟类。关系对象由一个查询构造器组成,用来保存由关系定义所决定的关系查询条件,和加载关系时的额外条件。比如一对一(多)的关系定义中:

    public function addConstraints()
    {
        if (static::$constraints) {
            $this->query->where($this->foreignKey, '=', $this->getParentKey());
    
            $this->query->whereNotNull($this->foreignKey);
        }
    }

    每当需要获取关系数据时,都会实例化关系对象,实例化的过程中调用addConstraints方法。与此同时,在加载关系数据时,可以传入额外的查询条件:

    $users = AppUser::with(['posts' => function ($query) {
        $query->where('title', 'like', '%first%');
    }])->get();

    这些条件最终都会保存在关系对象的查询构造器中,在获取关系数据时,起到筛选作用。

    在使用关系模型时,有两种模式:一种是即时加载模式,一种是预加载模式。

    5.2 即时加载

    即时加载关系对象,是基于当前模型对象来获取关系数据。当以$user->post的形式获取Model关系属性时,通过__get方法触发对关系模型的获取。

    public function getAttribute($key)
    {
        if (! $key) {
            return;
        }
    
        // 访问对象属性或存取器
        if (array_key_exists($key, $this->attributes) ||
            $this->hasGetMutator($key)) {
            return $this->getAttributeValue($key);
        }
    
        // 判断同名方法是否存在
        if (method_exists(self::class, $key)) {
            return;
        }
    
        // 获取关系对象
        return $this->getRelationValue($key);
    }

    获取关系模型并实例化,得到关系模型对象,执行关系模型对象的addConstraints方法,将模型对象,转化为关系模型对象的查询条件:

    • 已知模型对象
    • 关系定义绑定对象的模型名称
    • 关系定义外键,已知模型对象的主键,及主键的值 通过上述三个条件,可以生成关系查询,并获取到结果,这个过程是即时加载关系数据的。

    即时加载在只有单个模型对象时比较适用,如果我们拥有的是一个模型集合,并且需要用到关系数据时,通过即时加载的模式,会有N+1的问题。针对每个模型去获取关系数据,都要进行一次数据库查询,这种情况下,就需要使用预加载的模式。

    5.3 预加载

    对于预加载关系的情况,Model::with('relation')标记关系为预加载,在Model::get()获取数据时,检查到对关系的预加载标记,会对关系进行实例化,这个实例化的过程,会通过Relation::noConstraints屏蔽对关系数据的直接加载,在后续过程中,由通过Model::get()获取的模型列表数据,得到模型的ID列表,关系利用这个ID列表,统一查询关系模型数据。查询完成之后匹配到对应的模型中去,其过程如下:

    • EloquentBuilder::get():
      • Builder::get() 获取到模型数据列表
      • EloquentBuilder::eagerLoadRelations(): 获取所有模型关系
        • foreach relations EloquentBuilder::eagerLoadRelation() 针对每个关系获取关系数据
      • Collection: 转化为集合

    其中:eagerLoadRelation()的代码如下

    IlluminateDatabaseEloquentBuilder::eagerLoadRelation()
    protected function eagerLoadRelation(array $models, $name, Closure $constraints)
    {
        // 获取关系对象,这里获取关系对象时会通过Relation::noConstraints屏蔽即时加载
        $relation = $this->getRelation($name);
    
        // 在这里将模型id列表注入到关系对象中,作为关系模型查询的条件
        $relation->addEagerConstraints($models);
    
        // 这里可以注入Model::with(['relation' => function($query){}])时定义的关系额外条件
        $constraints($relation);
    
        // 匹配每个关系对象数据到模型对象中去
        return $relation->match(
            $relation->initRelation($models, $name),
            $relation->getEager(), $name
        );
    }

    六、 总结

    理解laravel的Eloquent ORM模型,可以先建立下列对象的概念:

    • Model,模型对象,编码中比较容易接触与使用的对象,是框架开放给用户的最直观的操作接口;
    • EloquentBuilder,Eloquent查询构造器;
    • Builder,数据库查询构造器,是EloquentBuilder的组成部分;
    • connection,数据库连接对象,与数据库进行交互,执行查询构造器描述的SQL语句;
    • Grammar,语法解析器,将查询构造器的描述解释为规范的SQL语句;
    • Processor,转发查询进程的结果数据;
    • Relation,关系对象,描述两个模型之间的关系,关键是关系之间的查询条件;
    • JoinClause,连接查询对象,多表join查询的实现;

    上述对象的关系如图所示

    cc790876d03ffd814d7982205ba19f4a.png

    当然,Eloquent ORM还有其他跟多的特性,比如数据迁移、数据填充、查询作用域、存取器等,可以留给读者自行去了解与熟悉。

    展开全文
  • 受Laravel Eloquent启发,以Typescript编写的ORM支持Mongodb / Mongoose。 警告:这是v0.4.x的文档,如果您使用的是v0.3.x,请签出自述。 如果您是Laravel Eloquent的爱好者,并且想在Node JS使用它,那么您会喜欢...
  • Laravel Eloquent Query Cache带来了很久以前从Laravel中删除的Remember remember()功能。 它直接在Eloquent级别上添加了缓存功能,从而在数据库查询中使用了缓存。 :handshake: 配套 GitHub上的Renoki Co.致力于...
  • Eloquent Refactor: Search

    2021-01-08 02:47:11
    <div><p>Switch the eloquent module to Eloquent using #2320 as an example. <p>This may well be another module which is almost entirely fluent, but it could end up using some very clevere polymorphic ...
  • Non-Eloquent models

    2020-12-26 04:02:59
    <div><p>Problem with Non-eloquent models. I have 4 Eloquent Resource type and 2 Non-Eloquent. For the dev purpose Im using Laradock and in that dev environment all 6 resources works fine. But, when I ...
  • <div><p>We should add support for <a href="https://laravel.com/docs/6.x/eloquent-resources">Eloquent resources</a>. <p>With the help of PHPStan generics, this is really easy to do. </p><p>该提问来源于...
  • 集成laravel框架Eloquent

    2021-01-12 15:54:31
    <div><p>| Q | A | ------------------- | -----...<p>laravel框架Eloquent的作为ORM很强大,能否把Eloquent集成进来,底层可能需要修改下</p><p>该提问来源于开源项目:swoft-cloud/swoft</p></div>
  • Laravel开发-eloquent-user

    2019-08-28 09:58:54
    Laravel开发-eloquent-user 雄辩的用户
  • Laravel开发-eloquent-otf

    2019-08-27 11:49:03
    Laravel开发-eloquent-otf 暂无描述
  • <div><p>It would be very nice if this package supported native <a href="https://laravel.com/docs/5.7/eloquent-resources">Eloquent API Resources</a> in addition to 3rd party Fractal transformers....
  • 一个简单的嵌入式解决方案,用于为Eloquent模型的ID提供UUID支持。 现成的v1和v4 ID均受支持,但是如果您需要v3或v5支持,则可以轻松地添加它。 正在安装 请参考下表以获得与已安装的Laravel版本一起使用的正确版本...
  • Laravel开发-slim-eloquent

    2019-08-28 09:29:03
    Laravel开发-slim-eloquent 口若悬河,包括分页
  • Laravel开发-eloquent-uuid

    2019-08-28 08:40:22
    Laravel开发-eloquent-uuid 雄辩模型UUID特征

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,960
精华内容 2,784
关键字:

eloquent