保存后如何返回实体及其关系?

我正在使用 Typegraphql 和 TypeORM 构建一个以 GraphQL 作为后端的存储应用程序。

需要单独添加类别,然后在添加产品时,从下拉列表中选择可用类别之一。这又以一对多/多对一的关系将 categoryId 传递给产品。

这是我的类别实体:

import {
  Entity,
  PrimaryColumn,
  Column,
  BaseEntity,
  Generated,
  OneToMany
} from 'typeorm';
import Product from './Product';

@ObjectType()
@Entity('categories')
export default class Category extends BaseEntity {
  @Field()
  @PrimaryColumn()
  @Generated('uuid')
  categoryId: string;

  @Field()
  @Column()
  categoryName: string;

  @OneToMany(() => Product, (product: Product) => product.category)
  products: Product[];
}

这是我的产品实体

import {
  Entity,
  PrimaryColumn,
  Column,
  BaseEntity,
  Generated,
  ManyToOne,
  JoinColumn
} from 'typeorm';
import Category from './Category';

@ObjectType()
@Entity('products')
export default class Product extends BaseEntity {
  @Field()
  @PrimaryColumn()
  @Generated('uuid')
  productID: string;

  @Field()
  @Column()
  productName: string;

  @Field(() => Category)
  @ManyToOne(() => Category, (category: Category) => category.products, {
    cascade: true,
    lazy: true
  })
  @JoinColumn()
  category: Category;

  @Field()
  @Column()
  productQuantity: number;

  @Field()
  @Column({ type: 'decimal', precision: 2 })
  productPrice: number;

  @Field()
  @Column({ type: 'decimal', precision: 2 })
  productPriceRA: number;

  @Field()
  @Column({ type: 'decimal', precision: 2 })
  productPriceKK: number;

  @Field()
  @Column('varchar', { length: 255 })
  productSupplier: string;

  @Field()
  @Column('varchar', { length: 255 })
  productOrderLink: string;

  @Field()
  @Column('longtext')
  productImage: string;
}

对于保存突变,我还创建了一个输入类型:

export default class ProductInput implements Partial<Product> {
    @Field()
    productName: string;

    @Field(() => String)
    category: Category;

    @Field()
    productQuantity: number;

    @Field()
    productPrice: number;

    @Field()
    productPriceRA: number;

    @Field()
    productPriceKK: number;

    @Field()
    productSupplier: string;

    @Field()
    productOrderLink: string;

    @Field()
    productImage: string;
}

关系有效,因为我可以使用以下查询查询产品及其类别数据:

{
  getProducts {
    productID
    productName
    category {
       categoryId
       categoryName
    }
  }
}

但是,当保存产品时,它总是返回 "message": "Cannot return null for non-nullable field Category.categoryName."

这是解析器中的突变代码:

@Mutation(() => Product, { description: 'Add new product' })
  async addProduct(
    @Arg('product') productInput: ProductInput
  ): Promise<Product | any> {
    try {
      const product = await Product.create(productInput).save();

      console.log('product: ', product);
      return product;
    } catch (error) {
      return error;
    }
  }

我一直在尝试不同的事情,但是似乎没有任何效果,我想知道是否甚至可以直接返回实体及其关系。如果不是,我能想到的另一个选择是根据结果返回真/假并重新查询所有数据。但这似乎非常低效,我正在积极尝试避免走这条路。

任何帮助都感激不尽。

stack overflow How to return the entity with its relations after saving it?
原文答案
author avatar

接受的答案

经过更多研究,我决定采用以下方法:

try {
      const { productID } = await Product.create(productInput).save();

      return await Product.findOne(productID);
    } catch (error) {
      return error;
    }

这使我可以在产品保存在数据库中后根据产品 ID 直接返回产品,并正确返回具有关系的对象。


答案:

作者头像

GraphQL 使用符号来识别数据。您可以将其视为 __typename 对象属性。当然,这必须在 GraphQL 服务器配置中开启。如果你看到它,它已经很清楚了。您可以通过这样的技巧获得正确的结果,而无需在客户端重新获取缓存数据中的关系更改。例如,假设我们已经用类别更新了产品。在要从更新突变返回的数据中,仅返回关系的 id 就足够了。

**For this to work, category and product must be cached separately on the client beforehand.**

例如:

mutation UpdateProduct($product: UpdateProductInput!) {
    updateProduct(product: $product) {
        id
        title
        category {
            id
        }
    }
}

你也可以写在writeFragment中,这是一个单独的方法,这是最小气的,但是它会让你的工作在嵌套数据中变得困难。

export class ProductFragmentService {
    constructor(private apollo: Apollo) {}

    updateProduct(product: Product): void {
        const client = this.apollo.client;
        client.writeFragment({
            id: `Product:${product.id}`,
            fragment: gql`
                fragment UpdateProductCategoryFragment on Product {
                    __typename
                    id
                    title
                    category {
                        id
                    }
                }
            `,
            data: {
                __typename: 'Product',
                ...product,
            },
        });
    }
}

如果您想要属于类别的所有字段,您需要将它们发送到解析器并从那里作为响应返回。否则,是的,它会警告我找不到名称属性。

这样做更有利可图的方法是将此数据与输入一起发送到解析器,正如我在上面所写的,然后作为来自服务器的响应返回给客户端。

如果还需要再次发起 SQL 请求,则需要在注册后调用同一个 id。

@Authorized()
@Mutation(() => Product, { description: 'Add new product' })
  async addProduct(
    @Arg('product') productInput: ProductInput
  ): Promise<Product> {
      await this.productRepo.save(productInput);
      return await this.productRepo.findOne({ where: { id: productInfo.id } });
  }

就这样 :)