hyperf 十九 数据库 二 模型

教程:Hyperf

一、命令行

symfony/console-CSDN博客

hypery 十一、命令行-CSDN博客

hyperf console 执行-CSDN博客

根据之前应该能了解到命令行的基本实现,和hyperf中命令行的定义。

1.1 命令初始化

hyperf.php中系统初始化中通过ApplicationFactory::__invoke(),将配置文件中的commands对应内容,通过Application::add($container->get($command)),设置为Application::command参数的值,类型为数组。

以ModelCommand::configure()为例,通过Hyperf\Config\ProviderConfig::load()进行配置加载,其作用就是将ModelCommand之类的命令类实例化,并设置为ProviderConfig::providerConfigs的值。

实例化的过程中,调用顺序为:

        1、Hyperf\Database\Commands\ModelCommand::__construct()

        2、Hyperf\Command\Command::__construct()

        3、Symfony\Component\Console\Command::__construct()

        4、$this->configure();

 

1.2 调用命令

执行命令通过Symfony\Component\Console\Application::run(),其中总过Application::get()获取Application::command中对应命令的尸体类。

Application::run()会调用Application::doRunCommand(),doRunCommand()中执行对应命令的run()方法。

实际运行以ModelCommand::handle()为例,调用顺序为:

        1、Hyperf\Database\Commands\ModelCommand::run()

        2、Hyperf\Command\Command::run()

        3、Symfony\Component\Console\Command\Command::run()

        4、Hyperf\Command\Command::execute()

        5、Hyperf\Database\Commands\ModelCommand::handle()

1.3 ModelCommand执行

命令执行的入口为ModelCommand::handle(),通过设置参数,获取可处理数据。

若设置table则执行createModel(),否者执行createModels()。createModels()通过循环调用createModel()。

参数中ignore-tables,可设置需要忽略的表,在createModels()中起作用。

参数pool、inheritance、uses,替换对应/stubs/Model.stub文件中的内容。

参数path、prefix、table-mapping,则影响model文件生成。

和文档中不同的参数包括:with-ide、visitors

with-ide:是否生成对应mode的ide文件

visitors:设置ModelUpdateVisitor、ModelRewriteConnectionVisitor之类,用于处理数据的类。可用文件在vendor\hyperf\database\src\Commands\Ast中。可参考:创建脚本 - Hyperf 帮助文档 v2.0 - 开发文档 - 文江博客

二、参数设置

命令行

php bin/hyperf.php gen:model table_name

参数

参数类型默认值备注
--poolstringdefault连接池,脚本会根据当前连接池配置创建
--pathstringapp/Model模型路径
--force-castsboolfalse是否强制重置 casts 参数
--prefixstring空字符串表前缀
--inheritancestringModel父类
--usesstringHyperf\DbConnection\Model\Model配合 inheritance 使用
--refresh-fillableboolfalse是否刷新 fillable 参数
--table-mappingarray[]为表名 -> 模型增加映射关系 比如 ['users:Account']
--ignore-tablesarray[]不需要生成模型的表名 比如 ['users']
--with-commentsboolfalse是否增加字段注释
--property-caseint0字段类型 0 蛇形 1 驼峰

可以通过命令行设置参数,可也在设置中写入参数。

设置中参数设置:

