Page 1 of 1

sandwichbar online order website

Posted: Mon Mar 19, 2018 2:02 am
by HarmO
Hi,
I would like your feedback on this:

i need to make a online order website for a company that makes sandwiches.
they want this to speed up the queue they have in front of their shop every noon.

i've been looking at the modules "product" & "cart2" the last 2 days, but was unable to set it up orders with different options (except with SKU codes which they don't have).

For example people want to order a ham cheese sandwich with extra pickles as topping for Tom and 1 ham cheese sandwich with mayonnaise for Linda.

Within the product detail page they should be able to select some options and add it to the cart. (options do change the price).

No need for shipping, taxes or online payments for this shop.
But i would like to use FEU.

What do you think? Will this work with the above mentioned modules or do i need to build my own shopping cart??

Re: sandwichbar online order website

Posted: Tue Apr 10, 2018 8:59 am
by HarmO
Ok, i made a simple shopping cart UDT that adds my items into the session variable shopping_cart (see code below).

Now the only thing i'm struggling with is this: i want to check if the description tag of a newly added product is different from an existing product line so i can add twice the same product, but with different descriptions... (example ham sandwich on white bread and ham sandwich on brown bread.)

Code: Select all

if(isset($_POST["cartaction"]) and !empty($_POST["cartaction"]))
{
	switch($_POST["cartaction"]){
		case "add":
			if(isset($_SESSION["shopping_cart"]))
			{  
				$item_array_id = array_column($_SESSION["shopping_cart"], "item_id");
				if(!in_array($_POST["itemID"], $item_array_id))
				{  
					$count = count($_SESSION["shopping_cart"]);  
					$item_array = array(   
						'item_id'			=>     $_POST["itemID"],  
						'item_name'			=>     $_POST["name"],
						'item_details'		=>     $_POST["details"],
						'item_price'		=>     $_POST["price"],  
						'item_quantity'		=>     $_POST["quantity"] 
					);  
					$_SESSION["shopping_cart"][$count] = $item_array;  
				}  
				elseif(in_array($_POST["itemID"], $item_array_id))
				{  
					$cart_item = array_search($_POST["itemID"], $item_array_id);
					$_SESSION["shopping_cart"][$cart_item]['item_quantity']= $_SESSION["shopping_cart"][$cart_item][item_quantity]+$_POST["quantity"];
				}  
			}  
			else
			{  
				   $item_array = array(  
						'item_id'			=>     $_POST["itemID"],  
						'item_name'			=>     $_POST["name"],  
						'item_details'		=>     $_POST["details"],
						'item_price'		=>     $_POST["price"],  
						'item_quantity'		=>     $_POST["quantity"]  
				   );  
				   $_SESSION["shopping_cart"][0] = $item_array;  
			}
			break;
		case "eddit":
			$_SESSION["shopping_cart"][$_POST["cart_item"]]['item_quantity'] = $_POST["quantity"];
			break;
		case "remove":
			unset($_SESSION["shopping_cart"][$_POST["cart_item"]]);
			$_SESSION["shopping_cart"]=array_values($_SESSION["shopping_cart"]);
			break;
	}
}

Re: sandwichbar online order website

Posted: Tue Apr 10, 2018 9:09 am
by velden
I don't see a description tag in your code.

I think it might be possible to make a hash of the description and use it as the key for the array:

$_SESSION["shopping_cart"][YOUR_HASH_VALUE] = $item_array;

Re: sandwichbar online order website

Posted: Tue Apr 10, 2018 1:02 pm
by HarmO
Sorry, my descriptiontag is $_POST["details"].

Re: sandwichbar online order website

Posted: Tue Apr 10, 2018 1:06 pm
by velden
Then you might use that to create the hash.

One warning: never trust user input. Those $_POST variables are user input so be careful to check their values before further processing.

Re: sandwichbar online order website

Posted: Tue Apr 10, 2018 1:55 pm
by HarmO
I was counting on sanitizing the user input, no worries there.
i see where your getting at, but in my case the check should now be on the combination of 2 variables: $_POST["itemID"] and $_POST["details"].

I'm not realy a PHP programmer so if i just can do the check in this part, i don't have to reorganize my cart's mechanism for the keys, nor do i need to redo my cart template ect.

Code: Select all

$item_array_id = array_column($_SESSION["shopping_cart"], "item_id");
//check if already in cart
if(!in_array($_POST["itemID"], $item_array_id)) //not in the cart yet?
{
    add to the end of the cart
}
else{
    Modify line in cart
}

