1

How can I add custom item/product (without id) to the cart?

<?php 
get_header();

if($_POST['add_to_cart']){
     global $woocommerce;
     $name = isset($_POST['name']) ? $_POST['name'] : '';
     $q = isset($_POST['q']) ? $_POST['q'] : '';
     $price = isset($_POST['price']) ? $_POST['price'] : '';

     $woocommerce->cart->add_to_cart(null, $q, array('_price' => $price));


}

?>
<main>

<form method="POST" action="">
     <input type="text" name="name" />
     <input typoe="text" name="q" />
     <input type="text" name="price" />
     <input type="submit" name="add_to_cart" value="Pievienot grozam" />
</form>

</main>

Now, after I submit the form, its redirect me to the 404.php. What have I missed?

Vincenzo Di Gaetano
  • 3,892
  • 3
  • 13
  • 32
aidron
  • 127
  • 9
  • Can you get product SKU? If yes, you can use the following function: `wc_get_product_id_by_sku( $sku );` to get the product id by SKU. – Vincenzo Di Gaetano Feb 13 '21 at 10:54
  • Nop! Its more like service thing. Customer wants pay for some service, like coffee machine fix, or buy something what's is new arrivals, but not in e-shop. Easier is to add name, quantity and price and send to cart :) – aidron Feb 13 '21 at 11:04
  • I understand. Are these products that exist in the e-shop? Is the product name entered manually? – Vincenzo Di Gaetano Feb 13 '21 at 11:09
  • Basically it should be able to be entered by hand, via an html form – aidron Feb 13 '21 at 11:14
  • Or some products contains smaller packages and client wants buy one item of it. This kind method needed for on place payment, in actual store :) – aidron Feb 13 '21 at 11:17
  • By entering the name of the product by hand it is really difficult to obtain the product due to any typing errors etc ... The ideal would be to create a select with the list of available products and filter them based on the entered text. See an example here: https://stackoverflow.com/questions/52023865/searchable-multiple-product-select-custom-field-for-woocommerce – Vincenzo Di Gaetano Feb 13 '21 at 11:19
  • Of course, but in this case it's more for service, and there is no need for text accuracy. The shop owner will fill the form, add to cart – aidron Feb 13 '21 at 11:30
  • Of course, I understand what your need is. The problem is that you cannot add a product to the cart without the product id. So to get the product id you should at least know the name of the product or its SKU. The fastest would be to add a new text field to enter the product SKU. This is up to you to decide. – Vincenzo Di Gaetano Feb 13 '21 at 11:39
  • But if I need add things which is not in eshop? Woocommerce don't allow to insert in cart and do the payment? – aidron Feb 13 '21 at 11:44

1 Answers1

2

The answer to this question can be found here.

So assuming that you can't add products to your cart if they don't exist you can get around the problem that way:

  1. Create a simple product with the data you want (which will not be displayed on the e-shop) with these parameters:

    • Catalog visibility: Hidden
    • Manage stock?: No (do not check the "Enable stock management at product level" option)
    • Stock status: In Stock
  2. Get the post data from the form and with the add_to_cart method add the custom data for the cart item. Then with the hook woocommerce_before_calculate_totals set the custom data to the item in the cart (in your case: name and price).
    See Store custom data using WC_Cart add_to_cart() method in Woocommerce 3 answer code to understand how to do it.

EXAMPLE

You will need to create an add-product-to-cart.php file. Add it inside the directory where functions.php is located (inside the child theme folder). In the add-product-to-cart.php file insert the following code:

<?php 

// load the Wordpress classes (change the directory based on where you uploaded your PHP file, currently works if this file is inside the child theme folder)
include_once( __DIR__ . "/../../../wp-load.php" );

if ( isset( $_POST['add_to_cart'] ) && $_POST['add_to_cart'] ) {

    global $woocommerce;

    $name  = $_POST['name'];
    $qty   = (int) $_POST['q'];
    $price = (float) str_replace( ',', '.', $_POST['price'] );

    // set the id of the product created
    $product_id = 736;

    // gets the custom fields to set
    $cart_item_data['custom_price'] = $price;
    $cart_item_data['custom_name'] = $name;

    // adds the product to the cart with custom data
    $woocommerce->cart->add_to_cart( $product_id, $qty, $variation_id = 0, $variation = array(), $cart_item_data );

}

?>
<main>
<form action="add-product-to-cart.php" method="POST" enctype="multipart/form-data">
    <input type="text" name="name" placeholder="Name" required/>
    <input typoe="text" name="q" placeholder="Quantity" required/>
    <input type="text" name="price" placeholder="Price" required/>
    <input type="submit" name="add_to_cart" value="Pievienot grozam" />
</form>
</main>

The page url will look like (for example):

yourdomain.com/wp-content/themes/storefront-child/add-product-to-cart.php

First of all add these lines in your child theme's functions.php to include the custom page add-product-to-cart.php:

// load the contents of the add-product-to-cart.php file only on the respective page
if ( isset($_SERVER['REQUEST_URI']) ) {
    if ( strpos($_SERVER['REQUEST_URI'], 'add-product-to-cart.php') !== false ) {
        require_once( 'add-product-to-cart.php' );
    }
}

Also always in the functions.php file add this code:

