How to work with custom shopping cart in php - javascript

I have successfully built a shopping cart and everything works fine.
Here is what i want to accomplish:
if a user clicks on the add to cart button, it should add to the cart and refreshe the same page with the added item update.
If a user clicks on the image or View button, it displays the image of the item.
my problem is as follows:
1. IF a user clicks on the add to cart button, it adds but redirects to the cart.php file (Which is not what i want, I want the page to reload with the added item update)
2. I tried using:
$head = $_SERVER['HTTP_REFERER'];
echo '<script>location.href="'.$head.'"</script>';
exit();
It seems to work well, when a user clicks add to cart button, but when a user clicks on image or click on view button, it refrehes the same page without allowing the user to view the image.
Below is my code.
//My custom shopping cart script
if (isset($_GET['pid'])) {
$pid = $_GET['pid'];
$wasFound = false;
$i = 0;
// If the cart session variable is not set or cart array is empty
if (!isset($_SESSION["cart_array"]) || count($_SESSION["cart_array"]) < 1) {
// RUN IF THE CART IS EMPTY OR NOT SET
$_SESSION["cart_array"] = array(0 => array("item_id" => $pid, "quantity" => 1));
} else {
// RUN IF THE CART HAS AT LEAST ONE ITEM IN IT
foreach ($_SESSION["cart_array"] as $each_item) {
$i++;
while (list($key, $value) = each($each_item)) {
if ($key == "item_id" && $value == $pid) {
// That item is in cart already so let's adjust its quantity using array_splice()
array_splice($_SESSION["cart_array"], $i-1, 1, array(array("item_id" => $pid, "quantity" => $each_item['quantity'] + 1)));
$wasFound = true;
} // close if condition
} // close while loop
} // close foreach loop
if ($wasFound == false) {
array_push($_SESSION["cart_array"], array("item_id" => $pid, "quantity" => 1));
}
}
$head = $_SERVER['HTTP_REFERER'];
//header("location: $head");
echo '<script>location.href="'.$head.'"</script>';
exit();
}?>
<!--My Products item Display-->
<div class="col-md-4">
<div class="product-img product-img-brd">
<img class="full-width img-responsive" src="../../backend/'.$product_img.'" alt="'.$product_name.'">
<a class="product-review" href="pro_single.php?pid='.$id.'">Quick review</a>
<a class="add-to-cart" href="cart.php?pid='.$id.'" ><i class="fa fa-shopping-cart"></i> Add to cart </a>
<div class="product-price">
<span class="title-price">$'.$price.'</span>
</div>
</div>

Instead of making it a link (with an anchor tag <a>), you'll want to write some JavaScript that will use one of the many different ways to call it with AJAX.
A simple version might look like this:
// update <a> in HTML
<a onclick="addToCart(' . $id . ')">Add to Cart</a>
// JavaScript
const addToCart = id => fetch('cart.php?pid=' + id).then(response => { /* do something with response here */ });
This is a simple version, but it gives you an idea. There are tons of guides for AJAX with all the different methods (I prefer the newer fetch(), but there is jQuery.ajax() and pure XMLHttpRequest too, amongst others).
When you use an anchor with a link, it's saying "go to this place", which isn't what you want. Using AJAX, you can pull down data without affecting the flow of the page itself.
Side note: ideally, change your <a> to a <button> as well. This will make it more semantic, since a button is a thing to click to do thing and an anchor is a link to another page.

Related

is there a way to update a div with new content only using ajax

This is the div that i am updating
but i want to add a active class to the (li) item
every time the div refreshes the active class goes away
so i don`t want to refresh all the data in the (ul) but
only add (li) if there is a new data in the database,
with out refreshing the previous (li) items
<div id="contacts">
<ul id="rooms" class="rooms">
<!-- This is where the data get inserted -->
<!-- the ajax call and this -->
<li class='contact' data-val='<?php echo $room['id']; ?>'>
<div class='wrap'>
<div class='meta'>
<p class='name'><?php echo $room['sender']; ?></p>
<p class='preview'><?php echo $room['senderemail']; ?></p>
</div>
</div>
</li>
</ul>
</div>
this is my ajax call
$(document).ready(function() {
var interval = setInterval(function(){
$.ajax({
url: 'rooms.php',
success: function(data){
$('#rooms').html(data);
}
});
}, 1000);
});
in the room php
$rooms = get_rooms();
foreach($rooms as $room){
?>
<li class='contact' data-val='<?php echo $room['id']; ?>'>
<div class='wrap'>
<div class='meta'>
<p class='name'><?php echo $room['sender']; ?></p>
<p class='preview'><?php echo $room['senderemail']; ?></p>
</div>
</div>
</li>
<?php
}
the get_rooms() function
function get_rooms() {
$sql = "SELECT id, sender, senderemail FROM chatroom ";
$result = mysqli_query($GLOBALS['dbh'], $sql);
$rooms = array();
while($room = mysqli_fetch_assoc($result)){
$rooms[] = array('id'=>$room['id'], 'sender'=>$room['sender'],
'senderemail'=>$room['senderemail']);
}
return $rooms;
}
You Just need to push new data to the div as below just replace your line with:
$('#rooms').append(data);
this will add new <li> in your existing <div> after the last <li>
jquery append()
To get the id of the last <li>
var lastId = $( "#rooms li" ).last().attr('id');
Once you get the last id then pass it in your ajax call.
If I understand you correctly, your problem is that you lose the active class (which you clicked on the li container) when there is new data.
This has to do with the fact that you exchange all of the content.
There are now three options. Either
You give the rooms.php the id of the currently active li-container
and this script sets the active class for the affected container.
You transfer all the chatrooms (ids) already shown to rooms.php and only
load the new ones (this means effort later with sorting).
You save the active li class and re set it after content changed (this is the fastest)
f.e: in your Ajax succes functions:
let id=0;
let active_li = $('li.active');
if (active_li.length>0) id=active_li.data('val');
$('#rooms').html(data);
if (id!=0) $('li[data-val="'+id+'"]').addClass ('active');
A few other thoughts:
Note the interval of 1000ms. Possible it makes Problems if the request lasts longer than 1000ms. This may still work well in your tests, but maybe not anymore if there are a hundred or 1000 users in your application.
Doesn't it make sense to tell the server when you click the active room and save it in a session so that the server knows which room is active in the client?
You need to simply update your JS code like:
$(document).ready(function() {
var active_list = '';
var interval = setInterval(function(){
$.ajax({
url: 'rooms.php',
beforeSend: function(){
active_list = $('#rooms').find('li.contact.active').attr('data-val');
}
success: function(data){
$('#rooms').html(data);
$(data).find('li[data-val="' + active_list +'"]').addClass('active');
}
});
}, 1000);
});
This should solve your problem and Let me know if you still face any issue.

Taking just a few elements in a JSON object

I'm using Laravel 5.5.* and jQuery (jquery-3.3.1.min.js).
I commercially develop mostly (like 95% of the time) in PHP, so using jQuery is really different for me, so I need help.
I am developing a blog's landing page and I must show just 3 posts in it. In it's bottom, I have a button <a> that is supposed to load 3 more posts and show it to the user. Everytime the user hits this button, 3 more posts must load in the page.
I have the following codes so far.
Posts controller
public function index() {
// this loads the landing page with 3 initial posts
// Working like a charm
$posts = Posts::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('published', 'desc')
->limit(3)
->get();
$rank = self::blogPanel();
return view('portal.pages.blog',
compact(
'rank',
'posts'
)
);
}
I call this action from the route
Route::get('/', 'User\PostsController#index')->name('landingPage');
For the logic in which I load more posts, I have the following:
Posts Controller
public function loadMore() {
$posts = Post::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('publicacao', 'desc')
// ->limit(3) I took this out because I was trying to set the limit in front-end
->get();
return json_decode($posts);
}
Which returns the following:
array:48 [▼
0 => {#257 ▼
+"id": 48
+"title": "Lorem ipsum"
+"summary": "Perferendis labore veritatis voluptas et vero libero fuga qui sapiente maiores animi laborum similique sunt magni voluptate et."
+"content": """
Really long text here, with line-breaks and all
"""
+"seo_id": null
+"url": "Sunt rerum nisi non dolores."
+"link_title": "dolor"
+"published": "2018-03-01 10:35:12"
+"scheduled": "2018-03-01 10:25:12"
+"user_id": 1
+"media_id": null
+"status": 1
+"created_at": "2018-03-01 10:25:12"
+"updated_at": "2018-03-01 10:25:12"
+"category_id": 3
+"slug": "cum-aut-officia-consequatur-dolor"
+"categories": []
+"user": {#313 ▼
+"id": 1
+"name": "Jonessy"
+"email": "jonessy#email.com"
+"status": 1
+"grupo_id": 1
+"created_at": null
+"updated_at": null
}
+"media": null
}
1 => {#341 ▶}
2 => {#254 ▶}
]
Please, note I'm using json_decode() because it looks easier to work with in front-end.
This is my blade file, where I should print my results
blogPost.blade.php
#foreach($posts as $post)
#php
$date = date_create($post->published);
#endphp
<div class="blog-post" id="blog-post">
<div class="blog-post__title" >
<h3 id="artTitle">
{{ $post->title }}
</h3>
#foreach($post->categories as $cat)
<span class="blog-post__category"> {{ $cat->name }} </span>
#endforeach
<span class="blog-post__date" id="artDate">
{{ date_format($date,"d/m/y - H") }}H
</span>
</div>
<div class="blog-post__image" id="artImage">
#if(isset($post->media_id))
<img src="{{ asset('img/post-img/' . $post->media->file) }}">
#else
<img src="{{asset('img/post-img/default-img-post.jpg')}}">
#endif
</div>
<div class="blog-post__resume">
<p id="artSumma">
{{ $post->summary }}
</p>
</div>
<div class="blog-post__link">
<a href="{{ route('blogPost', $post->slug) }}">
Read More
</a>
</div>
<div class="blog-post__social">
// Some social media links for sharing
</div>
</div>
#endforeach
I am calling the loadMore() method from PostsController using a GET route:
Route::get('/', 'User\PostsController#loadMore')->name('loadMore');
For jQuery, here is the code I got so far:
<script type="text/javascript">
// after importing jquery file...
$(document).on('click', '#loadingPosts', function(e) {
e.preventDefault();
var listing = {!! $posts !!};
console.log("list ", listing);
var lastId = listing[2].id;
console.log("id from pos 2, listing ", lastId);
var parts = $(listing).slice(lastId);
console.log("part ", parts);
// THIS DOESN'T WORK, BY THE WAY
// var lastId = listing[2].id;
// console.log("listing 2 id", lastId);
$('#loadingPosts').html("Loading More Posts");
$.ajax({
url: '{{ route('loadMore') }}',
method: 'GET',
// data: {
// 'id': lastId,
// I was trying to set up an ID here, but it didn't work as well
// },
// contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
// console.log("checking if data not null", data);
// this returns the expected $posts array
$('#blog-post').html(data);
// using .html() because .append() didn't work, which is weird, I guess
console.log("data depois do apend", data);
// This returns the same $posts array
lastId = data[2].id;
console.log("last id from data", lastId);
// I can retrieve the id from the given position from the array.
// I was trying to set up the id here so I could check, with jQuery, the last post iterated, so I could continue from that point forward
$('#loadingPosts').html("load More");
return data[2].id;
// A last minute despair
}
});
});
</script>
Well, it doesn't work (that's the reason I'm here). I really don't know what I am doing wrong, since the $posts array is passing...
I need help with this, please.
A few things worth saying:
Laravel comes with a default pagination, but it works "horizontally", and the projects asks for a "vertical" pagination.
The page must have a "load more" button because the footer has some much needed info, so the content can not load automatically
IF there is a way to make it work using vanilla JavaScript OR using Laravel's PHP methods (EXCEPT FOR THE PAGINATION METHOD, AS STATED BEFORE), I would be really happy
Thank you all in advance.
public function loadMore(Request $request) {
$posts = Post::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('publicacao', 'desc')
->limit($request->input('limit'))
->skip($request->input('skip'))
->get();
return json_decode($posts);
}
But you can just use the next page from pagination()
So, after a little while I came up with a fix for my needs.
Turns out I didn't need to json_encode() or json_decode() anything.
First of all, I'll use a pseudo mark-up for everything inside blades. It'll be easy to understand, since what I am using is HTML. For jQuery, someone involved with the project came up with a pseudo-jQuery-like functions that emulate its syntax. It is a straight forward syntax, easy to understand, nothing out of the ordinary.
Then, here it is.
PostsController
public function loadMore(Request $request) {
$limit = $request->input('limit');
$skip = $request->input('skip');
// as #Dry7 suggested, I am taking a dynamic skip
$posts = Post::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('published', 'desc')
->limit($limit)
->skip($skip)
->get();
return view(
'portal.pages.blogPost',
compact(
'posts'
)
)->render(); // here is the difference
}
So, what I did is pre-render the view where the posts will be printed WITHOUT loading a new page.
Before we continue, here is the structure of the blog.(Using pseudo-markup, as stated before)
main page
#extends('layouts.layout')
div class=container
div class=blog
h1
Page title
/h1
div class=blog-body
#include('portal.pages.blogPost')
a id=loadMorePosts class=none
Load More
/a
/div
div class=sidebar
#include('portal.components.panel')
/div
/div
/div
Then in my pages.blogPost I have the same code I posted in my question (The code is the one with the foreach loop).
After this, I came up with this pseudo-jQuery-like code.
// I'll start listening to any 'click' in the element I am passing the event
// then I'll increment the number of clicks in the element
var click = 0;
// this is the first skip number
var startCounting = 6;
// start a click event in the <a #loadMorePosts> element
$.event('#loadMorePosts','click',function () {
// increment the number of clicks
click++;
// set my skip that will be sent to server and
// applied in my PostsController
skip = startCounting * click;
// open an ajax request passing the route I set up
// that calls PostsController#loadMore method
HttpRequest.get('{{ route('loadPosts') }}?limit=6&skip=' + skip,function (res) {
// I am concatenating my skip var here, so It'll be sent to server
// checking if I have an empty data
if(res.data != "") {
// not empty, so I'll append it to my div with blog class
// first finding the element, searching for its class
// then passing the data to be appended
$.append('.blog',res.data);
} else {
// the data is empty, so first I'll search for
// the element with class=none
// clean up any innerHtml it has
// then set up a new innerHtml in it
$.replaceAll('.none',"No More Posts");
// to finish it all up, I style the same element with some suggesting colors and actions
$.css('.none', 'pointer-events: none; background-color: lightgrey;');
}
});
});
And its done. The posts are appended, the skip is working, so I don't take repeated posts, it works until all of my posts are loaded and when there are no more posts to show, the button is disabled, stopping any new request to be sent to server.
I hope that with these comments the process made to implement this functionality is clear and you can apply the same steps with whatever framework or library you are using.
Thank you all for reading and for taking time to answer my question.

Unset session deletes all saved items

Reading a lot about sessions and how to add and remove items. I see there are a lot of different way's to get this done. I'm having the same problem.
I hope someone here could help me out with this one. I've come along way from nothing to where I am now with the help of this community (thanks!).
This is my problem, I hope I can explain it so you can understand.
Let say I have a shop that sells t-shirts and pants. When I save a t-hirt item to my cart it display's it like it should. But I'm not able to remove the item. If I then filter my page for pans (this triggers an AJAX event) and try to remove the t-shirt, the item is removed. But ALL items are removed not just the one I clicked on to remove.
Here is my HTML to add a product:
<button type="submit"
class="btn btn-primary text-right add-product showtoast"
data-target="post-<?php the_ID(); ?>"
data-attribute="<?php the_title(); ?>"
data-product="<?php the_title(); ?>">
Add to cart
</button>
And this is the HTML to remove an item:
<div class="echo-product">
<?php echo htmlspecialchars($product); ?>
<input type="submit" class="delete-product" value="Remove">
</div>
These are my Javascripts to add and remove items:
$('.add-product').click(function() {
var productName = $(this).data('product');
$.post('http://example.com/reload.php?addparam',
{productName: productName}, function(data) {
$('.txtHint').html(data);
})
});
$('.delete-product').click(function() {
var productName = $(this).data('product');
$.post('http://examples.com/reload.php?delparam',
{productName: productName}, function(data) {
$('.txtHint').html(data);
})
});
And last but not least, my reload.php script:
<?php
session_start();
if (!array_key_exists('products', $_SESSION) || !is_array($_SESSION['products'])) {
$_SESSION['products'] = [];
}
$productName = array_key_exists('productName', $_POST) ? (string) $_POST['productName'] : '';
if(isset($_GET['delparam'])){
unset($_SESSION['products'][$productName]);
}
if(isset($_GET['addparam'])){
$_SESSION['products'][] = $productName;
}
?>
<?php foreach ($_SESSION['products'] as $product): ?>
<div class="echo-product">
<i style="color:#F60;padding-right:20px;" class="fa fa-anchor" aria-hidden="true"></i>
<?php echo htmlspecialchars($product); ?>
<input type="submit" class="delete-product" value="Remove">
</div>
<?php endforeach;?>
Is there anyone that could help me understand why (it looks like) an AJAX event has to take place before I'm able to remove items. And why it removes ALL the items when I just click on one item?
Thanks!
Compare how products are added vs. how they are deleted
if(isset($_GET['delparam'])){
unset($_SESSION['products'][$productName]);
}
if(isset($_GET['addparam'])){
$_SESSION['products'][] = $productName;
}
When adding a product, you just append the productName to an array. The entry receives an numeric index.
When deleting a product you look for the productName as index - but that does not exist!
I'm sure it doesn't answer why all products are removed, but it might help to do things right.

Update documents based on multiple dropdown values

I am building an administration panel within a meteor app to be able to manage the users of the app. Currently I am displaying all the users with a dropdown for each user to change their role .
I need to update the changed roles only after I hit some confirmation button at the end.
I have a template that gets rendered with the username and a dropdown with the current role of the user, my template is similar to this:
<div class="ui selection dropdown">
<input type="hidden" name="role">
<i class="dropdown icon"></i>
<div class="default text">{{role}}</div>
<div class="menu">
<div class="item" data-value="admin">Admin</div>
.
.
.
<div class="item" data-value="user">User</div>
</div>
</div>
This renders as many as users I have in my database. I need to update all users who have their role changed after clicking a button.
My current approach is to plug the user id to the dropdown:
<div class="ui selection dropdown" id={{_id}}>
Then I have an event handler for the dropdown change and catch the value:
Template.templateName.events({
"change .ui.selection.dropdown": function(event, template) {
var id = template.find(".ui.selection.dropdown").id;
var role = template.find("input[name=role]").value;
...
},
});
Now I am wondering if I should push those id,role pairs in some session key and update the users after clicking save button or there is a better and more effective alternatives for this ?
Instead of saving them in session variable and clearing session variable on template's destroyed callback, you can add an attribute to the changed element indicating that the element has changed. When save button is clicked, you can use attribute selector to get all the elements that were changed by this user. See the code sample below for further reference.
Template.templateName.events({
"change .ui.selection.dropdown": function(event, template) {
var target = $(event.target);
target.attr("data-changed", "data-changed");
},
"click #saveButtonId": function(event, template) {
var changedElements = template.$(".ui.selection.dropdown[data-changed]");
var changedItems = [];
for (var i = 0; i < changedElements.length; i++) {
var changedElement = changedElements[i];
changedItems.push({ id: changedElement.id, value: changedElement.value });
// or
//changedItems.push([changedElement.id, changedElement.value]);
}
// here you can call a meteor method like
// Meteor.call('updateUserRoles', changedItems); //this updateUserRoles will now get only changed items that you can update in your DB.
}
});

Sending and deleting records to database with a drag and drop table

I have three db tables.
-Paid
-Partially Paid
-Owes
When someone registers for an account I send their user_id, name, etc to my 'Owes' db table and then output their name into a drag and drop table I have in the 'Owes' column. As of now if I move anyone's name to any other category (Paid/Partially paid) I am not sure how to delete that record from the Owes db and insert the name into the new db table so the changes are permanent.
What's really throwing me off is how to do it with the drag and drop table. I'm not sure how to apply the logic that when something is dropped into that column that the past record is deleted and a new one is added to that specific table or how to make the changes without a submit button or page reload.
What is a way I can do this and how could I structure it?
PHP
<?php
//Payment Section
$con = mysqli_connect("localhost", "root", "", "db");
$paid_run = mysqli_query($con,"SELECT * FROM paid ORDER BY id DESC");
$partially_paid_run = mysqli_query($con,"SELECT * FROM partial_payment ORDER BY id DESC");
$owes_run = mysqli_query($con,"SELECT * FROM owes ORDER BY id DESC");
$paid_numrows = mysqli_num_rows($paid_run);
$partially_paid_numrows = mysqli_num_rows($partially_paid_run);
$owes_numrows = mysqli_num_rows($owes_run);
if($paid_numrows > 0){
while($row = mysqli_fetch_assoc($paid_run)){
$paid_id = $row['user_id'];
$paid_name = $row['name'];
}
}
if($partially_paid_numrows > 0){
while($row = mysqli_fetch_assoc($partially_paid_run)){
$partially_paid_id = $row['user_id'];
$partially_paid_name = $row['name'];
$partially_paid_amount = $row['payment'];
}
}
if($owes_numrows > 0){
while($row = mysqli_fetch_assoc($owes_run)){
$owes_id = $row['user_id'];
$owes_name = $row['name'];
}
}
?>
$(function() {
$( "#paid, #partially_paid, #owes" ).sortable({
connectWith: ".tdPayment",
remove: function(e, ui) {
var $this = $(this);
var childs = $this.find('div');
if (childs.length === 0) {
$this.text("Nothing");
}
},
receive: function(e, ui) {
$(this).contents().filter(function() {
return this.nodeType == 3; //Node.TEXT_NODE
}).remove();
},
}).disableSelection();
});
Table
<table class="paymentTable" id="dragTable">
<tr>
<th class="thPayment">Paid</th>
<th class="thPayment">Partially Paid</th>
<th class="thPayment">Owes</th>
</tr>
<tr>
<td class="tdPayment" id="paid">
<div>
<?php
if ($paid_name == true) {
echo $paid_name;
} else {
echo "No one has paid";
}
?>
</div>
</td>
<td class="tdPayment" id="partially_paid">
<div>
<?php
if ($partially_paid__name == true) {
echo $partially_paid__name . " - " . $partially_paid_amount;
} else {
echo "No one has made a partial payment";
}
?>
</div>
</td>
<td class="tdPayment" id="owes">
<div>
<?php
if ($owes_name == true) {
echo $owes_name;
} else {
echo "Everyone has paid something";
}
?>
</div>
</td>
</tr>
</table>
Here's a rough outline on how to do it. I would recommend using jQuery, a JavaScript library that has a lot of useful stuff for this.
Implement the drag and drop part using jQuery draggable and droppable. A nice guide can be found here.
Write a PHP page that updates the database the way you want it.
Use jQuery to call that page when something is dropped. This can be done using Ajax.
Clarification of part 3
I recommend looking at the photo manager in the droppable documentation to get a full working example of something similar.
For this to work, you need to set up the HTML so it has some class names and some data attributes:
<h1>Owes</h1>
<div class="bin" data-bin-id="1">
<div class="user" data-user-id="5">Eva</a>
<div class="user" data-user-id="8">Anna</a>
</div>
<h1>Partially paid</h1>
<div class="bin" data-bin-id="2">
<div class="user" data-user-id="2">Tom</a>
...
</div>
...
Then we need to implement some javascript that takes care of calling the PHP when the user drops something:
jQuery(".bin").droppable({
accept: ".user",
drop: function( event, ui ) {
//Get the ID of the bin the user was dropped into.
var intBinID = jQuery(this).attr("data-bin-id");
//Get the ID of the user that was dropped.
var intUserID = ui.droppable.attr("data-user-id");
//Make an ajax call to the PHP page.
jQuery.ajax("update.php?binid=" + intBinID + "&userid=" + intUserID);
}
});
In addition you might want update.php to return something to let the JavaScript know if it worked or not, and if it failed abort the drop.
Disclaimar: Since I don't have all the parts of this project set up, I have not tested this code.
An easier way to do this, is by creating an independent User table where you keep all the users. Then, in a separate table like the ones you have already, you just keep the Id of that user (and maybe some information related to how much he owes or has already payed).
This way, when you have to insert or to update any information about the state of the user, you just have to insert/delete the Id of the User from these tables.
The information about the user will always be safe and kept in it's own table.

Categories

Resources