[SalesForce] Fetching data from Apex controller for lightning web component

I am creating a shopping cart component with LWC and am struggling to get this to return data relating to the cart. I have a custom object to store the cart header details and am looking to have the controller initialise a new cart if no open carts exist for that user.

lwc-component.js

import {
  LightningElement,
  wire,
  track
} from 'lwc';

/** get the current user's ID */
import Id from '@salesforce/user/Id';

/** Import the necessary Objects, fields and controller */
import getCartHeader from '@salesforce/apex/BoardCartController.getCartHeader';

export default class BoardShoppingCart extends LightningElement {
  @track userId = Id;
  times = 0;

  @wire(getCartHeader, {
    userId: '$userId'
  })
  cartHeader;

  /** stuff used for debugging and console logging */
  logOutStuff(dataToLog, logmessage) {
    console.log(logmessage + JSON.stringify(dataToLog));
  }

  renderedCallback() {
    this.times++;
    console.log('render callback called this... ' + this.times);
    this.logOutStuff(this.userId, 'The user ID is: ');
    this.logOutStuff(this.cartHeader, 'The cart header is: ');
  }
  /************** END DEBUGGING *****************************/
}

BoardCartController.apex

public with sharing class BoardCartController {
    @AuraEnabled(cacheable=true)
    public static List<Boardstore_Cart__c> getCartHeader(String userId){
        // Debugging 
        System.debug('the userID is: ' + userId);
        List<Boardstore_Cart__c> currentCarts = new List<Boardstore_Cart__c>(
                                            [SELECT Id, Name, Date_Created__c, Cart_Status__c 
                                            FROM Boardstore_Cart__c 
                                            WHERE ownerId =: userId 
                                            AND  Cart_Status__c = 'Open'
                                            LIMIT 1]);
        System.debug('The current carts result: ' + currentCarts);
        if(currentCarts.isEmpty()) {
            // create a new cart and return that
            System.debug('in the createCart function');
            Boardstore_Cart__c singleToCreate = new Boardstore_Cart__c();
            singleToCreate.Date_Created__c = System.Datetime.now();
            singleToCreate.Cart_Status__c = 'Open';
            singleToCreate.ownerId = userId;
            System.debug('the single to create is: ' + singleToCreate);

            // Insert into database
            try {
                Database.SaveResult results = Database.insert(singleToCreate);
                System.debug('logging out the results of the DB save: ' + results);
            } catch(DmlException e) {
                System.debug('in the catch block: ' + e.getMessage());
            }
            // TODO: add default shipping fields.
            // reset current carts
            currentCarts = [SELECT Id, Name, Date_Created__c, Cart_Status__c 
                            FROM Boardstore_Cart__c 
                            WHERE ownerId =: userId 
                            AND  Cart_Status__c = 'Open'
                            LIMIT 1];
        } 
        return currentCarts;
    }
}

Expected behaviour is that the component will mount, calling the Apex controller method getCartHeader and passing in a string that represents the userID. The controller will take that string, search for an open cart record and if found, return that. If not, it will create a new cart record and return that.

Actual behaviour the controller is firing an exception for too many DML statements (1) and not creating the cart. If I manually create the cart, the code completes but the js side doesn't log the cartHeader details..

How can I get this controller to create the new cart record as required? I have tried removing the (cacheable=true) after reading this in the docs:

To improve runtime performance, annotate the Apex method with @AuraEnabled(cacheable=true), which caches the method results on the client. To set cacheable=true, a method must only get data, it can’t mutate (change) data.

However, when this is removed – the apex controller doesn't fire at all…

Best Answer

You cannot do DML in Apex Aura Methods declared as (cacheable=true).

If in LWC , if Aura Method does not have (cacheable=true) you cannot use @wire to call apex.

From Docs:

To use @wire to call an Apex method, you must set cacheable=true.

You have to call apex imperatively.

export default class BoardShoppingCart extends LightningElement {
  @track userId = Id;
  times = 0;
  @api cardHeader;

    handleLoad() {
        getCartHeader({userId: this.userId})
            .then(result => {
                this.cardHeader = result;
            })
            .catch(error => {
                this.error = error;
            });
    }
}
Related Topic