hyperf 二十一 数据库 模型关系

这篇具有很好参考价值的文章主要介绍了hyperf 二十一 数据库 模型关系。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

教程:Hyperf

一 定义关联

根据文档

  • 一对一:Model::hasOne(被关联模型,被关联模型外键,本模型被关联的字段)
  • 一对多:Model::hasMany(被关联模型,被关联模型外键,本模型被关联的字段)
  • 反向一对多:Model::belongsTo(被关联模型,本模型外键,被关联模型的对应字段,关联关系)
  • 多对多:Model::belongsToMany(被关联模型,自定义连接表名,该模型在连接表里的外键名,被关联模型在连接表里的外键名,该模型关联键,被关联模型关联键,关联关系)

1.1 一对一、一对多

根据文档说明,需要在model中设置方法调用hasOne()方法。获取的使用在查询出来的数据中获取对应方法名的属性。

#model
class User extends Model
{
    public function role()
    {
        return $this->hasOne(Role::class, 'user_id', 'id');
    }
    public function articles() {
        return $this->hasMany(Article::class, 'user_id', 'id');
    }
}
class Article extends Model {
    public function author() {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }
}

#查询
//一对一
$role = User::query()->find(1)->role;
//返回Role类对象

//一对多
$info = User::query()->find(1)->articles;
//返回Hyperf\Database\Model\Collection类

//一对多反向
$info = Article::find(1)->author;
//返回App1\Model\User




 根据上面的例子,实际上find(1)查询user表id为1的行,获取role时调用role()方法,并且结果写入user对象的relations属性中。

model中定义的方法返回Relation对象,调用model的对应属性时执行Hyperf\Database\Model\Model::__get(),使用Hyperf\Database\Model\Concerns\HasAttributes::getRelationValue()设置Moel::relations属性,并设置执行结果。Relation对象是包含查询信息的Builder对象。

一对多和一对一都是调用相同父类,通过HasOneOrMany::matchOneOrMany()通过最后参数$type的值'one'、'many'处理区分。

1.2 多对多

和一对多、一对一流程一样,但是用的父类不用,使用Hyperf\Database\Model\Relations\BelongsToMany::addConstraints()构造查询,被调用对应属性时执行查询。

BelongsToMany::performJoin()以设置的model作为基础表关联中间表,BelongsToMany::addWhereConstraints()关联被调用的model。

