ThinkPHP 多态关联

多态关联

多态多态,顾名思义,关联得到的对象类型取决于当前状态。

多态关联允许一个模型在单个关联方法中关联一个以上其它模型。

多态关联是动态的,关联的对象取决于多态字段。

使用多态关联的前提

模型 A 多态关联模型 C,模型 C 对应的数据表必须有两个多态字段,其中一个多态字段存着模型 A 对应的数据表的主键,另一个多态字段存着模型 A 对应的数据表的表名称。

多态关联种类

多态关联分为多态一对多关联和多态一对一关联。

多态一对多关联

以 ThinkPHP 官方手册的例子为例,例如用户可以评论书或文章,但评论表通常都是共用同一张数据表,多态一对多关联关系,就是为了满足类似的使用场景而设计。

以 ThinkPHP 官方手册的例子为例子,下面是关联表的数据表结构:

// 文章表
article
    id - integer // 文章表主键
    title - string // 文章标题
    content - text // 文章内容

// 书籍表
book
    id - integer // 书籍表主键
    title - string // 书籍标题

// 评论表
comment
    id - integer // 评论表主键
    content - text // 评论内容
    commentable_id - integer // 多态 id 字段,当前例子存文章表或书籍表的主键 
    commentable_type - string // 多态类型字段,当前例子存 article 或 book 

一个文章(书籍)有多个评论(一对多),文章表和书籍表共用同一张评论表,这就是多态一对多。如果要使用多态关联,评论表必须设计成这样的结构。

有两个需要注意的字段是 comment 表中的 commentable_id 字段和 commentable_type 字段我们称之为多态字段。其中,commentable_id 用于存放书籍表或者文章表的 id(主键),而 commentable_type 用于存放该评论所属的表的表名称(article 或 book),通过这样的表设计,我们可以清楚的知道当前评论属于哪篇文章或哪本书籍。通常多态字段的设计是有一个公共的前缀(例如这里用的 commentable ),当然,也支持设置完全不同的字段名(例如用 data_id 和 type)。

多态一对多关联定义

文章表模型:

morphMany('Comment', 'commentable');
        // 完整写法 
        // return $this->morphMany('Comment', 'commentable', 'Article');
    }
}

morphMany 方法的参数如下:

morphMany('关联模型', '多态字段前缀或数组', '多态类型');

关联模型(必须):关联的模型名称,可以使用模型名(如 Comment )或者完整的命名空间模型名(如 app\index\model\Comment )。

多态字段前缀或数组(可选):如果是字符串则表示多态字段的前缀(多态前缀_type 和 多态前缀_id) ,如果是数组,使用 ['多态类型字段名', '多态ID字段名'] 的格式,默认为当前的关联方法名作为字段前缀(当前例子默认为 comments)。

多态类型(可选):当前模型对应的多态类型,默认为当前模型名,可以使用模型名(如 Article 或 Book )或者完整的命名空间模型名(如  app\index\model\Article    app\index\model\Book  )。

书籍表模型:

morphMany('Comment', 'commentable');
        // 完整写法 
        // return $this->morphMany('Comment', 'commentable', 'Book');
    }
}

书籍模型的设置方法同文章模型一致,区别在于多态类型不同,但由于多态类型默认会取当前模型名,因此不需要单独设置。

评论表模型

morphTo(); // 当前例子获取 Article 模型或 Book 模型
    }
}

morphTo 方法的参数如下:

morphTo('多态字段前缀或数组', ['多态类型别名']);

多态字段前缀或数组(可选):如果是字符串则表示多态字段的前缀,如果是数组,使用['多态类型字段名', '多态ID字段名'] 的格式,默认为当前的关联方法名作为多态字段前缀(当前例子为 commentable )。

多态类型别名(可选):数组方式定义。

Comment 模型的 commentable 关联会返回 Article 或 Book 模型的对象实例,这取决于评论的多态类型字段值。

