1

I want to include some changes in default graphql response but no idea how can I do that and what is the right way to do that as there is not documentation available there. Do I have to create always new graphql APIs(without referencing old one) in order or extend prev. one.

Please let me know if anyone has already done this.

Thanks

3 Answers 3

2

If I am not wrong you meant to ask What if we’re adding a custom attribute, e.g., an attribute we’ve added to the database? And how we gonna deal with making that attribute available to GraphQL. So let's proceed with an example:

Let’s assume you want to add the parent_item_id as an attribute available through the GraphQL Quote Endpoint.Now, you may already be aware, parent_item_id is an existing field (it’s not a new database attribute). But currently, it’s not supported through the native GraphQL Quote Endpoint – so it’s not accessible via GraphQL responses. We’ll walk through a simple example to add it:

First, We’ll create a module:

// File: app/code/GraphQlHandson/Example/registration.php
<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'GraphQlHandson_Example',
    __DIR__
);

Next, We’ll add our etc/schema.graphqls file:

// File: app/code/GraphQlHandson/Example/etc/schema.graphqls
interface CartItemInterface @typeResolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CartItemTypeResolver") {
    parent_item_id: Int
}

Now, refresh your database / cache:

php bin/magento setup:upgrade

…That’s it! On the client side, We can send a GraphQL query that looks like this:

query { cart { items { parent_item_id } } }

(Obviously in a real-world scenario, you would request additional information).

Now, the only point that confusing at first glance is the interface definition in the schema.graphqls file. …Where did we get that? Well, if you look in the native Quote graphql module:

// File>: vendor/magento/module-quote-graph-ql/etc/schema.graphqls
...
type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") {
    customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions")
}

type VirtualCartItem implements CartItemInterface @doc(description: "Virtual Cart Item") {
    customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions")
}

interface CartItemInterface @typeResolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CartItemTypeResolver") {
    id: String!
    quantity: Float!
    product: ProductInterface!
}
...

You can see above, that by default Magento 2 does several things:

  • Defines an interface CartItemInterface which is used to represent cart items.
  • Defines concrete implementations of that interface for different product types.
  • Defines the fields available through GraphQL for each of them.

    So, by default, the only available fields for an item are id, quantity, product — the last of which is a complex ProductInterface type

In our custom module, all we did was copy the interface definition and start adding new properties to it.

That’s an important point to remember – the GraphQL definitions function more like layout xml in Magento 2, and less like typical extension_attributes. The beauty of this is that we can freely add new types, modify queries, etc.

Hope this provide a clear glimpse on how you can modify the GraphQL queries by adding custom or new attributes. If yes, then kindly upvote it and mark it as an answer. Thanks!

2
  • 1
    Please always provide credits to the original post, in this case: cadence-labs.com/2019/09/… Jun 15, 2020 at 13:13
  • The easy way to extend the interface schema. But with my question, I want to extend of Query type so how to do that?
    – huykon225
    Sep 25, 2020 at 1:34
2

Steps to extend the default mutation:

  1. Create a schema.graphqls file in you Module->etc->schema.graphqls.
  2. Copy the schema from the core module and paste it in your schema.graphql file.
  3. Add parameters.
  4. Change resolver class from your custom class e.g Module->Model->Resolver->Test.php.
  5. Get the new parameters added to schema in resolver class using args.

Example: I want to add an extar parameter to quote graphql mutation for EstimateShippingCostsAddress

Default schema and mutation:

type Query {
    getPaymentMethods(guestCartId: String): [PaymentMethod] @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\GetPaymentMethods")
    getCartForCustomer(guestCartId: String): QuoteData @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\GetCartForCustomer")
    getOrderList: OrderList @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\OrderListResolver") @doc(description: "The Sales Order query returns information about a Sales order")
    getOrderById(id: Int!): Order @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\ExpandedOrderResolver") @doc(description: "The Sales Order query returns information about a Sales order")
}

