https://cloud.tencent.com/developer/article/1413095
简单template
laravel cors有个package防止CORS错误❌
config->lighthouse.php(要先在下面第10步骤)
\Fruitcake\Cors\HandleCors::class
准备
安装国内镜像https://cloud.tencent.com/developer/article/1464303,否则install要等到天荒地老。。弄了三个小时都没开始,弄到自己哭!!怀疑我是不是学code的料!@!!!😢
composer config -g repos.packagist composer https://mirrors.aliyun.com/composer/
我修改打开了配置文件(private/etc/php.ini.default)里面的三样https://blog.csdn.net/weixin_40952369/article/details/80673975
开干
1.composer create-project --prefer-dist laravel/laravel booksql-laravel(不要跟到用这种 laravel new booksql-laravel )
2.DB_DATABASE配置
3.php artisan make:model Category -a
4.php artisan make:model Book -a
5.改写关系Category.php & Book.php
protected $guarded = [];
public function books()
{
return $this->hasMany(Book::class);
}
--------------------------------------------------
protected $guarded = []; //为了使用seed
public function category()
{
return $this->belongsTo(Category::class);
}
6.migrations改 up 然后php artisan migrate
$table->increments('id');
$table->string('title');
$table->string('author');
$table->string('image')->nullable();
$table->text('description')->nullable();
$table->string('link')->nullable();
$table->boolean('featured')->default(false);
$table->unsignedInteger('category_id');//因为有hasMany关系设置这个
$table->foreign('category_id')->references('id')->on('categories')->onUpdate('cascade');
$table->timestamps();
--------------------------------------------------------------
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
-
php artisan make:seed CategoriesTableSeeder
php artisan make:seed BooksTableSeeder
更改两个seeder里面的内容并且
seeds->DatabaseSeeder.php 修改:
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(CategoriesTableSeeder::class);
$this->call(BooksTableSeeder::class);
}
然后php artisan migrate:fresh --seed
- public下面添加所有图片资源img
- 添加lighthouse库
composer require nuwave/lighthouse
⚠️lighthouse就是一个server,不用连xampp,只用连数据库
- 将默认模式发布到 graphql/schema.graphql.(publish the default schema)
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema
╭─me@192.168. /Applications/booksql-laravel
╰─➤ php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema
Copied File [/vendor/nuwave/lighthouse/assets/default-schema.graphql] To [/graphql/schema.graphql]
Publishing complete.
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
╭─me@192.168./booksql-laravel
╰─➤ php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
Copied File [/vendor/nuwave/lighthouse/src/lighthouse.php] To [/config/lighthouse.php]
Publishing complete.
安装playground(它就好像 GraphQL 专用的 Postman,但有几个版本,# prisma-labs/graphql-playground
有些是app我下了又删了,还安了个Chrome的graphql插件点开就找不到server。期间卡了一爆,不知怎样打开这个tool。。各种错误❌😢心累死了
php artisan lighthouse:clear-cache
不知道有用没,反正试了;也在添加了一句LIGHTHOUSE_CACHE_ENABLE=false
)
composer require mll-lab/laravel-graphql-playground
安装了上面这个又自己写了句:php artisan vendor:publish --provider="MLL\GraphQLPlayground\GraphQLPlaygroundServiceProvider" --tag =views
这样在config文件夹📁下面就多了个graphql-playground.php (也不知道是不是这样才解决的问题)
这个文件点开有router,所以下面可以设置
11.更改route->graphql->schema.graphal 定义type(根据自己加的Model的属性scheme挨着写全;添加Query里面要查询📖的关联)
type Query {
users: [User!]! @paginate(defaultCount: 10)//默认就有可以注释
user(id: ID @eq): User @find
books: [Book] @all //查所有的book返回一个Book数组
Book(id: ID @eq): Book @find //
booksByFeatured(featured: Boolean! @eq): [Book] @all
someComplexQuery(search: String!): [Book]
}
//添加下面两个type
type Book {
id: ID!
title: String!
author: String!
image: String
link: String
description: String
featured: Boolean
category: category! @belongsTo
}
//这里写成了小写!!!卡了一天不知是不是这个原因
type Category {
id: ID!
name: String!
books: [Book] @hasMany
}
- app->config->lighthouse.php更改namespace下models=>'App'去掉那个后面的'App\Models' 【去掉model】
在playground
就可以查到,这样返回🔙的数据就可以用在前端了。
query {
category(id: 1) {
id
name
books {
title
author
image
}
}
}
- 如果需要自定义查询,可以在type Query里面新增
someComplexQuery(search: String!): [Book]
然后执行php artisan lighthouse:query SomeComplexQuery
可以php artisan查看写法(SomeComplexQuery和schema里面要一致)然后就在app->http->GraphQL里面新创建了个SomeComplexQuery
类, 然后在里面写复杂的逻辑
//模糊搜索wildcard,searchWord是参数
return \App\Book::where('author', 'like', '%'.$args['searchWord'].'%')->get();
去playground(http://localhost:8000/graphql-playground
⚠️后缀只写graphql返回一个error,虽然lighthouse相当于开启了一个server但是还要用xampp把数据库开启)
搜索🔍一下:就会返回两本这个Gary的书
query {
someComplexQuery(searchWord: "Gary") {
author
title
}
}
然后把Book和Category所有的【增删改】写了,瞬间感觉后端变得巨简单!
type Mutation { //⚠️是Mutation
# createUser(
# name: String @rules(apply: ["required"])
# email: String @rules(apply: ["required", "email", "unique:users,email"])
# ): User @create(model: "App\\User")
# updateUser(
# id: ID @rules(apply: ["required"])
# name: String
# email: String @rules(apply: ["email"])
# ): User @update(model: "App\\User")
# deleteUser(
# id: ID @rules(apply: ["required"])
# ): User @delete(model: "App\\User")
createCategory(
name: String @rules(apply: ["required", "unique:categories,name"])
): Category @create
updateCategory(
id: ID @rules(apply: ["required"])
name: String @rules(apply: ["required", "unique:categories,name"])
): Category @update
deleteCategory(
id: ID! @rules(apply: ["required"])
): Category @delete
createBook(
title: String! @rules(apply: ["required"])
author: String! @rules(apply: ["required"])
image: String
link: String
description: String
featured: Boolean
category_id: Int!
): Book @create
updateBook(
id: ID @rules(apply: ["required"])
title: String! @rules(apply: ["required"])
author: String! @rules(apply: ["required"])
image: String
link: String
description: String
featured: Boolean
category_id: Int! //⚠️💀不知为何不能写category!!又卡2小时
): Book @update
deleteBook(
id: ID! @rules(apply: ["required"])
): Book @delete
}
前端https://apollo.vuejs.org/guide/apollo/queries.html#simple-query
-
vue create booksql-vue
->多选了router 和 vuex 然后history Mode,Elint第一个error就够了,config files,OK。
cd booksql-vue => npm run serve - 在刚刚那个laravel开启server的app里面添加cors依赖,因为vue在另一个port,所以防止CORS,安装插件
composer require barryvdh/laravel-cors
(直接goole laravel cors就看到了这个GitHub,现在是composer require fruitcake/laravel-cors)
⚠️还是要安装到booksql-laravel根目录不是vue里面!
⚠️这个库改名字了!\Fruitcake\Cors\HandleCors::class
在config->lighthouse添加middle
protected $middleware = [
// ...
\Fruitcake\Cors\HandleCors::class,
];
各种坑啊!!安了也说报错拿不到数据,还说我不能cors😢!!又耗时三个小时至少!!添加了一个新的middlewarephp artisan make:middleware Cors
然后在$next下添加了三句话
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Content-Type, Authorizations');
}
然后在kernel.php里面注册
protected $middleware = [
\App\Http\Middleware\Cors::class,
妈妈咪呀,,总算拿到数据了。还有各种WebSocket connection to 'ws... '
报错❌不管了~~
- 在前端vue文件下也安装一个graphQL的接收器
vue add apollo
然后选三个 no 往下走 vue-cli-plugin-apollo@0.21.3
╭─me@192.168.0.100 /Applications/XAMPP/xamppfiles/htdocs/Icreate/Vue/book_shelf_vue ‹master*›
╰─➤ vue add apollo
📦 Installing vue-cli-plugin-apollo...
> @apollo/protobufjs@1.0.3 postinstall /Applications/XAMPP/xamppfiles/htdocs/Icreate/Vue/book_shelf_vue/node_modules/@apollo/protobufjs
> node scripts/postinstall
> nodemon@1.19.4 postinstall /Applications/XAMPP/xamppfiles/htdocs/Icreate/Vue/book_shelf_vue/node_modules/nodemon
> node bin/postinstall || exit 0
Love nodemon? You can now support the project via the open collective:
> https://opencollective.com/nodemon/donate
+ vue-cli-plugin-apollo@0.21.3
added 354 packages from 291 contributors and audited 44468 packages in 37.989s
50 packages are looking for funding
run `npm fund` for details
found 1 low severity vulnerability
run `npm audit fix` to fix them, or `npm audit` for details
✔ Successfully installed plugin: vue-cli-plugin-apollo
? Add example code No
? Add a GraphQL API Server? No
? Configure Apollo Engine? No
🚀 Invoking generator for vue-cli-plugin-apollo...
📦 Installing additional dependencies...
added 2 packages from 3 contributors and audited 44479 packages in 21.469s
50 packages are looking for funding
run `npm fund` for details
found 1 low severity vulnerability
run `npm audit fix` to fix them, or `npm audit` for details
⠋ Running completion hooks...error: Require statement not part of import statement (@typescript-eslint/no-var-requires) at apollo.config.js:1:14:
> 1 | const path = require('path')
| ^
2 |
3 | // Load .env files
4 | const { loadEnv } = require('vue-cli-plugin-apollo/utils/load-env')
error: Require statement not part of import statement (@typescript-eslint/no-var-requires) at apollo.config.js:4:21:
2 |
3 | // Load .env files
> 4 | const { loadEnv } = require('vue-cli-plugin-apollo/utils/load-env')
| ^
5 | const env = loadEnv([
6 | path.resolve(__dirname, '.env'),
7 | path.resolve(__dirname, '.env.local')
2 errors found.
报了两个错,貌似是哦eslint和typescript的语法错误,没管。
https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-var-requires.md
中间的1 low severity vulnerability 直接按提醒走
╭─me@npm audit fix
up to date in 8.756s
50 packages are looking for funding
run `npm fund` for details
fixed 0 of 1 vulnerability in 44479 scanned packages
1 vulnerability required manual review and could not be updated
然后文件目录多出 vue-apollo.js
和apollo.config.js
- 该vue-apllo.js里面的
http endpoint
,选择刚刚laravel开启的后端
http://localhost:8000/graphql'
然后开始在home版面发起query测试数据是否传过来了import gql from 'graphql-tag'
- 在src下面新建文件夹📁graphql里面再建两个文件夹mutations & queries。
在queries里面先新建🆕Categories.gql
⚠️如果遇到 error:Parsing error:Unexpected token,expected ";" 添加一个.eslintignore 文件写上: **/*.gql 就可以去掉warning
⚠️如果遇到Failed to compile with 1 errors 然后写了Module parse failed:Uxpected token(1:6)直接删除node_modules包,然后重新npm install
图片的链接🔗最好把path单独写在数据库里
query(ID: id) {
book($id: ID) //进行传参id的type就是ID
}
mutation(
$title: String!
$image: String!
$featured: Boolean
$category: Int!) {
createBook(
title: $title
image: $image
featured: $featured
category: $category
) {
}
}
自己摸索vuetify部分
- 用的
vue ui
找插件vuetify
安装,也可以直接vue add vuetify
在终端添加
然后npm run serve就报错,出师未捷身先死☠️什么都没做,就报错找不到 vuetify/lib
改了vuetify-loader下面的constructor
class VuetifyLoaderPlugin {
constructor (options) {
// this.options = options || {}
this.options = (options) ? options : {}
}
- OK,开始渲染各种数据
可以一直用tag这种写法,也可以用component,我后面还是选后者,这样可以不写script直接包含了slot的概念然后prop出来
import gql from 'graphql-tag'
export default {
name: "category",
data() {
return {
categories: []
}
},
apollo: {
categories: gql`{
categories {
id
name
}
}`,
},
component模版
<template>
<!-- Apollo Query -->
<ApolloQuery :query="/* some query */">
<!-- The result will automatically updated -->
<template slot-scope="{ result: { data, loading } }">
<!-- Some content -->
<div v-if="loading">Loading...</div>
<ul v-else>
<li v-for="user of data.users" class="user">
{{ user.name }}
</li>
</ul>
</template>
</ApolloQuery>
</template>
为了方便管理把所有的query和mutation新建一个graphql目录,然后对应放进去
⚠️出现错误
ll //查看
rm -rf node_modules //删除node_modules
npm install //再安装一遍
然后在npm run serve或者在vue UI工具里面run一遍,ok只是换了一个端口,但是数据可render出来🎉
虽然不影响但是UI工具里面报错
6:32 Could not find a declaration file for module './vue-apollo'. '/Applications/XAMPP/htdocs/Icreate/Vue/book_shelf_vue/src/vue-apollo.js' implicitly has an 'any' type.
4 | import store from './store'
5 | import vuetify from './plugins/vuetify';
> 6 | import { createProvider } from './vue-apollo'
| ^
7 |
8 | Vue.config.productionTip = false
9 |
ERROR in /Applications/XAMPP/htdocs/Icreate/Vue/book_shelf_vue/src/main.ts(13,3):
13:3 No overload matches this call.
Overload 1 of 3, '(options?: ThisTypedComponentOptionsWithArrayProps<Vue, object, object, object, never> | undefined): CombinedVueInstance<Vue, object, object, object, Record<...>>', gave the following error.
Argument of type '{ router: VueRouter; store: Store<{}>; vuetify: any; apolloProvider: any; render: (h: CreateElement) => VNode; }' is not assignable to parameter of type 'ThisTypedComponentOptionsWithArrayProps<Vue, object, object, object, never>'.
Object literal may only specify known properties, and 'vuetify' does not exist in type 'ThisTypedComponentOptionsWithArrayProps<Vue, object, object, object, never>'.
Overload 2 of 3, '(options?: ThisTypedComponentOptionsWithRecordProps<Vue, object, object, object, object> | undefined): CombinedVueInstance<Vue, object, object, object, Record<...>>', gave the following error.
Argument of type '{ router: VueRouter; store: Store<{}>; vuetify: any; apolloProvider: any; render: (h: CreateElement) => VNode; }' is not assignable to parameter of type 'ThisTypedComponentOptionsWithRecordProps<Vue, object, object, object, object>'.
Object literal may only specify known properties, and 'vuetify' does not exist in type 'ThisTypedComponentOptionsWithRecordProps<Vue, object, object, object, object>'.
Overload 3 of 3, '(options?: ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>> | undefined): CombinedVueInstance<...>', gave the following error.
Argument of type '{ router: VueRouter; store: Store<{}>; vuetify: any; apolloProvider: any; render: (h: CreateElement) => VNode; }' is not assignable to parameter of type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>'.
Object literal may only specify known properties, and 'vuetify' does not exist in type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>'.
11 | router,
12 | store,
> 13 | vuetify,
| ^
14 | apolloProvider: createProvider(),
15 | render: h => h(App)
16 | }).$mount('#app')
ERROR in /Applications/XAMPP/htdocs/Icreate/Vue/book_shelf_vue/src/plugins/vuetify.ts(2,21):
2:21 Could not find a declaration file for module 'vuetify/lib'. '/Applications/XAMPP/htdocs/Icreate/Vue/book_shelf_vue/node_modules/vuetify/lib/index.js' implicitly has an 'any' type.
Try `npm install @types/vuetify` if it exists or add a new declaration (.d.ts) file containing `declare module 'vuetify/lib';`
1 | import Vue from 'vue';
> 2 | import Vuetify from 'vuetify/lib';
| ^
3 |
4 | Vue.use(Vuetify);
找不到apollo就在tsconfig.json里面写了下面这两个地方
https://github.com/vuejs/vue-apollo/issues/278
Could not find a declaration file for module './vue-apollo'.
"typeRoots": [
"./node_modules/@types",
"./node_modules/vuetify/types",
"./node_modules/vue-apollo/types"
],
"types": [
"webpack-env",
"vuetify",
"vue-cli-plugin-apollo/types"
],
添加新的页面Book.vue
在router下面的index.ts下面注册
{
path: '/book/:id',
name: 'Book',
component: () => import(/* webpackChunkName: "about" */ '../views/Book.vue')
}
在App.vue下面是根目录入口点 http://localhost:8080/book/1
<v-content>
<!-- <category/> --> 这样就是加载component里面的组件
<router-view></router-view> 这样就是用每个view下面单独的vue文件
</v-content>
import category from './views/Category.vue'; 如果用view下面单独文件这个可以不用写
加fontawesome图标引用,放在public下面index.html里面cnd
<link href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" rel="stylesheet">
麻烦还注册然后给了一串自己的编号
<script src="https://kit.fontawesome.com/4e52f97a22.js" crossorigin="anonymous"></script>
可以用vue-apollo.js里面控制记录📝token, 存在localstorage里面,退出登录销毁
// Manually call this when user log in
export async function onLogin (apolloClient, token) {
if (typeof localStorage !== 'undefined' && token) {
localStorage.setItem(AUTH_TOKEN, token)
}
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (login)', 'color: orange;', e.message)
}
}
// Manually call this when user log out
export async function onLogout (apolloClient) {
if (typeof localStorage !== 'undefined') {
localStorage.removeItem(AUTH_TOKEN)
}
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
}
报错❌
Cannot read property 'freezeResults' of null
https://github.com/vuejs/vue-apollo/issues/631
只需要在vue-apollo.js里面把cache
import { InMemoryCache } from 'apollo-cache-inmemory';
const defaultOptions = {
// Override default cache
cache: new InMemoryCache(),
}