morphTo 方法正如我在文章开头说的那样,多态关联允许一个模型在单个关联方法中关联一个以上其它模型。

多态一对一关联

多态一对一关联与多态一对多关联的区别就像一对一关联和一对多关联。以 ThinkPHP 官方手册的例子为例,有一个个人表和一个团队表,而无论是个人还是团队都有一个头像需要保存,而且他们共用同一张头像表,并且无论是个人还是团队都只能拥有一个头像,数据表结构如下

// 会员表
member
    id - integer // 会员表主键
    name - string 
   
// 团队表
team
    id - integer // 团队表主键
    name - string

// 头像表
avatar
    id - integer // 头像表主键
    avatar - string
    avatartable_id - integer // 多态 id 字段,当前例子存会员表主键或团队表主键
    avatartable_type - string // 多态类型字段,当前例子存 member 或 team

一位会员或一个团队都是只有一个头像(一对一),会员表和团队表共用同一张头像表,这就是多态一对一。如果要使用多态关联,头像表必须设计成这样的表结构。

avatar 表中的 avatartable_id ​字段和 avatar_type 字段我们称之为多态字段。其中,avatartable_id 用于存放会员表或者团队表的 id(主键),而 avatar_type 用于存放头像所属的表的表名称( member 或 team) ,通过这样的表设计,我们可以清楚的知道当前头像属于哪位会员或哪个团队。通常多态字段的设计是有一个公共的前缀(例如这里用的 avatartable),当然,也支持设置完全不同的字段名(例如用 data_id 和 type

会员模型:

morphOne('Avatar', 'avatartable');
        // 完整写法
        // return $this->morphOne('Avatar', 'avatartable', 'Member');
    }
}

morphOne方法的参数如下:

morphOne('关联模型', '多态字段前缀或数组', '多态类型');

关联模型(必须):关联的模型名称,可以使用模型名(如 Avatar)或者完整的命名空间模型名(如 app\index\model\Avatar)。

多态字段前缀或数组(可选):如果是字符串则表示多态字段的前缀(多态前缀_type 和 多态前缀_id) ,如果是数组,使用 ['多态类型字段名', '多态ID字段名'] 的格式,默认为当前的关联方法名作为字段前缀(当前例子默认为 avatar)。

多态类型(可选):当前模型对应的多态类型,默认为当前模型名(Member),可以使用模型名(如 Member)或者完整的命名空间模型名(如 app\index\model\Member)。

团队模型:

morphOne('Avatar', 'avatartable');
        // 完整写法
        // return $this->morphOne('Avatar', 'avatartable', 'Team');
    }
}

团队模型的设置方法同会员模型一致,区别在于多态类型不同,但由于多态类型默认会取当前模型名,因此不需要单独设置。

头像模型:

morphTo(); // 当前例子获取 Member 模型或 Team 模型
    }
}

morphTo 方法的参数如下:

morphTo('多态字段前缀或数组', ['多态类型别名']);

多态字段前缀或数组(可选):如果是字符串则表示多态字段的前缀,如果是数组,使用['多态类型字段名',  '多态ID字段名'] 的格式,默认为当前的关联方法名作为多态字段前缀(当前例子默认为 avatartable )。

多态类型别名(可选):数组方式定义。

Avatar 模型的 avatartable 关联会返回 Member 或 Team 模型的对象实例,这取决于头像的多态类型字段值。

morphTo 方法正像我在文章开头说的那样,多态关联允许一个模型在单个关联方法中关联一个以上其它模型。

理解了多态一对多关联后,多态一对一关联其实就很容易理解了,区别就是当前模型和动态关联的模型之间的关联属于一对一关系。

总结

多态多态,顾名思义,关联得到的对象类型取决于当前状态。

多态关联允许一个模型在单个关联方法中关联一个以上其它模型。

多态关联是动态的,关联的对象取决于多态字段。

实例演示

shop_article 表

ThinkPHP 多态关联_第1张图片

 shop_comment 表

ThinkPHP 多态关联_第2张图片



