Laravel Eloquent 数据库关联模型的增删改操作

Laravel Eloquent ORM 提供了数据模型关联表操作的 API,熟练掌握这些API后,才体会到 Laravel 数据库操作有多高效。

一、hasMany 一对多关联

save/saveMany 创建关联记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 新建一条数据 Post 的评论,save 将自动添加 post_id 字段
$comment = new Comment(['message' => 'A new comment.']);
$post = Post::find(1);
$post->comments()->save($comment);

// 保持多条记录
$post->comments()->saveMany([
new Comment(['message' => 'A new comment.']),
new Comment(['message' => 'Another new comment.']),
]);

// 更新后需要重新加载模型及其关联,才会加到 $post->comments 中
$post->refresh();

// 所有评论,包括新保存的评论...
$post->comments;

create/createMany 创建关联记录

save/saveMany 的区别是参数时数组,而不是模型。

阅读更多

JavaScript 原型方法:call、apply、bind

callapplybind 是 JavaScript 函数的原型方法,用于改变函数的 this 指向来执行函数。一般这种鬼操作能不用则不用,没得选的时候才有必要用。

  • apply() 调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象的形式提供的参数
  • call() 使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
  • bind() 创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const bar = {
name: 'johnny'
};
function foo (age, hobby) {
console.log(this.name, age, hobby);
}

// apply 和 call 作用相同,传参方式不同
foo.apply(bar, [28, 'sleep']); // johnny 28 sleep
foo.call(bar, 28, 'sleep'); // johnny 28 sleep

// 先绑定对象返回一个函数
const bindBar = foo.bind(bar);
bindBar(28, 'sleep'); // johnny 28 sleep

参考:深入理解JavaScript——call、apply、bind三大将

JavaScript 箭头函数与 function 函数的区别

见过箭头函数后,知道箭头函数是 function 定义函数的简写,然后还有一点是箭头函数中的 this 是上级代码的 this
其实除了这两点外,箭头函数和 function 函数还有很多区别。

1、基本语法

1
2
3
4
5
6
7
8
9
10
11
12
// 没有参数时
() => {
// code ...
}
// 有参数时
(param1, param2, ..., paramN) => {
// code ...
}
// 只有1个参数时,圆括号可以省略
param => {
// code ...
}

2、this 指向

  • 顶级代码中, this 指向 window 对象;
  • function 定义的函数中, this 指向该函数的对象;
  • 箭头函数中,箭头函数不会创建自己的 this,始终指向箭头函数所在作用域下的 this

用原型方法 apply()call()bind() 不能改变箭头函数中 this 的指向。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const bar = {
name: 'johnny'
};
function foo () {
console.log(this.name);
}

const fa = () => {
console.log(this.name);
};

// bar 作为 foo() 实例的 this
foo.call(bar); // johnny
fa.call(bar); // TypeError: Cannot read properties of undefined (reading 'name')

3、箭头函数不能做构造函数

阅读更多

用可变变量访问 JavaScript 类、对象的方法(动态访问 JS 类、对象的方法)

JavaScript中,用可变变量访问对象方法或类静态方法,如果访问的方法中 this,此时 this 将会是 undefined
用对象解构赋值获得函数名(如:xxx)后,用函数名后面加括号(如:xxx())执行函数,this 也是 undefined

问题复现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 动态访问方法
class Cls1 {
fn () {
console.log(this);
}
}

const obj = new Cls1();

// ❌ 错误方法
const fn1 = obj.fn;
fn1(); // this undefined

// 解构赋值取得
const { fn } = obj;

// ❌ 错误方法
fn(); // this undefined

阅读更多

彻底搞清 JavaScript Promise

基本用法

我们知道,一个 js Promise 对象,我们可以用 then()catch()finally() 进行链式操作,如:

1
2
3
4
5
6
7
promiseObj.then((res) => {
// do something
}).catch((error) => {
// do something
}).finally(() => {
// do something
})

这是 js Promise 的基本使用,但是要用好 Promise,还有一下几个问题需要搞清楚。

怎样创建 Promise 对象

Promise 对象创建 Promise 实例

阅读更多

Vue3 组件间相互访问(单文件 + <script setup> + 组合式 API 举例)

父组件传参数到子组件 - props

<script setup> 中用 defineProps() 声明组件的属性,在父组件模板中引用子组件时,就可以给通过属性给子组件传参数(可以是值、变量、函数、对象等)。

defineProps 是编译器宏,Vue3 编译器宏不需要导入,并且只能在 <script setup> 中使用。

ex: 子组件声明、调用属性

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- HelloWorld.vue 子组件-->
<script setup>
// 声明属性
defineProps([{
msg: String
}])
</script>

<template>
<div>
{{ msg }}
</div>
</template>

ex: 父组件使用属性

1
2
3
4
5
6
7
8
<!-- Demo.vue 父组件 -->
<script setup>
import HelloWorld from './HelloWorld.vue'
</script>

<template>
<HelloWorld msg="Welcome"/>
</template>

父组件传递事件到子组件

阅读更多

VueRouter 后退页面刷新问题解决方案

用 VueRouter hash 模式时,点击浏览器上的后退按钮,浏览器显示上一次浏览页面的时候,会重新执行页面,像页面刷新一样。

跟微信小程序不一样,微信小程序后退的时候不会重新执行,只是给我们一个 onShow 事件,这样如果需要刷新数据,我们就可以在 onShow 里面解决。

阅读更多

在 Vue3 单文件组件中使用 <script setup> + 组合式 API

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。

基本语法

<script> 标签上添加 setup 属性启用该语法,里面的代码会背编译成组件 setup() 函数的内容,每次组件实例被创建时执行。