type Mutation {
    estimateShippingCosts(address: EstimateShippingCostsAddress!, guestCartId: String): [ShippingMethod] @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\EstimateShippingCosts")
    saveAddressInformation(addressInformation: SaveAddressInformation!, guestCartId: String): PaymentDetails @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\SaveAddressInformation")
    saveCartItem(cartItem: CartItemInput!, guestCartId: String): Query @resolver(class:"\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\SaveCartItem")
    removeCartItem(guestCartId: String, item_id: Int!): Query @resolver(class:"\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\RemoveCartItem")
    applyCoupon(guestCartId: String, coupon_code: String!): Query @resolver(class:"\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\ApplyCoupon")
    removeCoupon(guestCartId: String): Query @resolver(class:"\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\RemoveCoupon")

    # Magento 2 overrides to stop sending logged in user cart IDs
    s_setPaymentMethodOnCart(input: S_SetPaymentMethodOnCartInput!): SetPaymentMethodOnCartOutput @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart")
    s_placeOrder(guestCartId: String): PlaceOrderOutput @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder")
    s_setBillingAddressOnCart(input: S_SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart")
}

input S_SetBillingAddressOnCartInput {
    guest_cart_id: String
    billing_address: BillingAddressInput!
}

input S_SetPaymentMethodOnCartInput {
    guest_cart_id: String
    payment_method: PaymentMethodInput!
}

input CartItemInput {
    id: CartItemId
    sku: String
    quantity: Float!
    quote_id: String
    item_id: Int
    product_type: String
    product_option: ProductOptionInput
}

input CartItemId {
    sku: String,
    item_id: Int
}

input ProductOptionInput {
    extension_attributes: ExtensionsAttributeInput
}

input ExtensionsAttributeInput {
    configurable_item_options: [ConfigurableItemOptionsInput]
    customizable_options: [CustomizableOptionsInput]
    customizable_options_multi: [CustomizableOptionsInput]
    bundle_options: [BundleOptionInput!]
}

input ConfigurableItemOptionsInput {
    option_id: String
    option_value: Int
}

input CustomizableOptionsInput {
    option_id: String!
    option_value: String
}

input PaymentInformation {
    billing_address: AddressInput!
    paymentMethod: PaymentMethodInput!
}

input PaymentMethodInput {
    method: String
    additional_data: PaymentMethodAdditionalData
}

input PaymentMethodAdditionalData {
    payment_method_nonce: String
    cc_stripejs_token: String
    cc_save: Boolean
}

input AddressInput {
    method: String
    region: String
    region_id: Int
    region_code: String
    country_id: String
    postcode: String
    city: String
    firstname: String
    lastname: String
    email: String
    telephone: String
    company: String
    street: [String]
}





input EstimateShippingCostsAddress {
    region: String
    region_id: Int
    region_code: String
    country_id: String
    postcode: String
    city: String
    firstname: String
    lastname: String
    email: String
    telephone: String
    street: [String]
    customer_id: Int
    same_as_billing: Int
}

input SaveAddressInformation {
    shipping_address: AddressInput!
    billing_address: AddressInput!
    shipping_carrier_code: String
    shipping_method_code: String
}

type QuoteData implements TotalsObject  {
    is_virtual: Boolean
}

type PaymentTotals implements TotalsObject {
}

type PaymentDetails {
    payment_methods: [PaymentMethod]
    totals: PaymentTotals
}

type PaymentMethod {
    code: String
    title: String
}

interface TotalsObject @typeResolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\TotalsObject") {
    items_count: Int
    customer_is_guest: Boolean
    store_id: Int
    customer_tax_class_id: Int
    items_qty: Float
    coupon_code: String
    grand_total: Float
    base_grand_total: Float
    subtotal: Float
    base_subtotal: Float
    discount_amount: Float
    base_discount_amount: Float
    subtotal_with_discount: Float
    base_subtotal_with_discount: Float
    shipping_amount: Float
    base_shipping_amount: Float
    shipping_discount_amount: Float
    base_shipping_discount_amount: Float
    tax_amount: Float
    base_tax_amount: Float
    weee_tax_applied_amount: Float
    shipping_tax_amount: Float
    base_shipping_tax_amount: Float
    subtotal_incl_tax: Float
    shipping_incl_tax: Float
    base_shipping_incl_tax: Float
    base_currency_code: String
    quote_currency_code: String
    items: [TotalsItem]
}

