Save Prestashop Custom fields on “Add to cart” – Part 1

Having to save product customization before adding to the cart can be quite annoying. Some customers might even forget to do it if fields are not required. In the first part of this two-stage tutorial, we will see how to save customization text when the add to cart button is pressed.

  • Version used: Prestashop 1.6

Save Prestashop Custom fields on “Add to cart” – Part 2 »

Demo here  

We do need ajax

Before starting, it’s important to notice we do need to activate the ajax cart, as we will be catching the fields’ content and serialize it; then intercept the “add” method of the ajax cart and send another ajax request to the product controller, to save customized data before anything is added to the cart itself.
That said, we need to modify two javascript files, and one controller:

  • ajax-cart.js
  • tools.js
  • ProductController.php

While we can use an override for the controller, and clone the template for the ajax-cart file, we really need to amend the original tools.js, located in the main js/ folder.

That said, in this first part we will only deal with text inputs, leaving the file upload to the next tutorial.

Ajax-cart.php

After reaching the product page (of a product having a text customization field, of course), open up themes/*yourthemename*/modules/blockcart/ajax-cart.js

Locate the following method

	add : function(idProduct, idCombination, addedFromProductPage, callerElement, quantity, whishlist)

It should be around line 271 in Prestashop 1.6.0.14

Inside it, at the very beginning, right before:

if (addedFromProductPage && !checkCustomizations())

Add the following:

		if(addedFromProductPage && $('#customizationForm').length > 0)
		{
	
			$('#quantityBackup').val($('#quantity_wanted').val());
			customAction = $('#customizationForm').attr('action');
			$('body select[id^="group_"]').each(function() {
				customAction = customAction.replace(new RegExp(this.id + '=\\d+'), this.id +'=' + this.value);
			});
			
		}

Explanation: first, we have to make sure we are adding to the cart from the single product view (it would make no sense in the list) and that the current item does have customization fields. What comes inside is almost a 1:1 copy of the saveCustomization() method we can find in the product.js file. We need to modify the action so that it considers attributes as well. customAction is the url we will send the ajax POST to.

Therefore, continuing from it:

		if(addedFromProductPage && $('#customizationForm').length > 0)
		{
	
			$('#quantityBackup').val($('#quantity_wanted').val());
			customAction = $('#customizationForm').attr('action');
			$('body select[id^="group_"]').each(function() {
				customAction = customAction.replace(new RegExp(this.id + '=\\d+'), this.id +'=' + this.value);
			});
			// ajax to product page with custom action
			var customization_entries = $('#customizationForm').serialize();
			$.ajax({
				async:false,
				type: 'POST',
				data: customization_entries+ '&ajax=1',
				dataType: 'json',
				url: customAction,
				success: function(data){
					
				}
			})	
		}

Explanation: after serializing the array’s entries (remember we are only using text fields for the time being), we send them over to the actionUrl (that is, the ProductController) so it can further take care of our data. Notice I also appended &ajax=1 as the controller will need to know it’s receiving an ajax request. Time to move to the override!

IMPORTANT! Make sure you set the ajax request as async:false, otherwise the add to cart will run before our customization is saved!

Note: do not try adding the product to the cart at this stage, it will not work, saying the customization field is required.

The Product Controller

Having our javascript all setup, let’s deal with the product controller. To make it quick, I will append the new method to the original file, but you can feel free to use an override instead. What it needs is a method that handles data before anything is outputted to screen. Fortunately, we don’t even need to create it, it’s built in, and it is named postProcess. The only issue is that the ProductController.php file doesn’t use, so we need to append it ourselves.

Open up controllers/front/ProductController.php, reach the end of the file, and add the following (make sure you are inside the class!)

	public function postProcess()
	{
		if (Tools::getValue('ajax') && Tools::isSubmit('submitCustomizedDatas'))
		{
			if (!$this->context->cart->id && isset($_COOKIE[$this->context->cookie->getName()]))
			{
				$this->context->cart->add();
				$this->context->cookie->id_cart = (int)$this->context->cart->id;
			}

			$this->pictureUpload();
			$this->textRecord();
			$this->formTargetFormat();

			if($this->errors)
			{
				$error_list = implode('; ', $this->errors);
				die(Tools::jsonEncode(array('errors' => $error_list)));
			} else 
				die(Tools::jsonEncode(array('success' => true)));
				
		}
	}

Explanation: We only need to run this method for our ajax call, and only when customization data is sent over. After checking it, we simply run the methods Prestashop uses when submitting a regular customization form:


			$this->pictureUpload();
			$this->textRecord();
			$this->formTargetFormat();


Please notice pictureUpload is currently not being used, as we are not uploading anything. Also, we need to create a new cart in case it does not exist, before adding the text to it, so it has an id to deal with.

