I am interested in implementing an "order again" function for OpenCart, with a button located in the order history list under account/order. This button would take all this particular order's products (and its associated options) and add them all back to the cart.
The following code I have allows me to loop through my product as well as its respective product options depending on the order ID.
$products = $this->model_account_order->getOrderProducts(
$this->request->get['order_id']
);
foreach ($products as $product) {
$options = $this->model_account_order->getOrderOptions(
$this->request->get['order_id'],
$product['order_product_id']
);
foreach ($product['option'] as $option) {
// Implement custom logic
}
}
I have no issues at the moment to get the products that I have already ordered. What I do not understand what to do and need help on is - how do I use this [if this is the way to add items to cart] to add the items back to the cart?
By knowing the product ID and quantities (and options if any) you can just call:
$this->cart->add($product_id, $quantity, $options); // use array() instead of $options if there are none
for each product respectively. $options is an array, to see what structure it has to have look into the AJAX request when adding a product with options to the cart (I think it's single array with product_option_id as key and option_value as value).
Try this
$this->cart->add($product_id, $quantity, $options);
This is actually already implemented on the customer side of the cart. Here's the code contained in the /catalog/controller/account/order.php that does exactly what you want (1.5.5.1 in this particular case)
if (isset($this->request->get['order_id'])) {
$order_info = $this->model_account_order->getOrder($this->request->get['order_id']);
if ($order_info) {
$order_products = $this->model_account_order->getOrderProducts($this->request->get['order_id']);
foreach ($order_products as $order_product) {
$option_data = array();
$order_options = $this->model_account_order->getOrderOptions($this->request->get['order_id'], $order_product['order_product_id']);
foreach ($order_options as $order_option) {
if ($order_option['type'] == 'select' || $order_option['type'] == 'radio') {
$option_data[$order_option['product_option_id']] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'checkbox') {
$option_data[$order_option['product_option_id']][] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'text' || $order_option['type'] == 'textarea' || $order_option['type'] == 'date' || $order_option['type'] == 'datetime' || $order_option['type'] == 'time') {
$option_data[$order_option['product_option_id']] = $order_option['value'];
} elseif ($order_option['type'] == 'file') {
$option_data[$order_option['product_option_id']] = $this->encryption->encrypt($order_option['value']);
}
}
$this->session->data['success'] = sprintf($this->language->get('text_success'), $this->request->get['order_id']);
$this->cart->add($order_product['product_id'], $order_product['quantity'], $option_data);
}
$this->redirect($this->url->link('checkout/cart'));
}
}
Upon investigation and posting on the OpenCart website, it turns out reorder is already a functionality in OpenCart, but we never enabled it [or someone disabled it months ago]. We had to renable it by adding a button and attributed the reorder link under /catalog/controller/account/order.php.
We also added some custom logic in our:
if ($order_option['type'] == 'select' || $order_option['type'] == 'radio') {
$option_data[$order_option['product_option_id']] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'checkbox') {
$option_data[$order_option['product_option_id']][] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'text' || $order_option['type'] == 'textarea' || $order_option['type'] == 'date' || $order_option['type'] == 'datetime' || $order_option['type'] == 'time') {
$option_data[$order_option['product_option_id']] = $order_option['value'];
} elseif ($order_option['type'] == 'file') {
$option_data[$order_option['product_option_id']] = $this->encryption->encrypt($order_option['value']);
}
Since we have a custom option type called checkbox+quantity. For the future users, please make sure to add your custom option type logic to this logical statement.
Thanks Jay for pointing this out as well.
Related
I am making an e-commerce web app and I am trying to write code to handle if the user clicks the add to basket button on an item that is already in the basket but it doesn't seem to work correctly. If the button is clicked for a second time then the first item quantity is overridden by the second
if ($action === 'add_product') {
$id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : null;
$price = isset($_REQUEST['price']) ? (float) $_REQUEST['price'] : null;
$quantity = isset($_REQUEST['quantity']) ? (int) $_REQUEST['quantity'] : null;
$name = isset($_REQUEST['name']) ? trim($_REQUEST['name']) : null;
if ($id && $price && $quantity) {
$_SESSION['cart'][$id] = [
'id' => $id,
'price' => $price,
'quantity' => $quantity,
'name' => $name
];
} // if the action is increment, increse the quantity of the item.
} elseif ($action === 'increment' and array_key_exists('id', $_SESSION['cart'][$_REQUEST['id']])) {
$_SESSION['cart'][$_REQUEST['id']]['quantity'] += 1;
//if the action is decrement, decrease the quantity of item by 1
} elseif(array_key_exists('id',$_SESSION['cart'][$_REQUEST['id']))//here is where it is supposed to handle adding the same item twice {
$_SESSION['cart'][$_REQUEST['id']]['quantity'] += 1;
} elseif ($action === 'decrement' and array_key_exists('id', $_SESSION['cart'][$_REQUEST['id']])) {
$_SESSION['cart'][$_REQUEST['id']]['quantity'] -= 1;
if ($_SESSION['cart'][$_REQUEST['id']]['quantity'] <= 0) {
unset($_SESSION['cart'][$_REQUEST['id']]);
}
} elseif ($action === 'remove' and array_key_exists('id', $_SESSION['cart'][$_REQUEST['id']])) {
unset($_SESSION['cart'][$_REQUEST['id']]);
}
I have commented where it is supposed to be adding to the quantity, but it does not add to the quantity and update it simply overrides the old quality and I don't know why. I have also tried if(in_array($id,$_SESSION['cart'][$_REQUEST['id'])) but that also did not work. I don't quite understand why it won't be working so any help is greatly appreciated.
Hi i'd like to make a validation for a specific custom field on Opencart 2.3.0.2. On my code i had to disable the default validation, so i need to make a validation for only one custom field on form. The custom field called "NUMBER", is the number of customer's address. The validation purpose is check if the field is empty or not. So i'm making this
$this->load->model('account/custom_field');
$custom_fields = $this->model_account_custom_field->getCustomFields($this->config->get('config_customer_group_id'));
foreach ($custom_fields as $custom_field) {
if (($custom_field['location'] == 'address') && $custom_field['required'] && empty($this->request->post['custom_field'][$custom_field['custom_field_id'] == 7])) {$json['error']['custom_field' . $custom_field['custom_field_id'] == 7] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);}}
But when i submit the form its not showing any error or the div with text danger. Can anyone helps with this code. I'm grateful
The opencart has a default code in array who validate all the field. What i did was delete this validate configuration and make for only one field. So, the default validation for custom field located on checkout/checkout - shiping_address form is,
foreach ($custom_fields as $custom_field) {
if (($custom_field['location'] == 'address') && $custom_field['required'] && empty($this->request->post['custom_field'][$custom_field['custom_field_id']])) {
$json['error']['custom_field' . $custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
} elseif (($custom_field['location'] == 'address') && ($custom_field['type'] == 'text') && !empty($custom_field['validation']) && !filter_var($this->request->post['custom_field'][$custom_field['custom_field_id']], FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/' . html_entity_decode($custom_field['validation'], ENT_QUOTES, 'UTF-8') . '/')))) {
$json['error']['custom_field' . $custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
}
}
i commented this code and make this modification
foreach ($custom_fields as $custom_field) {
if (($custom_field['location'] == 'address') && $custom_field['required'] && empty($this->request->post['custom_field'][$custom_field['custom_field_id']])) {
//number field
if($custom_field['custom_field_id'] == 7) {
$json['error']['custom_field' . $custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
}
} elseif (($custom_field['location'] == 'address') && ($custom_field['type'] == 'text') && !empty($custom_field['validation']) && !filter_var($this->request->post['custom_field'][$custom_field['custom_field_id']], FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/' . html_entity_decode($custom_field['validation'], ENT_QUOTES, 'UTF-8') . '/')))) {
$json['error']['custom_field' . $custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
}
}
i just make a condition checking if is custom_field 7, if empty, shows the error
You need to separate the condition logic. Right now you have compacted your syntax and damaged the logic.
empty($this->request->post['custom_field'][$custom_field['custom_field_id'] == 7])
// this is performing an evaluation -------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// returning a true or false value
// if false, the key is converted to 0 because false isn't a valid key
// if true, the key is converted to 1
The trouble is you don't have those numeric keys in $this->request->post['custom_field'], so they will make empty() return true.
Use this instead:
if ($custom_field['location'] == 'address'
&& $custom_field['required']
&& // iterate your post array and check $post['custom_field_id'] == 7
&& // iterate your post array and check empty($post['custom_field_value'])) {
Currently I'm making an application to suggest a car that suits the users' preferences based on their selection. I'd skip the HTML form but the choices are as follow:
Type: MPV, SUV, Sedan
Passengers: 5, 7
Engine: Gasoline, Diesel
Budget: Low, Medium, High, Luxury
Here I'm trying to make a combination because I want that every single choice counts. Therefore this is what I've done so far:
if (isset($_POST['submit'])
{
$type = $_POST['type'];
$passengers = $_POST['passengers'];
$engine = $_POST['engine'];
$budget = $_POST['budget'];
if ($type == 'MPV' && $passengers == 5 && $engine == 'Gasoline' && $budget == 'Low')
{
// execute my function here
}
else if ($type == 'MPV' && $passengers == 7 && $engine == 'Gasoline' && $budget == 'Low')
{
// execute my function here
}
else if ($type == 'MPV' && $passengers == 5 && $engine == 'Diesel' && $budget == 'Low')
{
// execute my function here
}
// and it goes on and on......
I know my problem is I will have to keep making if/elseif function until I list every single possible combination. Now is there any better way to do this? I need to get every single possible combination because there are additional calculations to be done in my custom function.
Here is my custom function:
public function calculateDetails($displacement_bottom, $displacement_top, $price_bottom, $price_top, $clearance)
{
$stmt = $connect->prepare("SELECT id, brand, name FROM cars
WHERE type = '$type'
AND displacement BETWEEN '$displacement_bottom' AND '$displacement_top'
AND price BETWEEN '$price_bottom' AND '$price_top'
AND clearance = '$clearance'
AND passengers = '$passengers'
AND engine = '$engine'
LIMIT BY 3");
$stmt->execute();
while ($row = $stmt->fetch())
{
$result = echo htmlspecialchars($row['brand'])." ".htmlspecialchars($row['name']);
}
return $result;
}
So I can do this
if ($type == 'MPV' && $passengers == 5 && $engine == 'Gasoline' && $budget == 'Low')
{
$suggestion = calculateDetails(1500, 2000, 100, 150, 'Medium');
echo $suggestion;
} else if ($type == 'MPV' && $passengers = 7 && $engine == 'Gasoline' && $budget = 'Low')
{
// continue on and on...
}
Since I couldn't find a way to simplify the if/else "loop" (and I started thinking about giving up :( ), I came into few new functions
public function typeDetails($type)
{
if ($type == 'MPV')
{
$detailed_type = echo "1500 AND 2000";
}
else if ($type == 'SUV')
{
$detailed_type = echo "2000 AND 3500";
}
else
{
$detailed_type = echo "2000 AND 3000";
}
return $detailed_type;
}
public function budgetDetails($budget)
{
if ($budget == 'Low')
{
$detailed_price = echo "100 AND 150";
}
else if ($type == 'Medium')
{
$detailed_price = echo "150 AND 250";
}
else if ($type == 'High')
{
$detailed_price = echo "250 AND 450";
}
else
{
$detailed_price = echo "450 AND 800";
}
return $detailed_price;
}
public function groundClearance($type)
{
if ($type == 'MPV')
{
$ground_clearance = "Medium";
}
else if ($type == 'SUV')
{
$ground_clearance = "High";
}
else
{
$ground_clearance = "Low";
}
return $ground_clearance;
}
In which I hope to be able to do this instead
$type = $_POST['type'];
$budget = $_POST['budget'];
$displacement = typeDetails($type);
$price = budgetDetails($budget);
// same function but with simplified where clause
$suggestion = calculateDetails($displacement, $price, $clearance);
echo $suggestion;
I'm too tired to continue today, but thanks to you guys I came up into few more ideas. I appreciate all the replies and comments given here. I might not be that good in PHP, therefore I want to learn how to make things simpler
There is not much you can do here. As an alternative option, you may create kind of a matrix for your models using multi level array:
$matrix = [
'MPV' => [
'Diesel' => [
5 => [
'Low' => function() { ... },
'Medium' => function() { ... },
],
],
'Gasoline' => [ ... ],
],
'SUV' => [ ... ],
];
if (isset($matrix[$type][$engine][$passengers][$budget])) {
$matrix[$type][$engine][$passengers][$budget]();
}
You may also reuse some functions if they don't differ too much or set special constants instead of functions to be passed to a single custom method. It really depends on the actual functionality you have for each combination.
Well I will pick that your function will be to choose a car, so you can add all options that you have (ex 3 different cars) into your database and have 4 values for each :
Type
Passengers
Engine
Budget
And set default value for each car :
ex for this condition :
$type == 'MPV' && $passengers == 5 && $engine == 'Gasoline' && $budget == 'Low'
Type = 1
Passengers = 1
Engine = 1
Budget = 1
And regarding clients choosing different options, you can filter your query and select the car that match the search !
Ex :
$query = ("SELECT * FROM car WHERE type = :type AND passengers = :passengers AND engine = :engine AND budget = :budget");
I want to avoid modifing every data from a SalesOrder when the status equals "Cancelled", I get it to work with the part that involves SalesOrder info data, but I can`t get it to work with product part... any ideas?
$pedido = SalesOrder_Record_Model::getInstanceById($entityId);
$vtEntityDelta = new VTEntityDelta ();
$anterior = $vtEntityDelta->getOldValue($entity->getModuleName(), $entityId, "sostatus");
$actual = $pedido->get("sostatus");
$VTIGER_BULK_SAVE_MODE = true;
if($anterior == "Approved" || $anterior == "Cancelled" || (($actual == "Approved" || $actual == "Cancelled") && (!$vtEntityDelta->hasChanged($entity->getModuleName(), $entityId, 'sostatus')))){
foreach ($pedido->getModule()->getFields() as $field)
if($vtEntityDelta->hasChanged($entity->getModuleName(), $entityId, $field->getName()))
$pedido->set($field->getName(), $vtEntityDelta->getOldValue($entity->getModuleName(), $entityId, $field->getName()));
$pedido->set("mode","edit");
$pedido->save();
}
That works for info data as I was saying, some has any idea about this??
Thanks!
Context: On our website, we calculate whether an item/order meets the criteria for free shipping using an if statement evaluating if a value - 'ff' - is true. Depending on conditions met it sets the shipping appropriately. We now require the need to evaluate if an item is free shipping with the order of _. To top it off, for other purposes such as external feeds, another condition must apply (weight must = 0 or have no value), for fw to be free freight. Here is the code, that I can't figure out why it is NOT working:
if($r['freeFreight'] == 'ff' || ($r['freeFreight'] == 'fw' && ($r['weight'] == 0 || $r['weight'] == '') ) ) {
$r['shippingStandard'] = 0;
}
Are the conditions overly segregated in the if statement with the sets of ()? Or, are they just incorrectly placed. For example, should it be:
if(($r['freeFreight'] == 'ff') || ($r['freeFreight'] == 'fw' && $r['weight'] == 0 || $r['weight'] == '') ) {
$r['shippingStandard'] = 0;
}
The second of the two seems more logical to me because: if ($r['freeFreight'] == 'ff') - then regardless of the following conditions the statement should return true and set the variable to 0. Is my logic correct? Sorry for this newb question...
Thanks for any help you can offer.
So I think perhaps, based on the answers so far (thanks everybody that has chimed in to help an amateur) - I think I am going to do a trial with:
if( ($r['freeFreight'] == 'ff') || ( $r['freeFreight'] == 'fw' ) && empty( $r['weight'] ) ) {
$r['shippingStandard'] = 0;
}
Planning to run trial with this, if it is fundamentally wrong, please advise.
Try this out?
if( $r['freeFreight'] == 'ff' || ( $r[ 'freeFreight' ] == 'fw' && empty( $r['weight'] ) ) ) {
$r['shippingStandard'] = 0;
}
using empty might be a little cleaner too
if you really want to break it out more, you could do this
if( $r[ 'freeFreight' ] == 'ff' ) {
$r[ 'shippingStandard' ] = 0;
} elseif( $r[ 'freeFreight' ] == 'fw' && empty( $r[ 'weight' ] ) ) {
$r[ 'shippingStandard' ] = 0;
} else {
// Everything else
}
If you want to shorten the variables, and will be using them later:
$freight = $r[ 'freeFreight' ];
$weight = isset( $r[ 'weight' ] ) ? $r[ 'weight' ] : null;
$shipping = $r[ 'shippingStandard' ]; // Or whatever you want the default value to be...
if( $freight == 'ff' || ( $freight == 'fw' && empty( $weight ) ) ) {
$shipping = 0;
}
// ... Later down the file
$r = array(
'freeFreight' => $freight,
'weight' => $weight,
'shippingStandard' => $shipping
);
I really cant tell how the rest of the file looks, like if this is in a function to get shipping, you could simply just return $shipping. Hope this helps. You should be able to move the concepts around to get what you want
your first option should should be identical to the following:
if ($r['freeFreight'] == 'ff') {
$r['shippingStandard'] = 0;
}
elseif ($r['freeFreight'] == 'fw') {
// the dual check for an empty value here is kind of redundant and unneccessary.
// I'd probably just use "if ($r['weight'])" and be done with it
if ($r['weight'] == 0) {
$r['shippingStandard'] = 0;
}
elseif ($r['weight'] == '') {
$r['shippingStandard'] = 0;
}
else {
// $r['shippingStandard'] stays whatever it was before
}
}
else {
// $r['shippingStandard'] stays whatever it was before
}
if that logic looks right to you, then you should be right on and I'd print_r($r) to make sure it's holding what you expect it to be holding. Your parenthesis in the first example is exactly how I would do it.