shop_video表

ThinkPHP 多态关联_第3张图片

演示1

获取 article_id 为 1 的文章详情以及属于它的评论,一篇文章有多个评论,所以使用的是多态一对多 morphMany 方法。

Article 控制器

Article 模型

morphMany('Comment', 'comment_table', 'article');
    }
}

Comment 模型

Article 控制器 的 detail 方法输出如下

array(7) {
  ["article_id"] => int(1)
  ["title"] => string(12) "测试标题"
  ["content"] => string(12) "测试内容"
  ["user_id"] => int(1)
  ["create_time"] => string(19) "2023-07-15 14:12:46"
  ["update_time"] => string(19) "2023-07-15 14:12:46"
  ["comments"] => object(think\Collection)#50 (1) {
    ["items":protected] => array(2) {
      [0] => object(app\api\model\Comment)#45 (2) {
        ["data"] => array(6) {
          ["comment_id"] => int(1)
          ["content"] => string(30) "文章写得很好,点个赞"
          ["comment_table_id"] => int(1)
          ["comment_table_type"] => string(7) "article"
          ["create_time"] => int(1689401637)
          ["update_time"] => int(1689401637)
        }
        ["relation"] => array(0) {
        }
      }
      [1] => object(app\api\model\Comment)#46 (2) {
        ["data"] => array(6) {
          ["comment_id"] => int(2)
          ["content"] => string(36) "文章写得很好,下次别写了"
          ["comment_table_id"] => int(1)
          ["comment_table_type"] => string(7) "article"
          ["create_time"] => int(1689403338)
          ["update_time"] => int(1689403338)
        }
        ["relation"] => array(0) {
        }
      }
    }
  }
}

关联方法名 comments 作为键名,关联查询结果集对象 Collection 作为键值。

使用到的 SQL

SELECT * FROM `shop_article` WHERE `article_id` = 1 LIMIT 1 

SELECT * FROM `shop_comment` WHERE `comment_table_id` = 1 AND `comment_table_type` = 'article' 

获取文章的评论

comments);
    }


}

访问关联方法名即可,输出如下

object(think\Collection)#50 (1) {
  ["items":protected] => array(2) {
    [0] => object(app\api\model\Comment)#45 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(1)
        ["content"] => string(30) "文章写得很好,点个赞"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(7) "article"
        ["create_time"] => int(1689401637)
        ["update_time"] => int(1689401637)
      }
      ["relation"] => array(0) {
      }
    }
    [1] => object(app\api\model\Comment)#46 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(2)
        ["content"] => string(36) "文章写得很好,下次别写了"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(7) "article"
        ["create_time"] => int(1689403338)
        ["update_time"] => int(1689403338)
      }
      ["relation"] => array(0) {
      }
    }
  }
}

演示二

获取所有的文章以及属于它自己的评论

Article 控制器

Article 模型

select();
    }

    // 多态一对多获取文章的所有评论
    public function comments()
    {
        return $this->morphMany('Comment', 'comment_table', 'article');
    }
}

Article 控制器的 list 方法输出