#config\autoload\databases.php
use Hyperf\Database\Commands\ModelOption;return ['default' => [// 忽略其他配置'commands' => ['gen:model' => ['path' => 'app/Model','force_casts' => true,'inheritance' => 'Model','uses' => '','refresh_fillable' => true,'table_mapping' => [],'with_comments' => true,'property_case' => ModelOption::PROPERTY_SNAKE_CASE,],],],
];

 命令中设置参数的时候,通过ModelCommand::getOption(),会将命令行ArgvInput()::getOption(),通过判断是否使用配置中的参数

根据代码,参数中force-casts、refresh-fillable、with-comments、with-ide,若命令行中未传且配置中已设置,则会使用配置中的值。参数中table-mapping、ignore-tables、visitors都为数组,若命令行中未传且配置中已设置,则会使用配置中的值。即配置参数以命令行中参数优先使用

三、测试

3.1 配置

hyperf.php配置

 'commands' => ['gen:model' => ['path' => 'app1/Model','force_casts' => true,'inheritance' => 'Model',],
],

 mysql:

CREATE TABLE `userinfo` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`age` tinyint(2) DEFAULT '0',PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

 composer.json

 "autoload": {"psr-4": {"App\\": "app/","App1\\":"app1/"},"files": []},

3.2 创建

php bin/hyperf.php gen:model --table-mapping='userinfo:User' --prefix=app1 userinfo

执行挺简单一句, 没想到坑还挺多。

首先因为使用的是app1,而非app,所以在composer.json中增加psr-4,因为框架会循环psr-4中的值,根据逻辑判断返回对应的$path,没有匹配的数据则抛出throw。

composer.json修改之后需要composer update。否则对于以上情况,第一次生成model之后,再次处理对应model会报model找不到。

最后发现的一个坑,是文档上写明table-mapping应该是数组,写法应该是["userinfo:User"]。但是特别奇葩的是,创建过程中,将值格式化为["[userinfo"=>"User]"]……后来发现,table-mapping获取的值都是字符串和输入的格式无关,但是之后getTableMapping()没有对字符串进行处理,仅是分割字符串。可能和版本有关,或者因为我输入的格式有误。

还有一个可能是编译器导致的错误,创建的时候有个框架里的文件报错,里面的使用的部分类找不到。原因是没有设置对应的use,但是框架里对应文件存在,补充use语句之后bug解决。

3.3 插入

#model
class User extends Model
{public $timestamps = false;
}#测试
class TestController extends AbstractController
{public function testmodel(){$name = $this->request->input('name');$m_u = new User();$m_u->name = (string) $name;$result = $m_u->save();var_dump($result);}
}#执行结果
true

数据库配置文件在config/autoload/databases.php,但是里面设置的默认值只有在.env中对应值未设置的时候才生效。所以已有.env时,应该再检查下该文件数据库配置是否有误。

默认使用created_at,updated_at字段,设置$timestamps = false后关闭。

3.4 查询

$user = User::query()->where('id', 1)->first();
var_dump($user->name, $user->age);#运行结果
string(3) "123"
int(22)

3.5 软删除

#model
use Hyperf\DbConnection\Model\Model;
use Hyperf\Database\Model\SoftDeletes;
/***/
class User extends Model
{use SoftDeletes;
}#测试代码
$result = User::query()->where(['id' => 23])->delete();
var_dump($result);#测试结果
int(1)

 软删除必须在数据中设置deleted_at字段。

四、源码

4.1 ModelCommand执行

namespace Hyperf\Database\Commands;
class ModelCommand extends Command
{public function handle(){$table = $this->input->getArgument('table');$pool = $this->input->getOption('pool');$option = new ModelOption();$option->setPool($pool)->setPath($this->getOption('path', 'commands.gen:model.path', $pool, 'app/Model'))->setPrefix($this->getOption('prefix', 'prefix', $pool, ''))->setInheritance($this->getOption('inheritance', 'commands.gen:model.inheritance', $pool, 'Model'))->setUses($this->getOption('uses', 'commands.gen:model.uses', $pool, 'Hyperf\DbConnection\Model\Model'))->setForceCasts($this->getOption('force-casts', 'commands.gen:model.force_casts', $pool, false))->setRefreshFillable($this->getOption('refresh-fillable', 'commands.gen:model.refresh_fillable', $pool, false))->setTableMapping($this->getOption('table-mapping', 'commands.gen:model.table_mapping', $pool, []))->setIgnoreTables($this->getOption('ignore-tables', 'commands.gen:model.ignore_tables', $pool, []))->setWithComments($this->getOption('with-comments', 'commands.gen:model.with_comments', $pool, false))->setWithIde($this->getOption('with-ide', 'commands.gen:model.with_ide', $pool, false))->setVisitors($this->getOption('visitors', 'commands.gen:model.visitors', $pool, []))->setPropertyCase($this->getOption('property-case', 'commands.gen:model.property_case', $pool));if ($table) {$this->createModel($table, $option);} else {$this->createModels($option);}}protected function createModel(string $table, ModelOption $option){$builder = $this->getSchemaBuilder($option->getPool());$table = Str::replaceFirst($option->getPrefix(), '', $table);$columns = $this->formatColumns($builder->getColumnTypeListing($table));$project = new Project();$class = $option->getTableMapping()[$table] ?? Str::studly(Str::singular($table));$class = $project->namespace($option->getPath()) . $class;$path = BASE_PATH . '/' . $project->path($class);if (!file_exists($path)) {$this->mkdir($path);file_put_contents($path, $this->buildClass($table, $class, $option));}$columns = $this->getColumns($class, $columns, $option->isForceCasts());$stms = $this->astParser->parse(file_get_contents($path));$traverser = new NodeTraverser();$traverser->addVisitor(make(ModelUpdateVisitor::class, ['class' => $class,'columns' => $columns,'option' => $option,]));$traverser->addVisitor(make(ModelRewriteConnectionVisitor::class, [$class, $option->getPool()]));$data = make(ModelData::class)->setClass($class)->setColumns($columns);foreach ($option->getVisitors() as $visitorClass) {$traverser->addVisitor(make($visitorClass, [$option, $data]));}$stms = $traverser->traverse($stms);$code = $this->printer->prettyPrintFile($stms);file_put_contents($path, $code);$this->output->writeln(sprintf('<info>Model %s was created.</info>', $class));if ($option->isWithIde()) {$this->generateIDE($code, $option, $data);}}
protected function createModels(ModelOption $option){$builder = $this->getSchemaBuilder($option->getPool());$tables = [];foreach ($builder->getAllTables() as $row) {$row = (array) $row;$table = reset($row);if (!$this->isIgnoreTable($table, $option)) {$tables[] = $table;}}foreach ($tables as $table) {$this->createModel($table, $option);}}protected function isIgnoreTable(string $table, ModelOption $option): bool{if (in_array($table, $option->getIgnoreTables())) {return true;}return $table === $this->config->get('databases.migrations', 'migrations');}
}
namespace Hyperf\Utils\CodeGen;
class Project
{public function namespace(string $path): string{$ext = pathinfo($path, PATHINFO_EXTENSION);if ($ext !== '') {$path = substr($path, 0, -(strlen($ext) + 1));} else {$path = trim($path, '/') . '/';}foreach ($this->getAutoloadRules() as $prefix => $prefixPath) {if ($this->isRootNamespace($prefix) || strpos($path, $prefixPath) === 0) {return $prefix . str_replace('/', '\\', substr($path, strlen($prefixPath)));}}throw new \RuntimeException("Invalid project path: {$path}");}protected function getAutoloadRules(): array{return data_get(Composer::getJsonContent(), 'autoload.psr-4', []);}
}
namespace Hyperf\Database\Commands;
class ModelOption
{public function setTableMapping(array $tableMapping): self{foreach ($tableMapping as $item) {[$key, $name] = explode(':', $item);$this->tableMapping[$key] = $name;}return $this;}
}

4.2 $timestamps使用

namespace Hyperf\Database\Model;
abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, CompressInterface
{use Concerns\HasTimestamps;public function save(array $options = []): bool{$this->mergeAttributesFromClassCasts();$query = $this->newModelQuery();// If the "saving" event returns false we'll bail out of the save and return// false, indicating that the save failed. This provides a chance for any// listeners to cancel save operations if validations fail or whatever.if ($saving = $this->fireModelEvent('saving')) {if ($saving instanceof StoppableEventInterface && $saving->isPropagationStopped()) {return false;}}// If the model already exists in the database we can just update our record// that is already in this database using the current IDs in this "where"// clause to only update this model. Otherwise, we'll just insert them.if ($this->exists) {$saved = $this->isDirty() ? $this->performUpdate($query) : true;} else {// If the model is brand new, we'll insert it into our database and set the// ID attribute on the model to the value of the newly inserted row's ID// which is typically an auto-increment value managed by the database.$saved = $this->performInsert($query);if (! $this->getConnectionName() && $connection = $query->getConnection()) {$this->setConnection($connection->getName());}}// If the model is successfully saved, we need to do a few more things once// that is done. We will call the "saved" method here to run any actions// we need to happen after a model gets successfully saved right here.if ($saved) {$this->finishSave($options);}return $saved;}protected function performUpdate(Builder $query){// If the updating event returns false, we will cancel the update operation so// developers can hook Validation systems into their models and cancel this// operation if the model does not pass validation. Otherwise, we update.if ($event = $this->fireModelEvent('updating')) {if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {return false;}}// First we need to create a fresh query instance and touch the creation and// update timestamp on the model which are maintained by us for developer// convenience. Then we will just continue saving the model instances.if ($this->usesTimestamps()) {$this->updateTimestamps();}// Once we have run the update operation, we will fire the "updated" event for// this model instance. This will allow developers to hook into these after// models are updated, giving them a chance to do any special processing.$dirty = $this->getDirty();if (count($dirty) > 0) {$this->setKeysForSaveQuery($query)->update($dirty);$this->syncChanges();$this->fireModelEvent('updated');}return true;}
}

4.4 SoftDeletes使用

namespace Hyperf\Database\Model;
abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, CompressInterface
{public function delete(){$this->mergeAttributesFromClassCasts();if (is_null($this->getKeyName())) {throw new Exception('No primary key defined on model.');}// If the model doesn't exist, there is nothing to delete so we'll just return// immediately and not do anything else. Otherwise, we will continue with a// deletion process on the model, firing the proper events, and so forth.if (! $this->exists) {return;}if ($event = $this->fireModelEvent('deleting')) {if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {return false;}}// Here, we'll touch the owning models, verifying these timestamps get updated// for the models. This will allow any caching to get broken on the parents// by the timestamp. Then we will go ahead and delete the model instance.$this->touchOwners();$this->performDeleteOnModel();// Once the model has been deleted, we will fire off the deleted event so that// the developers may hook into post-delete operations. We will then return// a boolean true as the delete is presumably successful on the database.$this->fireModelEvent('deleted');return true;}
}
namespace App1\Model;use Hyperf\Database\Model\SoftDeletes;
/***/
class User extends Model
{use SoftDeletes;
}
namespace Hyperf\Database\Model;
trait SoftDeletes
{protected function performDeleteOnModel(){if ($this->forceDeleting) {$this->exists = false;return $this->newModelQuery()->where($this->getKeyName(), $this->getKey())->forceDelete();}return $this->runSoftDelete();}protected function runSoftDelete(){$query = $this->newModelQuery()->where($this->getKeyName(), $this->getKey());$time = $this->freshTimestamp();$columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];$this->{$this->getDeletedAtColumn()} = $time;if ($this->timestamps && ! is_null($this->getUpdatedAtColumn())) {$this->{$this->getUpdatedAtColumn()} = $time;$columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);}$query->update($columns);}public function getDeletedAtColumn(){return defined('static::DELETED_AT') ? static::DELETED_AT : 'deleted_at';}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/601834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

乐理燥废笔记

乐理燥废笔记 文章目录 终止式小调音阶转调不协和和弦进行大小转调1251 1451转调我的霹雳猫阿诺三全音代理五声音阶又怎样和弦附录&#xff1a;压缩字符串、大小端格式转换压缩字符串浮点数压缩Packed-ASCII字符串 大小端转换什么是大端和小端数据传输中的大小端总结大小端转换…

Golang高质量编程与性能调优实战

1.1 简介 高质量&#xff1a;编写的代码能否达到正确可靠、简洁清晰的目标 各种边界条件是否考虑完备异常情况处理&#xff0c;稳定性保证易读易维护 编程原则 简单性 消除多余的重复性&#xff0c;以简单清晰的逻辑编写代码不理解的代码无法修复改进 可读性 代码是写给人看…

Copilot在IDEA中的应用:提升编码效率的得力助手

Copilot在IDEA中的应用&#xff1a;提升编码效率的得力助手 前言: 欢迎来到本篇博客&#xff0c;今天我们将深入探讨 GitHub Copilot 在 IntelliJ IDEA 中的应用。GitHub Copilot 是一款由 GitHub 与 OpenAI 共同开发的人工智能代码生成工具&#xff0c;它能够根据上下文提示…

vue 异步加载组件

当页面引入多个组件并且有默认不展示的组件则可以进行异步加载&#xff08;引入的组件不进行初步渲染&#xff0c;只有需要的时候才加载&#xff09; 首先引入 defineAsyncComponent import { defineAsyncComponent } from "vue"; 然后引入组件 组件名自定义&…

【MybatisPlus Docker】

文章目录 MyBatisPlusMyBatisPlus 注解WrapperMybatisPlus批量插入(saveBatch)MyBatisPlus 分页插件 DockerDockerfileDocker网络Docker部署项目 黑马微服务文档 MyBatisPlus MyBatisPlus 注解 TableName TableId TableField MyBatisPlus也支持手写SQL的 mybatis-plus:map…

金山云升级全栈云计算体系,做人工智能时代云上“助力者”

随着云原生、AIGC、大模型等新兴技术的迅速发展&#xff0c;智能化时代开启。云计算也正全面步入3.0时代&#xff0c;即云计算和人工智能深度融合的阶段。在这个阶段&#xff0c;人工智能技术成为云计算进一步释放潜力的核心推动力。 2024年1月4日&#xff0c;金山云举办了「云…

水母目标检测数据集VOC格式500张

水母&#xff0c;一种美丽而神秘的海洋生物&#xff0c;以其独特的形态和生态习性而备受瞩目。 水母的体型呈伞状&#xff0c;身体透明&#xff0c;有各种颜色和花纹。它们没有骨骼&#xff0c;身体由胶状物质组成&#xff0c;非常柔软和脆弱。水母通过触手上的刺细胞释放毒素…

从零学Java Object类

Java Object类 文章目录 Java Object类Object类Object类中的方法1 getClass()方法2 hashCode()方法3 toString()方法4 equals()方法面试题: 和 equals 方法的区别? 5 finalize()方法&#xff08;JDK1.9放弃 了解即可&#xff09; Object类 所有类的直接或间接继承Object&…

TypeScript 从入门到进阶之基础篇(四) symbol类型篇

系列文章目录 TypeScript 从入门到进阶系列 TypeScript 从入门到进阶之基础篇(一) ts基础类型篇TypeScript 从入门到进阶之基础篇(二) ts进阶类型篇TypeScript 从入门到进阶之基础篇(三) 元组类型篇TypeScript 从入门到进阶之基础篇(四) symbol类型篇 持续更新中… 文章目录 …

win10关闭打开文件安全警告

1.使用场景 在流水线上&#xff0c;因车间刷软件的程序不能下发到每一台电脑上&#xff0c;会使用共享文件夹的形式来共享程序。每次打开会弹出一下窗口&#xff0c;影响员工生产。 需要将安全警告关闭。2.解决步骤 1.winR键打开运行窗口&#xff0c;输入gpedit.msc&#xff…

云原生学习系列之基础环境准备(单节点安装kubernetes)

一、环境要求 操作系统CentOS 7.x-86_x64 硬件配置&#xff1a;内存2GB或2G&#xff0c;CPU 2核或CPU 2核&#xff0c;需要在虚拟机中提前设置好&#xff0c;不然后续会报错 二、系统初始化 1、设置主机名 # 在master节点执行 hostnamectl set-hostname master01 2、配置主…

HTML的简单介绍

文章目录 1. HTML1.1 HTML 基础认识1.2 快速生成代码框架1.3 HTML 基础标签 1. HTML 1.1 HTML 基础认识 什么是HTML呢&#xff1f; HTML叫做超文本标记语言。超文本&#xff1a;例如图片&#xff0c;视频&#xff0c;文本&#xff0c;声音&#xff0c;表格&#xff0c;链接等…

C++的一点知识记录

时间记录&#xff1a;2024/1/6 类定义&#xff0c;一般在头文件中进行定义不进行实现&#xff0c;在源文件中进行实现 class Person{ public://公共属性和方法定义区Person();//构造函数定义~Person(){};//析构函数加实现int age;//定义属性void printAge();//定义方法 protec…

计算机环境安全

操作系统安全----比如windows,linux 安全标识--实体唯一性 windows---主体&#xff1a;账户&#xff0c;计算机&#xff0c;服务 安全标识符SID-Security Identifier 普通用户SID是1000&#xff0c;管理用SID是500 linux---主体&#xff1a;用户&#xff0c;用户组&#xf…

【前端】[vue3] vue-router使用

提示&#xff1a;我这边用的是typeScript语法&#xff0c;所以js文件的后缀都是ts。 安装vue-router&#xff1a; &#xff08;注意&#xff1a;vue2引用vue-router3 vue3才引用vue-router4&#xff09; npm install vue-router4src文件夹下面创建 router/index.ts&#xff08;…

hb_zhyu编程的一路

人生历程&#xff1a; 开始&#xff1a; 走上编程或许并不是我的一开始的意愿&#xff0c;在小学我第一次接触积木编程&#xff0c;或许是它简单&#xff0c;又或许是我天赋异禀&#xff0c;在第一次接触就展现出异于常人的能力&#xff0c;并且产生浓厚的兴趣。 当我几乎可…

synchronized语法与Before/After模式

synchronized void method(){ ... } synchronized(obj){ ... } 不管是synchronized方法&#xff0c; 还是synchronized代码块&#xff0c; 都可以看做在“{”处获取锁&#xff0c; 在"}"处释放锁。 比较下使用synchronized的代码 与显式处理锁的代码。假设存在一个…

Halcon计算最小外接矩形Smallest_rectangle2

Halcon计算最小外接矩形Smallest_rectangle2 该算子用于求最小外接矩形。该算子的原型如下&#xff1a; smallest _rectangle2 (Regions : : : Row, Column, Phi, Lengthl, Length2)其各参数的含义如下。 参数1&#xff1a;Regions 表示输入的区域。 参数2和3&#xff1a;Row…

[HCTF 2018]Warmup

[HCTF 2018]Warmup wp 进入页面&#xff1a; 查看源码&#xff1a; 发现提示&#xff1a;source.php &#xff0c;直接访问&#xff0c;得到源代码&#xff1a; <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist [&qu…

SolidUI Gitee GVP

感谢Gitee&#xff0c;我是一个典型“吃软不吃硬”的人。奖励可以促使我进步&#xff0c;而批评往往不会得到我的重视。 我对开源有自己独特的视角&#xff0c;我只参与那些在我看来高于自身认知水平的项目。 这么多年来&#xff0c;我就像走台阶一样&#xff0c;一步一步参与…