type TotalsItem {
    item_id: Int
    qty: Float
    price: Float
    base_price: Float
    row_total: Float
    base_row_total: Float
    row_total_with_discount: Float
    tax_amount: Float
    base_tax_amount: Float
    tax_percent: Float
    discount_amount: Float
    base_discount_amount: Float
    discount_percent: Float
    price_incl_tax: Float
    base_price_incl_tax: Float
    row_total_incl_tax: Float
    base_row_total_incl_tax: Float
    weee_tax_applied_amount: Float
    weee_tax_applied: Float
    options: String
    name: String
    sku: String
    customizable_options: [SelectedCustomizableOption]
    bundle_options: [SelectedBundleOption]
    product: ProductInterface
}

type TotalsSegment {
    code: String
    title: String
    value: Float
    extension_attributes: ExtensionAttributes
}

type ExtensionAttributes {
    tax_grandtotal_details: [TaxGrandTotalDetails]
}

type TaxGrandTotalDetails {
    # rates:
    amount: Float
    group_id: Int
}

type ShippingMethod {
    carrier_code: String
    method_code: String
    carrier_title: String
    method_title: String
    error_message: String
    amount: Float
    base_amount: Float
    price_excl_tax: Float
    price_incl_tax: Float
    available: Boolean
}

type OrderList {
    items: [Order]
}

type BaseOrderInfo {
    id: Int
    total_qty_ordered: Int
    increment_id: String
    created_at: String
    status: String
    status_label: String
    grand_total: Float
    sub_total: Float
}

type Order {
    base_order_info: BaseOrderInfo
    payment_info: PaymentInfo
    shipping_info: ShippingInfo
    order_products: [ProductInterface] @resolver(class: "\\ScandiPWA\\QuoteGraphQl\\Model\\Resolver\\ProductResolver")
}

type PaymentInfo {
    method: String
    cc_owner: String
    cc_last_4: String
    cc_type: String
    additional_information: AdditionalCustomerInfo
}

type AdditionalCustomerInfo {
    bank: String
    method_title: String
    credit_type: String
    month: Int
    customer_info: CreditCustomerInfo
}

type CreditCustomerInfo {
    first_name: String
    last_name: String
    iin_number: String
    middle_name: String
    phone: String
}

type ShippingInfo {
    shipping_method: String
    shipping_address: OrderCustomerAddress
    shipping_description: String
    shipping_amount: Float
    tracking_numbers: [String]
}

type OrderCustomerAddress {
    city: String
    company: String
    country_id: String
    customer_id: Int
    firstname: String
    id: Int
    lastname: String
    middlename: String
    prefix: String
    region: String
    street: String
    telephone: String
    district: String
    house_number: String
    apartment_number: String
    postomat_code: String
    store_pickup_code: String
    post_office_code: String
    is_b2b: String
    postcode: String
    organizationname: String
    organizationbin: String
    organizationaddress: String
    organizationiic: String
    organizationbik: String
}

interface ProductInterface {
    quantity: Float
    row_total: Float
    original_price: Float
    license_key: String
}

Extended schema:

type Mutation {
    estimateShippingCosts(address: EstimateShippingCostsAddress!, guestCartId: String, CustomData: EstimateData): [ShippingMethod] @resolver(class: "\\NameSpace\\Module\\Model\\Resolver\\EstimateShippingCosts")
}

input EstimateData {
    data: String
}

you can see that i have changed the mutation of EstimateShippingCosts by adding a new parameter CustomData ans changed the resolver class.

now in resolver class you can get the CustomData in args.