#数据库
CREATE TABLE `role_user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `roles` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `role_name` varchar(255) DEFAULT NULL,
  `status` tinyint(1) DEFAULT NULL COMMENT '状态 1可用 0不可用',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `userinfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` tinyint(2) DEFAULT '0',
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;


#模型设置
class User extends Model {
    public function role() {
        return $this->belongsToMany(Role::class);
    }
}

#查询
public function testmodel2() {
     $obj2 = User::query()->find(1);
     $list = $obj2->role;
     foreach ($list as $key => $value) {
            $role_id = $value->pivot->role_id;
            $user_id = $value->pivot->user_id;
            var_dump($role_id, $user_id);
     }
}
#结果
int(1)
int(1)
int(2)
int(1)

 hyperf 二十一 数据库 模型关系,php,php,hyperf

 实际Hyperf\Database\Model\Concerns\HasRelationships::belongsToMany()参数包括:

table 中间表、foreignPivotKey 中间表外键、relatedPivotKey 中间表关联键、parentKey 调用模型主键、relatedKey 设置模型主键、relation 关系。

在上面例子中,User::query()->find(1)->role中User为调用模型,Role为设置的模型,中间模型为RoleUser。belongsToMany()参数默认值为role_user、user_id、role_id、id、id、role。

例子中其他值未设置,因为关联表中对应的id都是为对应的表名加id,符合框架设置默认值的格式。若把role_user中rule_id设置为rule1_id,查询会报错找不到role_user.rule_id字段。

1.2.1 获取中间表字段

中间表获取通过pivot获取,其属性名可改。BelongsToMany::accessor默认pivot,可使用BelongsToMany::as()设置accessor属性名。

通过BelongsToMany::match()调用BelongsToMany::buildDictionary()设置$this->accessor的值。

一对一、一对多、多对多的过程中math()执行都是通过Builder::get()执行。

#model
 public function role() {
        return $this->belongsToMany(Role::class)->as("role");
 }

#测试
$obj2 = User::query()->find(1);
$list = $obj2->role;
foreach ($list as $key => $value) {
   $role_id = $value->role->role_id;
   $user_id = $value->role->user_id;
   var_dump($role_id, $user_id);
}

#测试结果
int(1)
int(1)
int(2)
int(1)

1.2.2 通过中间表过滤关系

并且可以对中间表设置查询条件,比如BelongsToMany::wherePivot()、BelongsToMany::wherePivotIn()、BelongsToMany::wherePivotIn()、BelongsToMany::orWherePivot()、BelongsToMany::withPivotValue()等,可以设置Builder的where的方法。

#model
 public function role() {
        return $this->belongsToMany(Role::class)->as("role")->wherePivot('role_id', "=", 1);
    }

#测试
public function testmodel2() {
        $log = User::query()->getConnection()->enableQueryLog();
        $obj2 = User::query()->find(1);
        $list = $obj2->role;
        foreach ($list as $key => $value) {
            $role_id = $value->role->role_id;
            $user_id = $value->role->user_id;
            var_dump($role_id, $user_id);
        }
        $log = User::query()->getConnection()->getQueryLog();
        var_dump($log);
}

#测试结果
int(1)
int(1)
array(2) {
  [0]=>
  array(3) {
    ["query"]=>
    string(94) "select * from `userinfo` where `userinfo`.`id` = ? and `userinfo`.`deleted_at` is null limit 1"
    ["bindings"]=>
    array(1) {
      [0]=>
      int(1)
    }
    ["time"]=>
    float(47.56)
  }
  [1]=>
  array(3) {
    ["query"]=>
    string(238) "select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = ? and `role_user`.`role_id` = ?"
    ["bindings"]=>
    array(2) {
      [0]=>
      int(1)
      [1]=>
      int(1)
    }
    ["time"]=>
    float(2.79)
  }
}

 原本想把中间表的条件放到controller层,就是通过参数设置。那么model需要改为role($roleid),controller层调用就得是User::query->find(1)->role(1),但是结果并没有执行第二次包含join的查询。因为Hyperf\Database\Model\Model::__get()和Hyperf\Database\Model\Model::__call()逻辑不同。所以中间表的过滤条件,controller大概不能控制。

二 源码

3.1 match()

3.1.1 多对多

public function match(array $models, Collection $results, $relation)
    {
        $dictionary = $this->buildDictionary($results);

        // Once we have an array dictionary of child objects we can easily match the
        // children back to their parent using the dictionary and the keys on the
        // the parent models. Then we will return the hydrated models back out.
        foreach ($models as $model) {
            if (isset($dictionary[$key = $model->{$this->parentKey}])) {
                $model->setRelation(
                    $relation,
                    $this->related->newCollection($dictionary[$key])
                );
            }
        }

        return $models;
    }
protected function buildDictionary(Collection $results)
    {
        // First we will build a dictionary of child models keyed by the foreign key
        // of the relation so that we will easily and quickly match them to their
        // parents without having a possibly slow inner loops for every models.
        $dictionary = [];

        foreach ($results as $result) {
            $dictionary[$result->{$this->accessor}->{$this->foreignPivotKey}][] = $result;
        }

        return $dictionary;
    }

3.1.2 一对一、一对多

#Hyperf\Database\Model\Relations\HasOneOrMany
public function matchOne(array $models, Collection $results, $relation) {
        return $this->matchOneOrMany($models, $results, $relation, 'one');
    }
public function matchMany(array $models, Collection $results, $relation) {
        return $this->matchOneOrMany($models, $results, $relation, 'many');
    }

protected function matchOneOrMany(array $models, Collection $results, $relation, $type) {
        $dictionary = $this->buildDictionary($results);

        // Once we have the dictionary we can simply spin through the parent models to
        // link them up with their children using the keyed dictionary to make the
        // matching very convenient and easy work. Then we'll just return them.
        foreach ($models as $model) {
            if (isset($dictionary[$key = $model->getAttribute($this->localKey)])) {
                $model->setRelation(
                    $relation,
                    $this->getRelationValue($dictionary, $key, $type)
                );
            }
        }

        return $models;
    }
protected function getRelationValue(array $dictionary, $key, $type) {
        $value = $dictionary[$key];

        return $type === 'one' ? reset($value) : $this->related->newCollection($value);
    }



#Hyperf\Database\Model\Relations\HasOne
public function match(array $models, Collection $results, $relation) {
        return $this->matchOne($models, $results, $relation);
    }

#Hyperf\Database\Model\Relations\HasMany
public function match(array $models, Collection $results, $relation)
    {
        return $this->matchMany($models, $results, $relation);
    }


#Hyperf\Database\Model\Concerns\HasRelationships
public function hasOne($related, $foreignKey = null, $localKey = null) {
        $instance = $this->newRelatedInstance($related);

        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $localKey = $localKey ?: $this->getKeyName();

        return $this->newHasOne($instance->newQuery(), $this, $instance->getTable() . '.' . $foreignKey, $localKey);
    }
public function hasMany($related, $foreignKey = null, $localKey = null) {
        $instance = $this->newRelatedInstance($related);

        $foreignKey = $foreignKey ?: $this->getForeignKey();

        $localKey = $localKey ?: $this->getKeyName();

        return $this->newHasMany(
            $instance->newQuery(),
            $this,
            $instance->getTable() . '.' . $foreignKey,
            $localKey
        );
    }
protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey) {
        return new HasOne($query, $parent, $foreignKey, $localKey);
    }
protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey) {
        return new HasMany($query, $parent, $foreignKey, $localKey);
    }

3.1.3 调用

#Hyperf\Database\Model\Builder
protected function eagerLoadRelation(array $models, $name, Closure $constraints)
    {
        // First we will "back up" the existing where conditions on the query so we can
        // add our eager constraints. Then we will merge the wheres that were on the
        // query back to it in order that any where conditions might be specified.
        $relation = $this->getRelation($name);

        $relation->addEagerConstraints($models);

        $constraints($relation);

        // Once we have the results, we just match those back up to their parent models
        // using the relationship instance. Then we just return the finished arrays
        // of models which have been eagerly hydrated and are readied for return.
        return $relation->match(
            $relation->initRelation($models, $name),
            $relation->getEager(),
            $name
        );
    }
public function eagerLoadRelations(array $models)
    {
        foreach ($this->eagerLoad as $name => $constraints) {
            // For nested eager loads we'll skip loading them here and they will be set as an
            // eager load on the query to retrieve the relation so that they will be eager
            // loaded on that query, because that is where they get hydrated as models.
            if (strpos($name, '.') === false) {
                $models = $this->eagerLoadRelation($models, $name, $constraints);
            }
        }

        return $models;
    }
public function get($columns = ['*'])
    {
        $builder = $this->applyScopes();

        // If we actually found models we will also eager load any relationships that
        // have been specified as needing to be eager loaded, which will solve the
        // n+1 query issue for the developers to avoid running a lot of queries.
        if (count($models = $builder->getModels($columns)) > 0) {
            $models = $builder->eagerLoadRelations($models);
        }

        return $builder->getModel()->newCollection($models);
    }
#Hyperf\Database\Model\Model 
public function __get($key)
    {
        return $this->getAttribute($key);
    }

#Hyperf\Database\Model\Concerns\HasAttributes
public function getAttribute($key)
    {
        if (!$key) {
            return;
        }

        // If the attribute exists in the attribute array or has a "get" mutator we will
        // get the attribute's value. Otherwise, we will proceed as if the developers
        // are asking for a relationship's value. This covers both types of values.
        if (array_key_exists($key, $this->getAttributes())
            || $this->hasGetMutator($key)
            || $this->isClassCastable($key)) {
            return $this->getAttributeValue($key);
        }
        // Here we will determine if the model base class itself contains this given key
        // since we don't want to treat any of those methods as relationships because
        // they are all intended as helper methods and none of these are relations.
        if (method_exists(self::class, $key)) {
            return;
        }
        return $this->getRelationValue($key);
    }
public function getRelationValue($key)
    {
        // If the key already exists in the relationships array, it just means the
        // relationship has already been loaded, so we'll just return it out of
        // here because there is no need to query within the relations twice.
        if ($this->relationLoaded($key)) {
            return $this->relations[$key];
        }

        // If the "attribute" exists as a method on the model, we will just assume
        // it is a relationship and will load and return results from the query
        // and hydrate the relationship's value on the "relationships" array.
        if (method_exists($this, $key)) {
            return $this->getRelationshipFromMethod($key);
        }
    }
protected function getRelationshipFromMethod($method)
    {
        $relation = $this->{$method}();

        if (!$relation instanceof Relation) {
            if (is_null($relation)) {
                throw new LogicException(sprintf(
                    '%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?',
                    static::class,
                    $method
                ));
            }

            throw new LogicException(sprintf(
                '%s::%s must return a relationship instance.',
                static::class,
                $method
            ));
        }

        return tap($relation->getResults(), function ($results) use ($method) {
            $this->setRelation($method, $results);
        });
    }

#Hyperf\Database\Model\Relations\HasMany
public function getResults()
    {
        return $this->query->get();
    }
#Hyperf\Database\Model\Relations\HasOne
public function getResults() {
        return $this->query->first() ?: $this->getDefaultFor($this->parent);
    }
#Hyperf\Database\Model\Relations\BelongsToMany
public function getResults()
    {
        return $this->get();
    }
#Hyperf\Database\Model\Relations\BelongsTo
public function getResults()
    {
        return $this->query->first() ?: $this->getDefaultFor($this->parent);
    }

3.2 pivot属性名自定义

#Hyperf\Database\Model\Relations\BelongsToMany
protected $accessor = 'pivot';
public function as($accessor)
    {
        $this->accessor = $accessor;

        return $this;
    }

3.3 通过中间件顾虑

#Hyperf\Database\Model\Relations\BelongsToMany
public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
    {
        $this->pivotWheres[] = func_get_args();

        return $this->where($this->table . '.' . $column, $operator, $value, $boolean);
    }
public function wherePivotIn($column, $values, $boolean = 'and', $not = false)
    {
        $this->pivotWhereIns[] = func_get_args();

        return $this->whereIn($this->table . '.' . $column, $values, $boolean, $not);
    }
public function orWherePivot($column, $operator = null, $value = null)
    {
        return $this->wherePivot($column, $operator, $value, 'or');
    }
public function withPivotValue($column, $value = null)
    {
        if (is_array($column)) {
            foreach ($column as $name => $value) {
                $this->withPivotValue($name, $value);
            }

            return $this;
        }

        if (is_null($value)) {
            throw new InvalidArgumentException('The provided value may not be null.');
        }

        $this->pivotValues[] = compact('column', 'value');

        return $this->wherePivot($column, '=', $value);
    }
public function orWherePivotIn($column, $values)
    {
        return $this->wherePivotIn($column, $values, 'or');
    }

3.4 __get()、__call()

#Hyperf\Database\Model\Model
public function __get($key)
    {
        return $this->getAttribute($key);
    }
public function __call($method, $parameters)
    {
        if (in_array($method, ['increment', 'decrement'])) {
            return $this->{$method}(...$parameters);
        }

        return call([$this->newQuery(), $method], $parameters);
    }

三 理解

        不管关系如何,都是先查一遍主表,再执行对应sql,可以通过不同设置拼接查询sql。

        可能是使用习惯问题,平时还是不太喜欢这种比较隐晦的方式设置对应关系,可能用会手动多查几次。

        根据这种方式,在不了解框架的情况下,会增加二开的难度。

        而且设置中间表的条件也不是很自由。文章来源地址https://www.toymoban.com/news/detail-796058.html

到了这里,关于hyperf 二十一 数据库 模型关系的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在Django的数据库操作模型中,怎样获取一对多或多对多关系中的所有“多”?(使用特殊属性:逆向关系管理器)

    当一个模型通过外键或多对多字段与另一个模型建立关联时,Django会自动为该模型生成逆向关系管理器。这个逆向关系管理器的名称是通过在模型类名后面添加 _set 来生成的。在逆向关系管理器上,你可以使用多种查询方法来访问相关对象。 让我们以一个示例来说明。假设你

    2024年02月16日
    浏览(49)
  • Django的mysql数据库问题:同一个模型(同一张表)中的不同记录也是可以相互关联的【使用“自引用关系”】

    是的,确实可以在Django的模型中使用外键来建立同一模型中不同记录之间的关联关系。这样的关联关系被称为自引用关系(self-referential relationship)或者自关联关系。通过在模型中定义外键字段,你可以使模型的实例与同一模型中的其他实例产生关联。 在Django中,这通常通过

    2024年01月18日
    浏览(61)
  • 数据库第十章(数据库恢复技术)十一章(并发控制)

    目录 1.事务 2.并发控制 1.事务 事务的特点:ACID 原子性   atom 一致性   consistent 隔离性   isolation 持久性   durable 故障的种类 1.事务内部故障         措施:采取redo重做和undo撤销技术 2.系统故障DBMS         措施:重启 3.介质故障         硬件损坏 4.计算机病毒   数据恢

    2024年02月09日
    浏览(59)
  • 关系型数据库和非关系型数据库

     关系型数据库是以 关系(表格) 为基础的数据库,它采用了 SQL(Structured Query Language)作为数据操作语言,常见的关系型数据库包括 MySQL、Oracle、SQL Server 等。 非关系型数据库则是基于 文档、键值、列族 等方式存储数据的数据库,它通常没有固定的表结构,因此也被称为

    2024年02月09日
    浏览(45)
  • 关系型数据库与非关系型数据库类比

    关系型数据库和非关系型数据库都有多种不同类型,每种类型都针对不同的数据存储需求和使用场景。以下是一些常见的关系型数据库和非关系型数据库类型: 关系型数据库类型: MySQL: 一种开源的关系型数据库管理系统,用于处理结构化数据,适用于各种规模的应用。

    2024年02月11日
    浏览(40)
  • 数据库原理与应用(SQL)——2、关系数据库(E-R图、关系模式、关系运算、关系代数)

      目录 关系  关系运算   元组、域关系演算表达式  题目  关系代数表达式——例 元组演算表达式——例  域演算表达式——例         关系数据库是以 二维表 形式组织数据,应用数学方法处理数据库组织的方法。目前关系数据库系统在数据管理中已占据了主导地位

    2023年04月08日
    浏览(45)
  • 数据库:关系数据库标准语言(二)

    一章写不完啊,这里全是概念和实战,所以再来一章,希望这章可以学的好一点 连接查询 连接查询 :同时涉及多个表的查询。用来连接两个表的条件称为连接条件或连接谓词 。 连接条件的一般格式 : [表名1]列名1 比较运算符 [表名2]列名2 或 [表名1]列名1 BETWEEN [表名2]列名

    2023年04月09日
    浏览(53)
  • 重学MySQL之关系型数据库和非关系型数据库

    1.1 关系型数据库的特性 1.1.1 事务的特性 事务,是指一个操作序列,这些操作要么都执行,或者都不执行,而且这一序列是无法分隔的独立操作单位。也就是符合原子性(Atomicity)、 一致性(Consistency)、 隔离性(Isolation)和持久性(Durability)的一组操作。 原子性:指一个

    2024年02月02日
    浏览(72)
  • 【数据库概论】图数据库 Vs 关系数据库(1)

    假设有一个社交网络需要用数据库存储,其中人与人之间的关系有:朋友(friend)、父母(parent) 首先用关系数据库来实现朋友关系,需要 3 张表:people、people_relation、relation 如果要查询 Jam 的所有朋友的信息,那么就需要连接三张表: 如果表的数据量较大,那么查询效率就

    2024年03月14日
    浏览(48)
  • 数据库介绍-非关系型数据库

    NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。 NoSQL 不依赖业务逻辑方式存储,数据存储的类型不需要一个固定形式。因此大大的增加了数据库的扩展能力。 不遵循 SQL 标准 不支持 ACID 远超于 SQL 的性能 易扩展 大读写量,高性能 数据模型灵活 高可用

    2024年02月16日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包