zoukankan      html  css  js  c++  java
  • Laravel5.1 模型--关联关系(复杂)

    关联关系不只是我之前记录的一对一,一对多,多对多这些相对简单的关系,在实际开发中我们会遇到比较复杂的关系。

    远程一对多

    远程一对多听着比较花哨 举个栗子就很清楚了,比如用户和文章是一对多的关系,国家和用户也是一对多的关系,这样看来 用户是可以作为中间关联对象来为国家和文章间建立一对多的关系,如果还是云里雾里 就直接看代码:

    我们创建一个国家表:

    php artisan make:migration create_countries_table --create=countries
        public function up()
        {
            Schema::create('countries', function (Blueprint $table) {
                $table->increments('id');
                $table->string('name');
                $table->timestamps();
            });
        }

    我们需要在user中在增加一列:

    php artisan make:migration insert_country_id_intro_users --table=users
        public function up()
        {
            Schema::table('users', function (Blueprint $table) {
                $table->integer('country_id')->unsigned();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::table('users', function (Blueprint $table) {
                $table->dropColumn('country_id');
            });
        }

    生成表后生成模型:

    php artisan migrate
    php artisan make:model Country

    在tinker中生成两条数据:

    >>> $country = new AppCountry();
    => AppCountry {#708}
    >>> $country->name = 'China';
    => "China"
    >>> $country->save();
    => true
    >>> $country2 = new AppCountry();
    => AppCountry {#709}
    >>> $country2->name = 'America';
    => "America"
    >>> $country2->save();
    => true

    post文章的东西在简单关联中已经生成过了,就不在这说了。

    现在来搞一搞远程一对多,在Country中添加方法:

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

    使用远程一对多方法hasManyThrough(),其中第一个参数是需要关联到的对象类名,第二个参数是中间关联对象类名。

    如果users表中表示用户对应国家的字段不是county_id(假设为$country_id),并且posts表中表示文章所属用户的字段不是user_id(假设为$user_id),我们可以传递更多参数到hasManyThrough方法

        public function posts()
        {
            return $this->hasManyThrough('AppModelsPost','AppUser',$country_id,$user_id);
        }

    来看看测试代码:

    Route::get('/', function () {
        $country = AppCountry::find(1);
        $posts = $country->posts;
        echo $country->name . '作者的文章有:' . '<br />';
        foreach ($posts as $post){
            echo $post->title . '<br />';
        }
    });

    多态关联

    多态关联用一个很简单的例子就可以说清楚,比如评论,现在我们不只有文章这一个表了,还有一个视频表,用户可以评论文章也可以评论视频,当然我们还需要一张评论表,但是一条评论可以属于一篇文章 又可以属于一段视频,解决这个关系就需要用到多态关联,在评论表添加item_id字段来存储归属模型的ID,再添加一个item_type来存储归属模型的类型 如:AppPost或AppVideo,来吧 上代码:

    生成评论表和视频表 并自行添加数据:

            Schema::create('comments', function (Blueprint $table) {
                $table->increments('id');
                $table->text('content');
                $table->integer('item_id')->unsigned();
                $table->integer('user_id')->unsigned();
                $table->string('item_type');
                $table->timestamps();
            });
            Schema::create('videos', function (Blueprint $table) {
                $table->increments('id');
                $table->string('title');
                $table->text('content');
                $table->text('desc');
                $table->integer('user_id')->unsigned();
                $table->timestamps();
            });

    创建Post和Video模型 并定义这个方法:

        public function comments()
        {
            return $this->morphMany(Comment::class, 'item');
        }

    其中第一个参数是关联模型类名,第二个参数是关联名称,即$item_id$item_type中的$item部分。当然也可以传递完整参数到morphMany方法:

    $this->morphMany('AppModelsComment',$item,$item_type,$item_id,$id);

    如果需要也可以在Comment模型中定义相对的关联关系获取其所属节点:

    public function item()
    {
        return $this->morphTo();
    }

    如果$item部分不等于item可以自定义传入参数到morphTo

    $this->morphTo($item,$item_type,$item_id);

    OK,完成 测试代码:

    Route::get('/', function () {
        $video = AppVideo::find(1);
        echo $video->title . '所有的评论:'. '<br />';
        foreach ($video->comments as $comment){
            echo $comment->content . '<br />';
        }
    });

    多对多多态关联

    多态关联之后还有一个更加复杂的关联——多对多的多态关联,这种关联最常见的应用场景就是标签,比如一篇文章对应多个标签,一个视频也对应多个标签,同时一个标签可能对应多篇文章或多个视频,这就是所谓的“多对多多态关联”。此时仅仅在标签表tags上定义一个item_iditem_type已经不够了,因为这个标签可能对应多个文章或视频,那么如何建立关联关系呢,我们可以通过一张中间表taggables来实现:该表中定义了文章/视频与标签的对应关系。

     我们创建tag表和其对应的模型类:

        public function up()
        {
            Schema::create('tags', function (Blueprint $table) {
                $table->increments('id');
                $table->string('name');
                $table->timestamps();
            });
        }

    创建taggables表和对应的模型:

        public function up()
        {
            Schema::create('taggables', function (Blueprint $table) {
                $table->increments('id');
                // 对应着文章或视频的id
                $table->integer('taggable_id')->unsigned();
                // 对应是文章类型还是视频类型
                $table->string('taggable_type');
                // 对应是tag表的id
                $table->integer('tag_id')->unsigned();
                $table->timestamps();
            });
        }

    我们在tags表添加几条数据后继续。

    我们在Post模型和Video模型中定义方法:

        public function tags()
        {
            return $this->morphToMany(Tag::class,'taggable');
        }

    其中第一个参数是关联模型类名,第二个参数是关联关系名称,完整的参数列表如下:

    $this->morphToMany('AppModelsTag','taggable','taggable','taggable_id','tag_id',false);

    其中第三个参数是对应关系表名,最后一个值若为true,则查询的是关联对象本身,若为false,查询的是关联对象与父模型的对应关系。

    在Tag中定义相对应的关系:

        public function posts()
        {
            return $this->morphedByMany(Post::class, 'taggable');
        }
    
        public function videos()
        {
            return $this->morphedByMany(Video::class, 'taggable');
        }

    其中第一个参数是关联对象类名,第二个参数是关联关系名称,同理完整参数列表如下:

    $this->morphedByMany('AppModelsVideo','taggable','taggable','tag_id','taggable_id');

    这样关联关系就已经对应好了,现在添加关联表taggable数据:

    >>> $tag = AppTag::find(1);
    => AppTag {#718
         id: 1,
         name: "php教程",
         created_at: "2017-03-30 13:05:07",
         updated_at: "2017-03-30 13:05:07",
       }
    >>> $post = AppPost::find(1);
    => AppPost {#715
         id: 1,
         title: "Molestiae sit quos ut saepe nam ut itaque eos.",
         body: "Consequuntur odio dolores iure nihil distinctio. Sed neque eos aut voluptatem est sit quis quia. Inventore sint sint nesciunt libero dolores. Neque blanditiis sequi odio quia distinctio.",
         views: "0",
         user_id: 1,
         created_at: "2017-03-26 17:25:47",
         updated_at: "2017-03-26 17:25:47",
       }
    >>> $post->tags()->save($tag);
    => AppTag {#718
         id: 1,
         name: "php教程",
         created_at: "2017-03-30 13:05:07",
         updated_at: "2017-03-30 13:05:07",
       }

    测试代码:

    Route::get('/', function () {
        $post = AppPost::find(1);
        $tags = $post->tags;
        dd($tags);
    });
  • 相关阅读:
    框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
    框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
    框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程
    框架源码系列八:Spring源码学习之Spring核心工作原理(很重要)
    框架源码系列七:Spring源码学习之BeanDefinition源码学习(BeanDefinition、Annotation 方式配置的BeanDefinition的解析)
    框架源码系列六:Spring源码学习之Spring IOC源码学习
    框架源码系列五:学习源码的方法(学习源码的目的、 学习源码的方法、Eclipse里面查看源码的常用快捷键和方法)
    框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)
    框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)
    框架源码系列二:手写Spring-IOC和Spring-DI(IOC分析、IOC设计实现、DI分析、DI实现)
  • 原文地址:https://www.cnblogs.com/Alex-sk/p/6624956.html
Copyright © 2011-2022 走看看