booksql-laravel

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/
我修改打开了php.ini配置文件(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();
        });
  1. 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

  1. public下面添加所有图片资源img
  2. 添加lighthouse库 composer require nuwave/lighthouse

⚠️lighthouse就是一个server,不用连xampp,只用连数据库

  1. 将默认模式发布到 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不知道有用没,反正试了;也在.env添加了一句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
}
  1. app->config->lighthouse.php更改namespace下models=>'App'去掉那个后面的'App\Models' 【去掉model】
    playground就可以查到,这样返回🔙的数据就可以用在前端了。
query {
  category(id: 1) {
    id
    name
    books {
      title
      author
      image
    }
  }
}
  1. 如果需要自定义查询,可以在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

  1. vue create booksql-vue ->多选了router 和 vuex 然后history Mode,Elint第一个error就够了,config files,OK。
    cd booksql-vue => npm run serve
  2. 刚刚那个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... '报错❌不管了~~

  1. 在前端vue文件下也安装一个graphQL的接收器 apollo 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.jsapollo.config.js

  1. 该vue-apllo.js里面的http endpoint,选择刚刚laravel开启的后端
    http://localhost:8000/graphql'然后开始在home版面发起query测试数据是否传过来了import gql from 'graphql-tag'
  2. 在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部分

  1. 用的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 : {}
  }

https://stackoverflow.com/questions/59005933/fresh-project-fail-to-compile-after-installing-vuetify-in-vue-js#

  1. 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目录,然后对应放进去


屏幕快照 2020-05-05 下午5.54.41.png

⚠️出现错误


屏幕快照 2020-05-05 下午6.02.26.png

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(),

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350