I am trying to store multiple values(products) in my session so that i can display them in a basket. However for some reason it only stores one product..
In the first pages controller I created an array in witch I will store my selected products later:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
$session = $this->getRequest()->getSession();
$item = array();
$session->set('itemCart', $item);
return $this->render('MpShopBundle:Frontend:index.html.twig', array(
'products'=>$products
));
}
Then when a user selects a product a new page opens with the products detail(product is selected by id). Then if the user adds the product to the cart he is taken to the basket page and I save the product in an array with session:
public function viewAction($id)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('MpShopBundle:Product')->find($id);
$session = $this->getRequest()->getSession();
$itemCart = $session->get('itemCart');
array_push($itemCart, $product);
$session->set('itemCart', $itemCart);
return $this->render('MpShopBundle:Frontend:product_details.html.twig', array(
'product'=>$product
));
}
In the basket page I get the products:
public function summaryAction()
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
$session = $this->getRequest()->getSession();
$itemCart = $session->get('itemCart');
return $this->render('MpShopBundle:Frontend:product_summary.html.twig', array(
'products'=>$products,
));
}
Now in my twig I display them like this. First i get the products:{% set items = app.session.get('itemCart') %}. Then i go cycle through them and try to display:
{% for item in items %}
<tr>
<td> <img width="60" src="{{ asset('bundles/mpFrontend/assets/products/4.jpg') }}" alt=""/></td>
<td>{{ item.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"><i class="icon-remove icon-white"></i></button> </div>
</td>
<td>$120.00</td>
<td>$25.00</td>
<td>$15.00</td>
<td>$110.00</td>
</tr>
{% endfor %}
The first product is displayed. When I go back and select another product, the new product overlaps the older one and is the only one shown. Why?
PROBLEM FOUND. HOW To FIX IT?
Ok so I think I found my problem. Im only getting one value because when I add my first product to the basket I go back to my index page to select another product myy indexController $item = array(); part and makes my array empty again. Thats the problem. But if i select the first product and just browse back without reloading the page and add a new product it works. So what can I do with my indexController to make it not empty my cart everytime I go back?
Well, I think the very basic solution can be:
public function indexAction()
{
...
if (!$session->get('itemCart')) {
$session->set('itemCart', array());
}
Then I believe it would be better to abstract managing session data freely in those controllers, maybe better to wrap it in a dedicated service and/or within an event logic, and use that one inside the controllers. This way you can avoid code duplication, you obtain better reusable code and responsibilities in one shot.
-----EDIT2-----
What you're asking now it's a long story and maybe I'm not the best to explain it.
What I meant (was just a thought and not a things that must be done) was:
//IndexAction method
// You could "wrap" this part of the code:
$session = $this->getRequest()->getSession();
$item = array();
$session->set('itemCart', $item);
// with a service (see Symfony DOC) like "CartSession" or
// simply "Cart" and use it there like:
$this->cartSession->init();
// or
$this->cart->init();
// And inside the init method you can use the logic you want;
// Any further changes or if you want to move the session from $_SESSION to another storage mechanism,
// this doesn't impact on your controller code but only inside your service.
and
// viewAction method
// You could "wrap" this part of the code:
$session = $this->getRequest()->getSession();
$itemCart = $session->get('itemCart');
array_push($itemCart, $product);
$session->set('itemCart', $itemCart);
// inside the same service previously defined and used in indexAction
$this->cart->add($itemCart);
However I can suggest to read something about:
Symfony Service Container, to helps you to better understand how to extract code in more appropriate and dedicated wrappers
SOLID principles, to helps you better design your code architecture
I think you should add a new item to the cart array by using
$item[] = $product;
instead of
array_push($itemCart, $product);
From PHP's manual:
Note: If you use array_push() to add one element to the array it's better to use $array[] = because in that way there is no overhead of calling a function.
Note: array_push() will raise a warning if the first argument is not an array. This differs from the $var[] behaviour where a new array is created.
Since your array is null to start, I'm not confident that you receive an array in the viewAction controller. I would change the viewAction to:
$itemCart = $session->get('itemCart');
if(!is_array($itemCart){
$itemCart = array();
}
$itemCart[] = $prodcut;
$session->set('itemCart', $itemCart);
Related
Since 3 weeks i try to learn php with the symfony framework.
I want to build an application with which i can track my expanses.
I made good progress but since 2 days i have a little logic problem so maybe someone can help me here.
I want to make a dashboard.(the main side of the project) There the user can monitor the expenditures.
This works. Now i want also a form at the dashboard, so the user can add new expenditures. I already implement a form but with a extra route. So in my ExpenditureController i have the functions dashboard and the function addExpenditure which generate different twig.html templates.
So the user can monitor his expenditures with ...budgetapp/expenditure/dashboard
and he can add new Expenditure with ...budgetapp/expenditure/addexpenditure
My Dashboard-Function
#[Route('/dashboard/', name: 'dashboard')]
public function dashboard(ExpenditureRepository $ar)
{
$user = $this->getUser();
$expenditures = $ar-> findexpendituresOfUser($user);
return $this->render('expenditure/dashboard.html.twig', [
'expenditures' => $expenditures,
]);
}
The expenditure/dashboard.html.twig shows the Expentiures of the current user in a table
My addExpenditure-Function
public function addExpenditure (ManagerRegistry $doctrine, Request $request){
$em = $doctrine->getManager();
$expenditure = new Expenditure();
$form = $this->createForm(ExpenditureType::class, $Expenditure);
$form->handleRequest($request);
if($form->isSubmitted()){
$em->persist($expenditure);
$em->flush();
}
return $this->render('expenditure/addexpenditure.html.twig', [
'addexpenditureForm' => $form->createView()
]);
}
The expenditure/addexpenditure.html.twig looks like this:
{% block body %}
<div class="container">
{{form(eintragenForm)}}
</div>
{% endblock %}
My problem /mistake in thinking:
How can i implement the form to the dashboard? So of course i can take the code from the addexpenditure function and put it 1:1 in the dashboard-funciton. but i dont think this is the right way? I also tried to including template fragments with the offical Embedding Controllers Documentation of Symfony, but this also dont work.
So someone can help me with a suggestion how you would handle this in your project?
Best regards
Markus
There are two possible solutions: put both view and add logic inside one controller's action, or separate them. Since you probably have some validation, it's reasonable to have both add and view code inside one action (otherwise, you'll have to pass errors via sessions, which is not very pretty). So basically, your dashboard/add action will look like this:
#[Route('/dashboard/', name: 'dashboard')]
public function dashboard(Request $request, ExpenditureRepository $ar, ManagerRegistry $doctrine)
{
$expenditure = new Expenditure();
$form = $this->createForm(ExpenditureType::class, $expenditure);
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $doctrine->getManager();
$em->persist($expenditure);
$em->flush();
return $this->redirectToRoute('dashboard');
}
}
$user = $this->getUser();
$expenditures = $ar-> findexpendituresOfUser($user);
return $this->render(
'expenditure/dashboard.html.twig',
[
'expenditures' => $expenditures,
'addexpenditureForm' => $form->createView(),
]
);
}
What's happening here:
First you check if POST http method was used, if it was - it means
that form has been submitted and you can handle it. There is an alternative solution without checking request method, see comments for more.
If this is a GET request, just show ordinary dashboard
If the form is submitted and it's valid - save data and redirect to
the same page. Otherwise, you'll need to empty all submitted data
from the form, etc. (it's not the right way to do it)
If the form is invalid, do not redirect, just render the page
normally, and all errors will be shown.
Finally, you of course have to render the form on your dashboard.html.twig like you did: {{form(eintragenForm)}}
Good afternoon colleagues ...
I need to be able to update another collection from a custom hook,
The following happens that if I use ItemsService or TableGatewayFactory it uses the acl of the container ... in this case the user will be from a customer role, but this does not have the permission to edit orders, I would like to keep it that way ...
It doesn't matter if I have to update the item with another user using their token, or any other alternative ...
Thanks in advance....
'item.update.transactionlog' => function ($data) { <br>
$container = Application::getInstance()->getContainer(); <br>
$itemsService = new \Directus\Services\ItemsService($container);<br>
$acl = $container->get('acl');<br>
$params = [];<br>
$orden = ['status' => 'paid'];<br>
$orderUpdate = $itemsService->update('orders', $data['orderid'], $orden, $params);<br>
}
Usually on cakephp2 i used to unset form data and everything was ok.
Some times i use redirects to clear it. But i cant do that in this current page.
Anyone has found this issue or a solution for this?
If you are staying on the "add" page after successfully adding a record, for example to allow entry of multiple records more quickly, you'll need to reset the entity after saving. For example, if you're entering Posts, your controller will look something like:
$post = $this->Posts->newEntity();
if ($this->request->is('post')) {
$post = $this->Posts->patchEntity($post, $this->request->data);
if ($this->Posts->save($post)) {
$post = $this->Posts->newEntity(); // <- Reset the entity
}
}
$this->set(compact('post'));
(Error checking, flash messages, etc. all left out for brevity.)
An alternative is to simply redirect to the same page.
I had the problem that not everything was deleted
$contact = new ContactForm();
if ($this->request->is('post')) {
if ($contact->execute($this->request->data)) {
$this->Flash->success(__('Submited.'));
$this->request->data(null);
$contact = new ContactForm();
return $this->redirect('/(Same Page)');// This did the final trick for me
}
}
I have been working with magento cart pro extension. For ajax add to cart functionality, i have followed this following link Excellance Magento Blog
Its working Fine.But i have searched a lot regarding ajax delete item from cart, But google returns me always with 3rd party extension. I can't find tutorial for delete product from cart. So i thought, to take a referrance from one of free 3rd party extension which have an both add to cart and delete from cart functionality. I found this following extension which have above both function and also working rock. Ajax cart pro .
I have checked this extension coding part. But I'm really confused, I can't find which code doing delete functionality. They have overwrite only deleteAction in controller file only, I think.I can't understand even what i'm doing.I just need the guidance to add ajax delete functionality.
If anybody have an idea or found any tutorial of this, please share with me your thoughts friends.
Diving into an Ajaxcart extension, in the controller's action, we could find (yeah, you were right!), the following line is actually removing an item:
$this->_getCart()->removeItem($id)->save();
public function deleteAction() {
$id = (int) $this->getRequest()->getParam('id');
if ($id) {
try {
$this->_getCart()->removeItem($id)
->save();
} catch (Exception $e) {
$_response = Mage::getModel('ajaxcart/response');
$_response->setError(true);
$_response->setMessage($this->__('Cannot remove the item.'));
$_response->send();
Mage::logException($e);
}
}
$_response = Mage::getModel('ajaxcart/response');
$_response->setMessage($this->__('Item was removed.'));
//append updated blocks
$this->getLayout()->getUpdate()->addHandle('ajaxcart');
$this->loadLayout();
$_response->addUpdatedBlocks($_response);
$_response->send();
}
So answering you question "which code doing the delete", it's definitely therein;)
And to understand a whole process, you should have in mind these points:
ajaxcart.js - they're overriding Magento's setLocation function and then doing a ajax call(in ajaxCartSubmit method):
var oldSetLocation = setLocation;
var setLocation = (function() {
return function(url){
if( url.search('checkout/cart/add') != -1 ) {
//its simple/group/downloadable product
ajaxcart.ajaxCartSubmit(url);
to render a button use url helper, like this:
<button type="button" title="Add to Cart" class="button btn-cart"
onclick="setLocation('<?php echo $this->helper('checkout/cart')->getAddUrl($_product, $additional = array('qty' => 1));?>')">
+
</button>
updateBlocks. There are an Observer and Response Models that grabs a list of blocks to update/replace on frontend, renders their contents and put em to json reponse.
$updated_blocks = unserialize(Mage::getStoreConfig('ajaxcart/general/update_blocks'));
// ... for each do a $layout->getBlock('<blockName>')->toHtml() and add to json responce
A blocks definitions taken from a core layout and from additional ajaxcart handle (layout/ajaxcart.xml). (cart_sidebar, top.links & etc.)
you can refer the following code
<?php
$id = '100'; // replace product id with your id
$cartHelper = Mage::helper('checkout/cart');
$items = $cartHelper->getCart()->getItems();
foreach($items as $item):
if($item->getProduct()->getId() == $id):
$itemId = $item->getItemId();
$cartHelper->getCart()->removeItem($itemId)->save();
break;
endif;
endforeach;
?>
I'm currently trying to add a custom option to a specific orderline on add to cart via the following:
public function addToPackageQuote()
{
$cart = Mage::getSingleton("checkout/cart");
$quote = Mage::getSingleton("checkout/session")->getQuote();
$packageId = Mage::getModel('MyTuxedo_OPP/Package')->checkPackageId();
$products = $this->sortArray();
foreach ($products as $productInfo) {
try {
$split = explode(",", $productInfo);
$_product = Mage::getModel('catalog/product')->load($split[0]);
if($_product->isConfigurable()) {
$simpleId = $this->getConfigurableSimple($split[1],$split[3],$split[0]);
} else {
$simpleId = $split[0];
}
$product = Mage::getModel('catalog/product')->load($simpleId);
$options = new Varien_Object(array(
"qty" => 1,
"custom_options" => array(
"package" => $packageId,
"packageName" => Mage::helper('MyTuxedo_OPP')->getPackageName()
)
));
$quote->addProduct($product, $options);
$this->_getSession()->setCartWasUpdated(true);
$quote->save();
} catch (Exception $e) {
echo $e->getMessage();
}
$this->addFreeItems();
}
$cart->save();
unset($_SESSION['products']);
unset($_SESSION['productId']);
$cart->save();
// Let's unset all the package sessions (apart from a few that are needed!).
$this->kill();
}
This method is completely seperate from the generic add to cart handler, and is used purely in a packages system so that it adds simple products exclusively (also breaks down configurables super attribute to find the simple product too).
These simple products have no custom options attached to them in the Magento backend, nor is it a goal to add custom options to the product itself. What I would like to do is attach custom options to the order-line that is then transferred over to the order if a purchase is made. So effectively data that is added at the add to cart method and no where else!
The add to cart method works as expected it's just not including the custom options I am trying to attach. I have also tried defining the options object as simply:
$options = new Varien_Object(array(
"qty" => 1,
"package" => $packageId,
"packageName" => Mage::helper('MyTuxedo_OPP')->getPackageName()
)
The above info, not including qty is not in the orderline object at all, and I can't seem to work out where to move on from here.
Endlessly googling at the moment so some help would be most appreciated!!
I do appreciate I’m instantiating the product model object twice in this, however the plan is to just get it working then optimise! :)
You have to set the custom options for the product before adding it to cart.
$product->setCustomOptions($options);
The in Mage_Sales_Model_Quote::_addCatalogProduct() the custom options will be added to the cart item.
See also here: http://www.magentocommerce.com/boards/viewthread/49659/
By the way: Your code may be pretty slow because you are loading products twice in a foreach loop. You should consider some refactoring by using the product collection instead. Also it looks kind of hackish to directly access the $_SESSION variable here. You could rather use the Checkout Session for that (Mage::getSingleton('checkout/session')).
I have now resolved this, after much headache. You can add a custom option to the cart and not have to instantiate the product object and save a custom option to do this, it can be done via tacking onto an observer, and pulling the quote items.
After tacking onto: sales_quote_add_item
I then used:
public function addCustomData($observer) {
$event = $observer->getEvent();
$quote_item = $event->getQuoteItem();
$quote = $session->getQuote();
$quote_item->addOption(array("product_id" => $quote_item->getProduct()->getId(),
"product" => $quote_item->getProduct(),
"code" => 'PackageId',
"value" => Mage::getModel('MyTuxedo_OPP/Package')->checkPackageId()
));
$quote->save();
}
It is most important to include the product object and id, as the function doesn't use the loaded object for some reason.
You can then get at the object via:
$_item->getOptionByCode('PackageId')->getValue();
Quick piece of handy info, if it dumps a stack trace in front of you it can't find the defined option, lose the getValue() (if using var_dump) function to see if you are getting a null value, otherwise xdebug will give you a ton of hints to get around it.