Re: sandwichbar online order website

Posted: Mon Apr 16, 2018 10:05 am
by HarmO
I made it work with the hash, thanks for the idea Velden.

will be posting details after i finished up the project.

Re: sandwichbar online order website

Posted: Wed May 09, 2018 8:08 am
by HarmO
So i promised my simple solution and here it is:

Well it is not that simple but this way i did not have to work with the e-commerce suite which was to advanced for my use case.

As suggested by Velden i created a plugin called function.simplecart.php in the "/assets/plugins" folder.

in this plugin i wrote the next function, where again as suggested by Velden, i generate a ripemd160 hash for each line in the cart.

Code: Select all

<?php
function smarty_cms_function_simplecart()
{
	if(isset($_POST["cartaction"]) and !empty($_POST["cartaction"]))
	{
		switch($_POST["cartaction"]){
			case "add":
				//check vars
				
				$postItemId = "";
				if(isset($_POST["itemID"])){ 
					$postItemId = filter_input(INPUT_POST, 'itemID', FILTER_SANITIZE_SPECIAL_CHARS);
				}
				$postName = "";
				if(isset($_POST["name"])){
					$postName = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_SPECIAL_CHARS);
				}
				$postDetails = "";
				if(isset($_POST["details"])){
					$postDetails = filter_input(INPUT_POST, 'details', FILTER_SANITIZE_SPECIAL_CHARS);
				}
				$postPrice = "";
				if(isset($_POST["price"])){
					$postPrice = filter_input(INPUT_POST, 'price', FILTER_SANITIZE_SPECIAL_CHARS);
				}
				$postQuantity = ""; 
				if(isset($_POST["quantity"])){
					$postQuantity = filter_input(INPUT_POST, 'quantity', FILTER_SANITIZE_SPECIAL_CHARS);
				}
				$postRemark ="";
				if(isset($_POST["remark"])){  
					 $postRemark = filter_input(INPUT_POST, 'remark', FILTER_SANITIZE_SPECIAL_CHARS);
				}

				$postHash = hash('ripemd160', urlencode($postItemId."-".$postDetails."-".$postRemark));
				
						
				if(isset($_SESSION["cart"][lines])) //if shoppingcart exists
				{  
					$item_array_id = array_column($_SESSION["cart"][lines], "item_hash");
					
					if(!in_array($postHash, $item_array_id))
					{  
						$count = count($_SESSION["cart"][lines]);  
						$item_array = array(   
							'item_id'			=>	$postItemId,  
							'item_name'			=>	$postName,
							'item_details'		=>	$postDetails,
							'item_price'		=>	$postPrice,  
							'item_quantity'		=>	$postQuantity,
							'item_remark'		=>	$postRemark,
							'item_hash'			=>	$postHash
						);  
						$_SESSION["cart"][lines][$count] = $item_array;  
					}  
					else //if(in_array($posthash, $item_array_id))
					{  
						$cart_item = array_search($postHash, $item_array_id);
						$_SESSION["cart"][lines][$cart_item]['item_quantity']= $_SESSION["cart"][lines][$cart_item][item_quantity]+$postQuantity;
					} 
				}  
				else
				{  
					   $item_array = array(  
							'item_id'			=>	$postItemId,  
							'item_name'			=>	$postName,
							'item_details'		=>	$postDetails,
							'item_price'		=>	$postPrice,  
							'item_quantity'		=>	$postQuantity,
							'item_remark'		=>	$postRemark,
							'item_hash'			=>	$postHash
					   );  
					   $_SESSION["cart"][lines][0] = $item_array;
				}
				break;
			case "eddit":
				$postCartItem = filter_var($_POST["cart_item"], FILTER_SANITIZE_SPECIAL_CHARS);
				$postQuantity = filter_var($_POST["quantity"], FILTER_SANITIZE_SPECIAL_CHARS);
				$_SESSION["cart"][lines][$postCartItem]['item_quantity'] = $postQuantity;
				break;
			case "remove":
				$postCartItem = filter_var($_POST["cart_item"], FILTER_SANITIZE_SPECIAL_CHARS);
				unset($_SESSION["cart"][lines][$postCartItem]);
				$_SESSION["cart"][lines]=array_values($_SESSION["cart"][lines]);
				break;
			case "empty":
				unset($_SESSION["cart"][lines]);
				break;
		}
		$_SESSION["cart"][details][itemcount] = array_sum(array_column($_SESSION["cart"][lines], "item_quantity"));
		if (count($_SESSION["cart"][lines]) > 0)
		{
			foreach($_SESSION["cart"][lines] as $line)
			{
				$grantotal=$grantotal+($line[item_price]*$line[item_quantity]);
			}
			$_SESSION["cart"][details]["grandtotal"]= $grantotal;
		}
		else
		{
			$_SESSION["cart"][details]["grandtotal"]= 0;
		}
		
		
	}
	if(!isset($_SESSION["cart"][details]))
	{
	   $_SESSION["cart"][details][itemcount] = 0;
	   $_SESSION["cart"][details]["grandtotal"]= 0;
	}
}
?>
Next i made sure to load the {simplecart} function as first on the page with the add to cart button.

