If I have a lineItem that needs some validation, for example on an option or note filed, how do I show potential errors in a template and repopulate the form with the posted values?

In addition, I’m working with a template that has multiple products on a page and uses MulitAdd to add items to the cart.

I’m validating lineItem data using onBeforeSaveLineItem and adding the error with something similar to $lineItem->addError('text','This is required');.

  • This is a quick guess before testing - but have you tried $lineItem->addError('options[text]','This is required'); Jun 29 '16 at 13:50
  • You can't attach errors to $lineItem because it doesn't exist - you stop it from being created if the validation fails. The above will work if the line item exists in the cart and will show the error message with {{ item.getError( 'options[text]' ) }} Jun 29 '16 at 14:03
  • Very good point, yes. I’m not adding the item though if there’s an error. $event->performAction = false; gets set instead.
    – James
    Jun 29 '16 at 14:11
  • Trying to find a solution where you attach errors to the cart - would be fine if there was only ever one item on a page, but you state there are multiple? If it were single you could just use $cart->addError('lineItemOptions[text]','This is required'); Seems messy though. Jun 29 '16 at 14:13
  • Thanks. Yep, multiple items in the add to cart form. I may need to fall back to a general message, which could work but no ideas yet on how to repopulate the form if it fails validation. As you say lineItems don’t exist, and creating a custom model seems messy.
    – James
    Jun 29 '16 at 14:18

To repopulate the form fields you can use the data from the post request using something like:

value="{{ craft.request.getPost( 'options.text' ) }}"

I also think I've got a rough solution for handling the errors.

//Add the cart to your event
$cart = craft()->commerce_cart->getCart();

//Check for field/fields
$text = craft()->request->getPost( 'options.text' );

//Check if it's got a value
if ( $text === '' )
  $event->performAction = false;

  //Build the handle we'll attach the error to
  //Use the purchasable id (variant) and then add the field we're checking for
  $lineItemErrorHandle = $item->purchasable->id . '-options[text]';

  //Add the error to the cart
  $cart->addError( $lineItemErrorHandle, 'The text is required' );

Then in your template you display it with the following where product.defaultVariant.id needs to be the id of the purchasable that attempted to be added to the cart.

{% set string = product.defaultVariant.id ~ '-options[text]' %}
{% if cart.getError( string ) %}
  We've got an error its {{ cart.getError( string ) }}
{% endif %}

I had to build the string outside of the .getError method because string interpolation wouldn't work using '#{product.defaultVariant.id}-options[text]'

  • If the form has multiple items there could be multiple options.text fields though, some valid and some not, would this work?
    – James
    Jun 29 '16 at 14:58
  • What does the post request look like for multiple items? The purchasable id makes each error added to the cart unique to the item that through the error. I have only tested this with a single product page though. Jun 29 '16 at 14:59
  • We’re using MultiAdd so the post looks like items[num][options][text] – Perhaps we could loop through items[] and match the purchasable->id?
    – James
    Jun 29 '16 at 15:09
  • Looking at the MultiAdd docs it looks like you can use {{ craft.request.getPost( items[{{ loop.index }}][options][text] ) }} to populate the fields. In the plugin you would then loop through each item in the items post array and do the same as if it were a single item. Jun 29 '16 at 15:10
  • Also, having trouble accessing the post data as 'options.text' actually has a name containing spaces. For example, option[Item text]. How do you target that in a template?
    – James
    Jun 29 '16 at 15:45

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.