Then, we check for errors, sending them back to the ajax-cart.js file if there are, otherwise returning success. Since we want to make sure data has been saved, let’s go back to our JS for a second, and do something on the “success” method of the ajax call:

		if(addedFromProductPage && $('#customizationForm').length > 0)
		{
	
			$('#quantityBackup').val($('#quantity_wanted').val());
			customAction = $('#customizationForm').attr('action');
			$('body select[id^="group_"]').each(function() {
				customAction = customAction.replace(new RegExp(this.id + '=\\d+'), this.id +'=' + this.value);
			});
			// ajax to product page with custom action
			var customization_entries = $('#customizationForm').serialize();
			$.ajax({
				type: 'POST',
				data: customization_entries+ '&ajax=1',
				dataType: 'json',
				url: customAction,
				success: function(data){
					if(typeof(data.errors) !== 'undefined')
					{
						alert('Error while saving customization data');
						return;
					}
						
				}
			})	
		}

We basically only added this part


if(typeof(data.errors) !== 'undefined')
{
	alert('Error while saving customization data');
	return;
}

To the success handler, that triggers when the ajax call is successful. Of course, you might want to expand this, and add a further level of checks to make sure data has really been saved. However, if the field is required, your product will not be added to the cart, in any case.

At this point, try writing something in the field, then click add to cart. What? Not working? Why? Prestashop can be nasty to deal with; we have to perform one last modification.

Tools.js

The last file we need to deal with might be a pain in case you upgrade. Therefore, make sure you keep a backup of the modification and/or write these steps somewhere, in case you need to apply them again. Open up js/tools.js. What we are interested in is the checkCustomizations method:

function checkCustomizations()
{
	var pattern = new RegExp(' ?filled ?');

	if (typeof customizationFields != 'undefined')
		for (var i = 0; i < customizationFields.length; i++)
		{
			/* If the field is required and empty then we abort */
			if (parseInt(customizationFields[i][1]) == 1 && ($('#' + customizationFields[i][0]).html() == '' ||  $('#' + customizationFields[i][0]).text() != $('#' + customizationFields[i][0]).val()) && !pattern.test($('#' + customizationFields[i][0]).attr('class')))
				return false;
		}
	return true;
}

Pay attention to the following line