object(think\Collection)#43 (1) {
  ["items":protected] => array(2) {
    [0] => object(app\api\model\Article)#41 (2) {
      ["data"] => array(6) {
        ["article_id"] => int(1)
        ["title"] => string(12) "测试标题"
        ["content"] => string(12) "测试内容"
        ["user_id"] => int(1)
        ["create_time"] => int(1689401566)
        ["update_time"] => int(1689401566)
      }
      ["relation"] => array(1) {
        ["comments"] => object(think\Collection)#51 (1) {
          ["items":protected] => array(2) {
            [0] => object(app\api\model\Comment)#46 (2) {
              ["data"] => array(6) {
                ["comment_id"] => int(1)
                ["content"] => string(30) "文章写得很好,点个赞"
                ["comment_table_id"] => int(1)
                ["comment_table_type"] => string(7) "article"
                ["create_time"] => int(1689401637)
                ["update_time"] => int(1689401637)
              }
              ["relation"] => array(0) {
              }
            }
            [1] => object(app\api\model\Comment)#47 (2) {
              ["data"] => array(6) {
                ["comment_id"] => int(2)
                ["content"] => string(36) "文章写得很好,下次别写了"
                ["comment_table_id"] => int(1)
                ["comment_table_type"] => string(7) "article"
                ["create_time"] => int(1689403338)
                ["update_time"] => int(1689403338)
              }
              ["relation"] => array(0) {
              }
            }
          }
        }
      }
    }
    [1] => object(app\api\model\Article)#42 (2) {
      ["data"] => array(6) {
        ["article_id"] => int(2)
        ["title"] => string(13) "测试标题1"
        ["content"] => string(13) "测试内容1"
        ["user_id"] => int(1)
        ["create_time"] => int(1689498967)
        ["update_time"] => int(1689498967)
      }
      ["relation"] => array(1) {
        ["comments"] => object(think\Collection)#52 (1) {
          ["items":protected] => array(0) {
          }
        }
      }
    }
  }
}

每个文章模型对象的 relation 属性(关联数组)保存着关联查询得到的结果集对象,其中键名是所用到的关联方法名,键值是结果集对象,结果集对象的 items 属性(数组)保存着关联查询结果。

使用的 SQL

SELECT * FROM `shop_article` 

SELECT * FROM `shop_comment` WHERE `comment_table_id` IN (1,2) AND `comment_table_type` = 'article'

获取每篇文章的评论

访问关联方法名即可,输出如下

object(think\Collection)#51 (1) {
  ["items":protected] => array(2) {
    [0] => object(app\api\model\Comment)#46 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(1)
        ["content"] => string(30) "文章写得很好,点个赞"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(7) "article"
        ["create_time"] => int(1689401637)
        ["update_time"] => int(1689401637)
      }
      ["relation"] => array(0) {
      }
    }
    [1] => object(app\api\model\Comment)#47 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(2)
        ["content"] => string(36) "文章写得很好,下次别写了"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(7) "article"
        ["create_time"] => int(1689403338)
        ["update_time"] => int(1689403338)
      }
      ["relation"] => array(0) {
      }
    }
  }
}
object(think\Collection)#52 (1) {
  ["items":protected] => array(0) {
  }
}

演示三

 获取 comment_id 为 1 的评论详情以及关联它的多态类型,使用的是 morphTo 方法。

comment 控制器

comment 模型

morphTo();
        // 完整写法
        // return $this->morphTo('comment_table');
    }
    

}

comment 控制器的 detail 方法输出

array(7) {
  ["comment_id"] => int(1)
  ["content"] => string(30) "文章写得很好,点个赞"
  ["comment_table_id"] => int(1)
  ["comment_table_type"] => string(7) "article"
  ["create_time"] => string(19) "2023-07-15 14:13:57"
  ["update_time"] => string(19) "2023-07-15 14:13:57"
  ["comment_table"] => array(6) {
    ["article_id"] => int(1)
    ["title"] => string(12) "测试标题"
    ["content"] => string(12) "测试内容"
    ["user_id"] => int(1)
    ["create_time"] => string(19) "2023-07-15 14:12:46"
    ["update_time"] => string(19) "2023-07-15 14:12:46"
  }
}

关联方法名 comment_table 作为键名,关联查询结果作为键值。

使用的 SQL

SELECT * FROM `shop_comment` WHERE `comment_id` = 1 LIMIT 1 

SELECT * FROM `shop_article` WHERE `article_id` = 1 LIMIT 1 

获取评论关联的多态类型

comment_table);
    }

}

访问关联方法名即可,输出如下

array(6) {
  ["article_id"] => int(1)
  ["title"] => string(12) "测试标题"
  ["content"] => string(12) "测试内容"
  ["user_id"] => int(1)
  ["create_time"] => string(19) "2023-07-15 14:12:46"
  ["update_time"] => string(19) "2023-07-15 14:12:46"
}

演示四

获取所有评论以及评论对应的多态类型

comment 控制器

comment 模型

select();
    }


    // 获取评论对应的多态模型
    public function commentTable()
    {
        return $this->morphTo();
    }
    

}

Comment 控制器 list 方法输出

object(think\Collection)#44 (1) {
  ["items":protected] => array(3) {
    [0] => object(app\api\model\Comment)#40 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(1)
        ["content"] => string(30) "文章写得很好,点个赞"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(7) "article"
        ["create_time"] => int(1689401637)
        ["update_time"] => int(1689401637)
      }
      ["relation"] => array(1) {
        ["comment_table"] => object(app\api\model\Article)#47 (2) {
          ["data"] => array(6) {
            ["article_id"] => int(1)
            ["title"] => string(12) "测试标题"
            ["content"] => string(12) "测试内容"
            ["user_id"] => int(1)
            ["create_time"] => int(1689401566)
            ["update_time"] => int(1689401566)
          }
          ["relation"] => array(0) {
          }
        }
      }
    }
    [1] => object(app\api\model\Comment)#42 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(2)
        ["content"] => string(36) "文章写得很好,下次别写了"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(7) "article"
        ["create_time"] => int(1689403338)
        ["update_time"] => int(1689403338)
      }
      ["relation"] => array(1) {
        ["comment_table"] => object(app\api\model\Article)#47 (2) {
          ["data"] => array(6) {
            ["article_id"] => int(1)
            ["title"] => string(12) "测试标题"
            ["content"] => string(12) "测试内容"
            ["user_id"] => int(1)
            ["create_time"] => int(1689401566)
            ["update_time"] => int(1689401566)
          }
          ["relation"] => array(0) {
          }
        }
      }
    }
    [2] => object(app\api\model\Comment)#43 (2) {
      ["data"] => array(6) {
        ["comment_id"] => int(3)
        ["content"] => string(30) "视频做得很好,点个赞"
        ["comment_table_id"] => int(1)
        ["comment_table_type"] => string(5) "video"
        ["create_time"] => int(1689473711)
        ["update_time"] => int(1689473711)
      }
      ["relation"] => array(1) {
        ["comment_table"] => object(app\api\model\Video)#46 (2) {
          ["data"] => array(5) {
            ["video_id"] => int(1)
            ["title"] => string(12) "视频测试"
            ["user_id"] => int(1)
            ["create_time"] => int(1689473536)
            ["update_time"] => int(1689473536)
          }
          ["relation"] => array(0) {
          }
        }
      }
    }
  }
}

使用的 SQL

SELECT * FROM `shop_comment`

SELECT * FROM `shop_article` WHERE `article_id` = 1

SELECT * FROM `shop_video` WHERE `video_id` = 1 

获取每个评论关联的多态类型

访问关联方法名即可,输出如下

array(6) {
  ["article_id"] => int(1)
  ["title"] => string(12) "测试标题"
  ["content"] => string(12) "测试内容"
  ["user_id"] => int(1)
  ["create_time"] => string(19) "2023-07-15 14:12:46"
  ["update_time"] => string(19) "2023-07-15 14:12:46"
}
array(6) {
  ["article_id"] => int(1)
  ["title"] => string(12) "测试标题"
  ["content"] => string(12) "测试内容"
  ["user_id"] => int(1)
  ["create_time"] => string(19) "2023-07-15 14:12:46"
  ["update_time"] => string(19) "2023-07-15 14:12:46"
}
array(5) {
  ["video_id"] => int(1)
  ["title"] => string(12) "视频测试"
  ["user_id"] => int(1)
  ["create_time"] => string(19) "2023-07-16 10:12:16"
  ["update_time"] => string(19) "2023-07-16 10:12:16"
}

如果觉得作者写得好,请帮我点个赞,谢谢。

你可能感兴趣的:(数据库,sql,php,ThinkPHP)