Next i had a LISE instance with products
this is my summary template:

Code: Select all

<div class="products">
	{foreach from=$items item=item}
	{LISELoader item='category' force_array=1 value=$item->fielddefs.cat->value assign='cats'}
	<div class="product">
		{if $item->fielddefs.foto->value}
			<div class="text-center">
				{CGSmartImage src="{$item->fielddefs.foto->GetImagePath(true)}/{$item->fielddefs.foto->value}" filter_resize='h,100' noembed=1}
			</div>
		{/if}
		<form method="post" action="{cms_selflink href=8}" class="add_to_cart">
			<p class="title"><strong>{$item->title}</strong></p>
			{if 'Broodje'|in_array:$cats}
				<select name="details" class="details">
					{foreach $shop.broodjes as $broodje}
						<option value="{$broodje}">{$broodje}</option>
					{/foreach}
				</select>
			{/if}
			{if !empty($item->desc)}<p>{$item->desc}</p>{/if}
			<div class="prijs">€ {$item->price}</div>
			<input type="hidden" name="cartaction" class="cartaction" value="add">
			<input type="hidden" name="itemID" class="itemID" value="{$item->alias}{if 'Broodje'|in_array:$cats}-{$cats|implode:'-'}{/if}">
			<input type="hidden" name="name" class="name" value="{if 'Broodje'|in_array:$cats}{$cats|implode:'-'} {/if}{$item->title}">
			<input type="hidden" name="price" class="price" value="{$item->price}">
			<input type="hidden" name="quantity" class="quantity" value="1">
			<button type="submit" name="add_to_cart" class="cartbutton"><i class="fa fa-cart-plus"></i></button>
			<a href="{$item->url}" class="button">Opmerking</a>
		</form>
	</div>
	{/foreach}
	</div>
and detail template:

Code: Select all

{include file='cms_template:Prinske-Vars'}
{LISELoader item='category' force_array=1 value=$item->fielddefs.cat->value assign='cats'}
<div class="product" id="productdetail">
	<form method="post" action="{cms_selflink href=2}" class="add_to_cart">
		<p class="title"><strong>{$cats|implode:'-'} {$item->title}</strong></p>
		{if $item->fielddefs.cat->value|strstr:'1'}
			<select name="details" class="details">
				{foreach $shop.broodjes as $broodje}
					<option value="{$broodje}">{$broodje}</option>
				{/foreach}
			</select>
		{/if}
		{if !empty($item->desc)}<p>{$item->desc}</p>{/if}
		<div class="prijs">€ {$item->price}</div>
		<input type="hidden" name="cartaction" value="add">
		<input type="hidden" name="itemID" class="itemID" value="{$item->alias}{if 'Broodje'|in_array:$cats}-{$cats|implode:'-'}{/if}">
		<input type="hidden" name="name" class="name" value="{if 'Broodje'|in_array:$cats}{$cats|implode:'-'} {/if}{$item->title}">
		<input type="hidden" name="price" class="price" value="{$item->price}">
		<input type="hidden" name="quantity" class="quantity" value="1">
		<textarea name="remark" id="remark" cols="30" rows="10" placeholder="{$_t.form.remark}"></textarea>
		<button type="submit" name="add_to_cart" class="cartbutton"><i class="fa fa-cart-plus"></i></button>
	</form>
</div>
I also made sure that on submit of the add-to-cart form, the page did not reload and the cart-icon with the number of items got updated.
In the summary page a reload would mean you have to scroll all the way down again to continue.

Preventing a reload was don with a little Jquery functions you can call on each form:

Code: Select all