EstimateShippingCosts.php

    <?php
    
    
    declare(strict_types=1);
    
    namespace NameSpace\Module\Model\Resolver;
    
    use Magento\Customer\Api\Data\AddressInterfaceFactory;
    use Magento\Framework\GraphQl\Config\Element\Field;
    use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
    use Magento\Framework\GraphQl\Query\Resolver\Value;
    use Magento\Framework\GraphQl\Query\ResolverInterface;
    use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
    use Magento\Quote\Api\Data\EstimateAddressInterface;
    use Magento\Quote\Api\Data\EstimateAddressInterfaceFactory;
    use Magento\Quote\Api\Data\ShippingMethodInterface;
    use Magento\Quote\Model\QuoteIdMaskFactory;
    use Magento\Quote\Model\QuoteIdMask;
    use Magento\Quote\Model\ShippingMethodManagement;
    use Magento\Quote\Model\Webapi\ParamOverriderCartId;
    
  
    class EstimateShippingCosts implements ResolverInterface {
        /**
         * @var QuoteIdMaskFactory
         */
        protected $quoteIdMaskFactory;
    
        /**
         * @var ShippingMethodManagement
         */
        protected $shippingMethodManagement;
    
        /**
         * @var ParamOverriderCartId
         */
        protected $overriderCartId;
    
        /**
         * @var AddressInterfaceFactory
         */
        protected $addressInterfaceFactory;
    
        /**
         * EstimateShippingCosts constructor.
         * @param QuoteIdMaskFactory $quoteIdMaskFactory
         * @param ShippingMethodManagement $shippingMethodManagement
         * @param ParamOverriderCartId $overriderCartId
         * @param EstimateAddressInterfaceFactory $addressInterfaceFactory
         */
        public function __construct(
            QuoteIdMaskFactory $quoteIdMaskFactory,
            ShippingMethodManagement $shippingMethodManagement,
            ParamOverriderCartId $overriderCartId,
            EstimateAddressInterfaceFactory $addressInterfaceFactory
        ) {
            $this->addressInterfaceFactory = $addressInterfaceFactory;
            $this->quoteIdMaskFactory = $quoteIdMaskFactory;
            $this->shippingMethodManagement = $shippingMethodManagement;
            $this->overriderCartId = $overriderCartId;
        }
    
        /**
         * Fetches the data from persistence models and format it according to the GraphQL schema.
         *
         * @param \Magento\Framework\GraphQl\Config\Element\Field $field
         * @param ContextInterface $context
         * @param ResolveInfo $info
         * @param array|null $value
         * @param array|null $args
         * @throws \Exception
         * @return mixed|Value
         */
        public function resolve(
            Field $field,
            $context,
            ResolveInfo $info,
            array $value = null,
            array $args = null
        ) {

**$CustomData= $args['CustomData']['data'];**

            /** @var EstimateAddressInterface $address */
            $shippingAddressObject = $this->addressInterfaceFactory->create([ 'data' => $args['address'] ]);
    
            $cartId = isset($args['guestCartId'])
                ? $this->quoteIdMaskFactory->create()->load($args['guestCartId'], 'masked_id')->getQuoteId()
                : $this->overriderCartId->getOverriddenValue();
    
            $shippingMethods = $this->shippingMethodManagement->estimateByAddress($cartId, $shippingAddressObject);
    
            return array_map(function($shippingMethod) {
                /** @var ShippingMethodInterface $shippingMethod */
                return [
                    'carrier_code' => $shippingMethod->getCarrierCode(),
                    'method_code' => $shippingMethod->getMethodCode(),
                    'carrier_title' => $shippingMethod->getCarrierTitle(),
                    'method_title' => $shippingMethod->getMethodTitle(),
                    'error_message' => $shippingMethod->getErrorMessage(),
                    'amount' => $shippingMethod->getAmount(),
                    'base_amount' => $shippingMethod->getBaseAmount(),
                    'price_excl_tax' => $shippingMethod->getPriceExclTax(),
                    'price_incl_tax' => $shippingMethod->getPriceInclTax(),
                    'available' => $shippingMethod->getAvailable()
                ];
            }, $shippingMethods);
        }
    }

query will be same for this just add a new parameter that you have added into it.

0

Did you get a solution for this issue, I am facing the same problem now, how to add new attribute to the input (CartItemInput) for the default magento graphql mutation schema (addProductsToCart). Can we add a resolver for it to store that custom value.

If anyone knows the answer please help me out.

Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.