1
2
3
<script setup>
// 组合式 API 写在这里
</script>

如果要使用 TypeScript,加上 lang="ts" 属性即可。

1
2
<script lang="ts" setup>
</script>

当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用。

响应式

响应式即变量数据变化的时候,视图跟着变化。
响应式状态需要明确使用 响应式 API 来创建.
常用 API:

  • ref()
  • reactive()
  • computed()
  • watch()
  • watchEffect()
  • readonly()
阅读更多

Laravel Eloquent 数据库关联查询

一对一 HasOne

tables

1
2
3
4
5
6
- users
- id
- name
- avatars
- user_id
- image

HasOne 关联

1
2
3
4
5
6
7
8
9
// App\Models\User
public function avatar(): HasOne
{
// $this->hasOne('class_name', 'foreign_key', 'local_key');
// foreign_key默认是表名去掉 s 加上 '_id' 后缀
// local_key 默认是 id
return $this->hasOne(Avatar::class);
// 即 $this->hasOne(Avatar::class, 'user_id', 'id');
}

HasOne 反向关联

1
2
3
4
5
6
7
// App\Models\Avatar
public function user(): BelongsTo
{
// $this->belongsTo(User::class, 'foreign_key', 'owner_key');
// return $this->belongsTo(User::class, 'user_id', 'id');
return $this->belongsTo(User::class); // 省略 'foreign_key', 'owner_key' 参数
}

数据操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 新建
$user = User::create(['name', '...']);
Avatar::create([
'user_id' => $user->id,
'image' => '...',
]);

// 更新
$user = User::find(1);
// 更新关联数据
$user->avatar()->update(['image' => '...']);
// 更新模型
$user->update(['name' => '...']);

// 反向关联操作 belongsTo 的 associate/dissociate
$avatar = Avatar::find(1);
$avatar->user()->dissociate(); // 把 user_id 设为 null
$avatar->save(); // 保存更改

$user = User::find(10);
$avatar->user()->associate($user); // 把 user_id 改为 10
$avatar->save();

一对多 HasMany

tables

1
2
3
4
5
6
7
- posts
- id
- title
- comments
- id
- post_id
- comment

模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// App\Models\Post 关联
public function comments(): HasMany
{
// return $this->hasMany('class_name', 'foreign_key', 'local_key');
return $this->hasMany(Comment::class, 'post_id', 'id');
return $this->hasMany(Comment::class); // 使用默认参数
}

// App\Models\Comment 反向关联
public function post(): BelongsTo
{
// $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
return $this->belongsTo(Post::class, 'post_id', 'id');
return $this->belongsTo(Post::class); // 省略 'foreign_key', 'owner_key' 参数
}

//TODO

子查询

1
2
3
4
5
6
7
User::query()->whereIn(
'id',
UserRolePivot::query()
->select('user_id')
->where('role_id', $roleId)
)->get();
// SELECT * FROM user WHERE id IN( SELECT user_id FROM user_role_pivot WHERE role_id = $roleId)

分组查询

阅读更多

PhpStorm 快捷键

  • 文件

    • option + command + y 同步文件/文件夹,文件夹中外部新增或删除文件时刷新有延迟,使用该快捷键同步
    • Ctrl + n 新建文件/文件夹
  • 编辑

    • command + x 剪切行
    • command + c 复制行/复制选中文本
    • command + shift + c 复制文件/文件夹路径
    • command + d 复制行
    • option + ↑ 选择更多代码
    • option + ↓ 选择更少代码
    • option + → 下一个代码(令牌)位置
    • option + ← 上一个代码(令牌)位置
    • command + → 代码行尾
    • command + ← 代码行头
    • tab 增加缩进
    • shift + tab 减少缩进
    • command + shift + enter 完成代码声明
  • 查找相关

    • command + f 查找当前文件
    • command + r 查找替换
    • command + shift + o 快速查询文件
    • command + shift + f 关键字查找,更强大的查询器
    • command + shift + r 高级替换
    • command + option + b 找到当前类(接口的)所有继承(实现类)
    • option + shift + c 查找最近修改的文件
    • option + f7 直接查询选中的字符
    • ctrl + f7 文件中查询选中字符
    • command + 鼠标点击 跳到类或方法或变量等声明处
    • command + shift + tab 切换tab页文件
    • command + shift + +,- 展开或缩起
    • command + . 折叠或展开选中的代码
  • View

    • command + e 打开最近的文件
    • shift + command + e 最近打开文件具体代码位置
  • Bookmarks

    • F3 加入/取消书签
    • option + F3 加入(可设置数字字母记号的)/取消书签
    • command + F3 弹窗显示/隐藏书签
  • Code

    • shift + command + ↑ 声明段上移
    • shift + command + ↓ 声明段下移
    • shift + option + ↑ 行上移
    • shift + option + ↓ 行下移
  • 自动代码

    • option + 回车 选中类名/接口名后按快捷键,导入包,自动修正
    • command + n 快速生成代码,为每个成员属性生成 getter/setter/__construct/方法、文档注释、单元测试等
    • ctrl + i 快速生成插入魔术方法
    • ctrl + o 复写父类方法
    • command + option + l 对当前文件进行格式化排版
    • command + d 复制当前行
    • command + / // 单行注释
    • command + shift + / 多行注释
  • 显示窗口

    • command + 1 Project
    • command + 2 Favorites
    • command + 6 TODO
    • command + 7 Structure
    • command + 9 Version Control 版本控制
    • option + F12 Terminal 终端
  • Git

    • ctrl + v 版本控制相关操作
    • option + command + a Add
    • option + command + z revert (取消Add)
    • shift + command + p pull
    • shift + command + k push
    • command + k commit