$(".products .product").each(function(){
	$("form", this).on( "submit", function(event) {
		event.preventDefault(); 
		var posturl = $(this).attr("action") + "?showtemplate=false"; //console.log('posturl: '+ posturl);
		var postdata = $(this).serialize(); //console.log('postdata: '+ postdata);
		
		$.post(posturl,postdata,function(data) { 
			$(".cart.items").text(data);
// updates the 
		});
	});
});
next you want the see and maybe modify the cart....

Code: Select all

{if $smarty.session.cart.details.itemcount > 0}
	<div class="showcart">
	{foreach $smarty.session.cart.lines $line}
		<div class="cart-item">
			<div class="name">
				{$line.item_name|escape}{if $line.item_details} ({$line.item_details|escape}{if !empty($line.item_remark)} - {$line.item_remark}{/if}){/if}
			</div>
			<div class="price">
				{if $line.item_quantity gt 1}
				<form action="{cgsimple::self_url()}" method="post" class="minus1">
					<input type="hidden" name="cartaction" value="eddit">
					<input type="hidden" name="cart_item" value="{$line@key|escape}">
					<input type="hidden" name="quantity" value="{$line.item_quantity|escape -1}">
					<button type="submit" name="eddit"><i class="fa fa-minus"></i></button>
				</form>
				{else}
				<button type="submit" name="eddit" disabled><i class="fa fa-minus"></i></button>
				{/if}
				{$line.item_quantity|escape}
				<form action="{cgsimple::self_url()}" method="post" class="add1">
					<input type="hidden" name="cartaction" value="eddit">
					<input type="hidden" name="cart_item" value="{$line@key|escape}">
					<input type="hidden" name="quantity" value="{$line.item_quantity|escape +1}">
					<button type="submit" name="eddit"><i class="fa fa-plus"></i></button>
				</form>
				<span>€{($line.item_price*$line.item_quantity)|string_format:"%.2f"}</span>
				<form action="{cgsimple::self_url()}" method="post" class="remove">
					<input type="hidden" name="cartaction" value="remove">
					<input type="hidden" name="cart_item" value="{$line@key|escape}">
					<button type="submit" name="remove"><i class="fa fa-trash"></i></button>
				</form>
			</div>
		</div>
	{/foreach}
		<div class="grandtotal">Totaal: € {$smarty.session.cart.details.grandtotal}</div>
	</div>
{else}
	<p>Het winkelwagentje is leeg</p>
{/if}
{/function}
there will probably be some improvements to be made on the plugin.
All the rest is layout and setup work.

I hope i helped someone by sharing my solution.

Re: sandwichbar online order website

Posted: Wed May 09, 2018 1:45 pm
by velden
Bedankt voor je uitleg! Nog erg uitgebreid voor een 'niet PHP-programmeur'.

Paar opmerkingen:

Aan een array in php kun je gewoon nieuwe elementen toevoegen zonder daarvoor per se een index op te geven.

http://php.net/manual/en/language.types.array.php

Code: Select all

$arr[key] = value;
$arr[] = value;
// key may be an integer or string
// value may be any value of any type
Dat zou je code iets eenvoudiger maken en wat dubbele zaken eruit kunnen.

Ik denk dat

Code: Select all

$_SESSION["cart"][lines]
zou 'moeten' zijn

Code: Select all

$_SESSION['cart']['lines']
omdat, voor zover ik het zie, 'lines' als een normale string moet worden behandeld.

Verder vind ik het tricky dat je de gegevens van de bezoeker overneemt, zoals de prijs bijvoorbeeld. Ik neem aan dat de website eigenaar de bedragen nog een keer controleert alvorens te leveren?
Het is anders relatief eenvoudig om de prijzen te beinvloeden.

Je zou dat kunnen oplossen door met codes voor de producten en opties te werken en daarvan de data uit LISE op te vragen en die dan in de sessie op te slaan. Maar dat maakt het uiteraard een stukje complexer.

Mooie handleiding zo!

Re: sandwichbar online order website

Posted: Tue May 15, 2018 9:45 am
by HarmO
Hi Velden,
Thank you for the feedback.
i did my best, but as you could see, there are many improvements one could make on my code :-)

i'll improve my code for the $_SESSION['cart']['lines'] as you suggested.

Many plug&play cart solutions use this implementation.
Yes we could get the information from the DB instead, but this would have complicated things and budget was limited ;-)

The owner of the sandwich bar does checks all orders, then confirms to the customer so this solution is sufficient.

I'm also curious to the tutorial Rolf is making...