// set the price and change the name of the cart item based on the form values
add_action( 'woocommerce_before_calculate_totals', 'set_custom_cart_item_data', 10, 1 );
function set_custom_cart_item_data( $cart ) {

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    foreach ( $cart->get_cart() as $cart_item ) {
        // if custom name and price are set, modify the product in the cart
        if ( isset( $cart_item['custom_price'] ) && isset( $cart_item['custom_name'] ) ) {
            $cart_item['data']->set_price( $cart_item['custom_price'] );
            $cart_item['data']->set_name( $cart_item['custom_name'] );
        }
    }
}

Finally, to force the change of the name and price of the items in the cart (especially in the mini cart) add this:

// update the price of the cart item (especially in the mini cart)
add_filter( 'woocommerce_cart_item_price', 'custom_cart_item_price', 10, 3 );
function custom_cart_item_price( $price, $cart_item, $cart_item_key ) {
    if ( isset( $cart_item['custom_price'] ) ) {
        return wc_price( $cart_item['custom_price'] );
    }
    return $price;
}

// update the name of the cart item (especially in the mini cart)
add_filter( 'woocommerce_cart_item_name', 'custom_cart_item_name', 10, 3 );
function custom_cart_item_name( $item_name, $cart_item, $cart_item_key  ) {
    if ( isset( $cart_item['custom_name'] ) ) {
        return $cart_item['custom_name'];
    }
    return $item_name;
}

The code has been tested and works.

POSSIBLE PROBLEMS

If you want to load the header into your custom php file you shouldn't use the get_header() function unless you check first to see if it has already been loaded.

The notice PHP Notice: Trying to get property 'order_awaiting_payment' of non-object in... will be shown on all pages where the header is loaded twice (be it the frontend or the backend).

SOLUTION #1

You can use a control (to be inserted in the functions.php of your active theme) to load the content of the custom page only when this is present in the url (so by exclusion it will not be loaded in all other pages) If your page is named differently, replace add-product-to-cart.php with the name of your page:

It is the same function as above.

// load the contents of the add-product-to-cart.php file only on the respective page
if ( isset($_SERVER['REQUEST_URI']) ) {
    if ( strpos($_SERVER['REQUEST_URI'], 'add-product-to-cart.php') !== false ) {
        require_once( 'add-product-to-cart.php' );
    }
}

SOLUTION #2

You can load the header by adding an include_once just after the wp-load.php load line inside the add-product-to-cart.php file:

<?php 

// load the Wordpress classes (change the directory based on where you uploaded your PHP file, currently works if this file is inside the child theme folder)
include_once( __DIR__ . "/../../../wp-load.php" );
include_once( __DIR__ . "/../../../wp-blog-header.php");

...
Vincenzo Di Gaetano
  • 3,892
  • 3
  • 13
  • 32
  • Thanks, but after submit it's sends me to 404 error – aidron Feb 13 '21 at 16:58
  • In the `action` attribute of the `form` tag you must enter the name of the current php file. – Vincenzo Di Gaetano Feb 13 '21 at 21:06
  • I have page-service.php where is form and the php, if I add in action page-service.php it send me to error 404 or if I use $_SERVER['PHP_SELF'], send me to 404 error.. If my form is in page-service.php and the php part is in the add-product-to-cart.php it do not recognize add_to_cart() function. When form and php are in add-product-to-cart.php, try to open in browser, there is: `Notice: Trying to get property 'order_awaiting_payment' of non-object in /home/kafijass/public_html/wp-content/plugins/woocommerce/includes/wc-cart-functions.php on line 184` – aidron Feb 14 '21 at 16:51
  • To resolve the `Notice: Trying to get property 'order_awaiting_payment' of non-object` error you must load `wp-load.php` before each call to any Wordpress function or class. Then, at the beginning of the custom PHP file you will need to insert `include_once( __DIR__ . "/../../../wp-load.php" );`. The directory will need to be changed based on the location of the file. – Vincenzo Di Gaetano Feb 14 '21 at 21:31
  • In add-product.to-cart.php i have php open tag and wp-load.php include and then i have `get_header()` function. I know it's got annoying, but i don get why this errors keep appearing :) – aidron Feb 15 '21 at 15:58
  • @aidron I have updated my answer with the solution for the notice you receive. In the future, I kindly ask you to test the code within any user answer. The solution was already included in my answer *(take this as advice, I'm not argumentative)*. – Vincenzo Di Gaetano Feb 15 '21 at 19:36
  • Sorry, but I don't have nerves to do this anymore... WooCommerce is simple thing, who are written very complicated, 1 000 000 functions... Easier is write ecommerce site from zero! All Youre suggestions and samples not working for me, sorry for wasteing youre time and big thanks for trying! ;) – aidron Feb 16 '21 at 16:54
  • @aidron Don't apologize, when I develop I never waste time. If my answer doesn't work for you I'm sure you've omitted some important information in your question. If you are interested in solving the problem please update your question. It is evident that you have only published part of your code. Mine is just a basic example of how to add a product to your cart from a custom page. – Vincenzo Di Gaetano Feb 16 '21 at 19:22
  • @VincenzoDiGaetano - Could this technically work for an ajax function? If i include this functionality within my themes function.php file, it technically should work when i call the function right? Just validating, before trying this solution. I am looking for exactly what the post's author was/is just no form needed, i could just send the data in ajax technically, right? – Royal Jun 21 '23 at 12:04