Id like to begin by saying that this shopping cart is not created by me, however it is changed in the way that I need and I understand most of the code and how it works and all the functions and methods are mine. The things I dont understand I hope to learn with you guys. This is going to be a long and in-depth post but I hope you will be as much interested in teaching me as I am in learning from you.
Background:
I have an e-commerce project in which I need to create a shopping cart for the products to be stored in. The process goes like this: indexpage(with all the products)->detailspage(details of the selected product)->summary(The shopping cart with selected products).
The questions:
I have a few questions commented in my code that are just simple questions regarding the shopping cart that I didnt create. I hope you can help me understand them.
The problem:
There are a few problems with the code, but I probably can figure out everything once the biggest problem is fixed:
When I add my first product to the cart it works like a charm, I am able to display the prices, discounts and everything I need and I even can delete it. After adding the second product the loop doubles. That means that instead of Product1, Product2 I am getting Product1, Product2, Product1, Product2. The same goes with 3 products and etc. This is the biggest problem. Theres one more strange thing. Doing dump on both cart and products(you will learn about them later) I am getting only 2 values in arrays the way it supposed to. That means the problem is somewhere in the twig?
Now with the code. This is the controller of my indexpage. In here i simple fetch all the products and get my cart with session:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll(); // I get the products
$session = $this->getRequest()->getSession();
if (!$session->get('cart')) {
$session->set('cart', array()); // QUESTION#1 : I am checking if the cart is empty? If ith empty I am making it empty again?
}
return $this->render('MpShopBundle:Frontend:index.html.twig', array(
'products'=>$products
));
}
The second controller is for my product details page. This is were the fun starts:
public function viewAction($id)
{
// fetch the cart
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('MpShopBundle:Product')->find($id); // I am getting the product based on the id.
$session = $this->getRequest()->getSession();
$cart = $session->get('cart', array()); // I am getting the cart
// check if the $id already exists in it.
/**if ( $product == NULL ) { // QUESTION#2: I am checking if the
return $this->redirect($this->generateUrl('cart'));
}
else { **/
if( isset($cart[$id]) ) { // Checking if the product is already in the cart. If so, increase the product by 1
$qtyAvailable = $product->getStock(); // I am getting the quantity of that prouct
if( $qtyAvailable >= $cart[$id] = $cart[$id] + 1 ) {
$cart[$id] = $cart[$id] + 1;;
} else {
return $this->redirect($this->generateUrl('cart'));
}
}
/**else {
// if it doesnt make it 1
$cart = $session->get('cart', array()); // IF not just
$cart[$id] = 1;
}
**/
$session->set('cart', $cart);
return $this->render('MpShopBundle:Frontend:product_details.html.twig', array(
'product'=>$product
));
}
The final controller is the summary with all the selected products:
public function summaryAction()
{
$session = $this->getRequest()->getSession();
$cart = $session->get('cart', array());
// fetch the information using query and ids in the cart
if( $cart != '' ) {
$em = $this->getDoctrine()->getEntityManager();
foreach( $cart as $id => $quantity ) { //QUESTION#3 For each product in the cart I am setting a new key and a value?
// and then im creating a new array where I store the products ids?
$productIds[] = $id;
}
if( isset( $productIds ) ) // QUESTION#4 What am I checking here?
{
$em = $this->getDoctrine()->getEntityManager();
$product = $em->getRepository('MpShopBundle:Product')->findById( $productIds ); // I am getting all of the products in the cart by their id?
} else {
return $this->render('MpShopBundle:Frontend:product_summary.html.twig', array(
'empty' => true,
));
} //QUESTION#5 What is going on with these renders?
return $this->render('MpShopBundle:Frontend:product_summary.html.twig', array(
'product' => $product,
));
} else {
return $this->render('MpShopBundle:Frontend:product_summary.html.twig', array(
'empty' => true,
));
}
}
Finally this is how I try to display the cart in my twig:
UPDATE
I found the problem to the item doubling however I dont know how to fix it. So the problem is in my twig. If i remove the loop {{ for key in cart }}
, it fixes the doubling, so this loop is bad. However I need that loop to be able to remove items from cart... Maybe there is some other way??:
{% if product is defined %}
{% for key in cart %} /// this loop causes the problem.
{% for info in product %}
<tr>
<td> <img width="60" src="" alt=""/></td>
<td>{{ info.model }}</td>
<td>
<div class="input-append"><input class="span1" style="max-width:34px" placeholder="1" id="appendedInputButtons" size="16" type="text">
<button class="btn" type="button"><i class="icon-minus"></i></button>
<button class="btn" type="button"><i class="icon-plus"></i></button>
<button class="btn btn-danger" type="button"><a href="{{ path('cart_remove', {'id': key}) }}"><i class="icon-remove icon-white"></i></button>
</div>
</td>
<td>{{ info.price }}</td>
<td>{{ info.discount }}</td>
<td>{{ info.value }}</td>
<td>{{ info.getFinal }}</td>
</tr>
{% endfor %}
{% endfor %}
{% endif %}
Cart dump:
array:3 [▼
12 => 3
10 => 1
11 => 1
]
Product dump:
array:3 [▼
0 => Product {#668 ▼
-id: 10
-model: "Test1"
}
1 => Product {#757 ▼
-id: 11
-model: "Test2"
}
2 => Product {#858 ▼
-id: 12
-model: "Test3"
}
]
I would like to give my suggestion / opinion on some areas in your code. This might help you to further investigate the cause of your issue (duplication) and hopefully expand your level of understanding about Symfony 2.
Questions 1 : Yes, you are setting the session variable cart
to empty by calling $session->set('cart', array())
. (Actually it will remove the cart
session variable from the Session storage)
Question 2 : Yes, you are checking whether entity $product
returned any result or not. if ( $product == NULL ) {
. (I'd use === instead of ==) . Also you can simply do like this if (!$product) {
and it's valid.
Not a Question : I'd alter this if( $cart != '' ) {
with if($session->has('cart') && count($session->get('cart')) > 0 ) {
inside your function summaryAction()
.
Just an advice from the experience. If you are performing an operation like this $cart[$id] = $cart[$id] + 1
, do not fill the $cart
array with the id
of the Product
table. Because when you are accessing products from the Product
table with the values filled in the $cart
array, you will experience unexpected situations such as Product not found. Simply because you are increasing the id by 1 in your code and that value might not exist in the table. (Hope I am not missing anything here. The reason I assumed like this was you have been using the same $id
to access a record from the Product
entity as well to fill up the cart
session variable)
Question 3 : This situation perfectly matches with my above explanation. In here you are iterating through the $cart
array assuming that values inside the $cart
array reflects the id's of the product
table which might not. Otherwise, there's no issue with the iteration.
foreach( $cart as $id => $quantity ) { //QUESTION#3 For each product in the cart I am setting a new key and a value?
// and then im creating a new array where I store the products ids?
$productIds[] = $id;
}
Question 4 : In here, if( isset( $productIds ) )
you check whether $productIds
variable is set and is not NULL. Since you are only setting the $productIds
variable (which is an array) only if the there are ids stored in the cart
session variable, this ($productIds
) will not get declared / set if the cart
session variable is empty. (http://php.net/manual/en/function.isset.php)
Question 5 : In here,
return $this->render('MpShopBundle:Frontend:product_summary.html.twig', array( 'product' => $product, ));
what you do is return a twig to the response, so it can render what's in the twig file. product
is the twig variable which expects at the twig file (MpShopBundle:Frontend:product_summary.html.twig
). So, in this case, it uses this to iterate through the products, as you might have understood by now.
I hope this will help you at least up to some extent. I'd rather prefer if any other Symfony 2 developer can further enhance my reply if necessary. Many thanks. Cheers!!!
Update : I think I found the issue here. In the twig you are having a nested for loop. First it will iterate through the whole items in cart
session and for each iteration (item in cart) it iterates what's in the product
which will cause the duplication. So what you can do is a simple check like this, before you peform the operation.
{% for key in cart %}
{% for info in product %}
{% if key == info.id %}
// do your bits here
{% endif %}
{% endfor %}
{% endfor %}
Hope this helps. Cheers!