function checkCustomizations()
{
	if (parseInt(customizationFields[i][1]) == 1 && ($('#' + customizationFields[i][0]).html() == '' ||  $('#' + customizationFields[i][0]).text() != $('#' + customizationFields[i][0]).val()) && !pattern.test($('#' + customizationFields[i][0]).attr('class')))
				return false;


This is what is currently preventing us from saving our customization. Actually, it gets saved, but the product is not added to the cart.
let’s get rid of a good portion of it


	if (parseInt(customizationFields[i][1]) == 1 && ($('#' + customizationFields[i][0]).val() == ''))
		return false;


The only thing we are doing to check it, is making sure it has a value. THat is all we need for now!
Refresh and test, it should be working now!

Next up…

Saving text customization fields when hitting the add to cart button was not too complicated. Things will get really nasty in the next tutorial, when we will deal with asynchronous file uploads! Stay tuned!

Save Prestashop Custom fields on “Add to cart” – Part 2 »

You like the tuts and want to say "thank you"? Well, you can always feel free to donate:

  • Leo

    It seems that this tutorial is outdated for prestashop 1.7 :/

  • Makina Nero

    You should add async:false in your code ! I use this trick for the second time and it didn’t work ! I compared the code on the first site I made using this trick and the only difference was the use of async:false. Even if you tell to use it, it should be in the example…

  • Marek Z

    It is work but interpretation of instruction can make problem. Code in ajax-cart.js must stay. Custom code from trutorial must be add after all if (addedFromProductPage && !checkCustomizations()) {};
    CODE:
    add : function(idProduct, idCombination, addedFromProductPage, callerElement, quantity, whishlist) {
    if (addedFromProductPage && !checkCustomizations()) {
    if (contentOnly) {
    var productUrl = window.document.location.href + ”;
    var data = productUrl.replace(‘content_only=1′, ”);
    window.parent.document.location.href = data;
    return;
    }

    if (!!$.prototype.fancybox) {
    $.fancybox.open([
    {
    type: ‘inline’,
    autoScale: true,
    minHeight: 30,
    content: ” + fieldRequired + ”
    }
    ], {
    padding: 0
    });
    } else {
    alert(fieldRequired);
    }
    return;
    }

    if(addedFromProductPage && $(‘#customizationForm’).length > 0)
    {

    $(‘#quantityBackup’).val($(‘#quantity_wanted’).val());
    customAction = $(‘#customizationForm’).attr(‘action’);
    $(‘body select[id^=”group_”]’).each(function() {
    customAction = customAction.replace(new RegExp(this.id + ‘=\d+’), this.id +’=’ + this.value);
    });
    // ajax to product page with custom action
    var customization_entries = $(‘#customizationForm’).serialize();
    $.ajax({
    type: ‘POST’,
    async:false,
    data: customization_entries+ ‘&ajax=1′,
    dataType: ‘json’,
    url: customAction,
    success: function(data){

    if(typeof(data.errors) !== ‘undefined’)
    {
    alert(‘Error while saving customization data’);
    return;
    }

    }
    })
    }

    ……. cdn

    }

  • Priyanshu Khandelwal

    Its not working for me, Please somebody help.

  • David

    I have the version 1.6.1.6, followed the tutorial but it does not work at all. In my theme i dont have ajaxcart.js, so i edited it directly in the modulesof the prestashop.
    So i did all the steps first in ajax-cart.js:
    add : function(idProduct, idCombination, addedFromProductPage, callerElement, quantity, wishlist){
    if (addedFromProductPage && !checkCustomizations())
    {
    alert(fieldRequired);
    return ;
    }
    emptyCustomizations();
    if(addedFromProductPage && $(‘#customizationForm’).length > 0)
    {

    $(‘#quantityBackup’).val($(‘#quantity_wanted’).val());
    customAction = $(‘#customizationForm’).attr(‘action’);
    $(‘body select[id^=”group_”]’).each(function() {
    customAction = customAction.replace(new RegExp(this.id + ‘=\d+’), this.id +’=’ + this.value);
    });
    // ajax to product page with custom action
    var customization_entries = $(‘#customizationForm’).serialize();
    $.ajax({
    type: ‘POST’,
    data: customization_entries+ ‘&ajax=1′,
    dataType: ‘json’,
    url: customAction,
    success: function(data){
    if(typeof(data.errors) !== ‘undefined’)
    {
    alert(‘Error while saving customization data’);
    return;
    }

    }
    })
    }
    and in the line 200
    //send the ajax request to the server
    $.ajax({
    type: ‘POST’,
    headers: { “cache-control”: “no-cache” },
    url: baseUri + ‘?rand=’ + new Date().getTime(),
    async: false,
    cache: false,

    The followed the other tutorial files and did as you wrote, but still not working, any idea?

    • NemoPS

      Hard to tell at first glance, I used it myself last week on the same version and it worked like a charm, did you clear cache after creating the php override?

      • David

        i tried it four times already, would you mind uploading the files so i can take a look at the to see if i miss something. Also i noticed that in your demo web it does not work.

  • Algo

    Great job, this is exactly what I needed and it works perfectly.
    However, I have a question. I work on lenses website and in a product page, i divided product page with two parts with its attributes for each eye and just one submit add_to_cart button. I’d like attach my custom field to the two generated products in cart but it works for just the first. How to generate twice?
    If anyone can tell me how it works. Thanks All. Cheers.

  • thorink

    It is working for me but 1 case. If user is not logged in and the cookie is not created yet, the custom field is not saved. Can someone tell me why and how to resolve it? Thanks

    • Alex

      In ajax-cart.js add “async:false,” before “type: ‘POST'”, Nemo missed it in last example.

  • Dany Edy

    It’s working perfect in the view normal but in the quick view I can’t do the same. Can you help me? I’m trying to adapt you code but I can’t found the way.

    • NemoPS

      It’s going to be a real pain in the rear. Try opening a thread in the official forum, we can try something there if you post your snippets

      • Dany Edy

        Ok, I’ll try. thx for the answer

  • Sandra

    hi, i don’t understand the tools.js part. abit confusing.

    Do you mean remove this:

    if (parseInt(customizationFields[i][1]) == 1 && ($(‘#’ + customizationFields[i][0]).html() == ” || $(‘#’ + customizationFields[i][0]).text() != $(‘#’ + customizationFields[i][0]).val()) && !pattern.test($(‘#’ + customizationFields[i][0]).attr(‘class’)))

    and replace with this:?

    if (parseInt(customizationFields[i][1]) == 1 && ($(‘#’ + customizationFields[i][0]).val() == ”))

    I’m using a custom theme and this modification doesn’t work. need help.

  • Sandra

    Good day. suppose this directory is themes/*yourthemename*/js/modules/blockcart/ajax-cart.js

  • http://www.hancke.be Maarten Reynders

    I was able to move the customized fields just above the “Add to Cart button”. But with the testing i found out that there is a problem with the code.
    It’s possible to save the customized text just by using the “add to cart” button. And after that you can even hide the old save btton. But in my case when the user orders more then one piece of the same product i get a double entry in my cart.
    The first line is then displaying the product with the choosen quantity (for example 3).
    Just below there is the customized text that the user has entered. (so far perfect)
    But just below that there is another line wich is displaying the product with the choosen quantity minus 1
    In attachement a screenshot. The yellow part is the duplicate line.

  • http://www.hancke.be Maarten Reynders

    Is working perfectly with my 1.6.0.14 version.
    I have also one question. Is it possible to move the customized field just above the “Add to Cart” button?

    • Priyanshu Khandelwal

      how is it working in ur case, I can’t implement it. Please explain this

  • Ghetla

    Excellent Tutorial *****
    Worked in single attempt!.
    Got one
    question if based on custom text field content if i want to update
    shopping cart value then which is the best place to do it ?
    Example, Text length >20 chars then increase product cost by 1%.

Store Top Sales

You like the tuts and want to say "thank you"? Well, you can always feel free to donate: