Magento 2 How to set custom data / option in a quote item? - magento

I would like to add some data into the quote item, not product.
my approach now is
$quoteItems = $this->cart->getItems();
foreach ($quoteItems as $eachQuoteItem) {
$eachQuoteItem->setCustomname('aaaa');
$eachQuoteItem->setIsSuperMode(true);
$eachQuoteItem->save();
};
I can use $eachQuoteItem->getCustomname(); to get back 'aaaa' in the same page, but i can not get the data in other request.
any suggestion?
thanks

The answers provided address the task of converting the quote items to order items. But, it sounds like you're asking how to set the data on the quote item in the first place.
You can do this by:
a) get items from quote using getAllVisibleItems(),
b) call setData('field', val) on each item.
c) set updated items on quote using setItems(items)
d) then, save the quote

It's late but possibly you need to create plugin for such case as suggested here.

You can save custom field value in quote_item table by following below,
Create ModuleName/CompanyName/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Item">
<plugin name="to_save_custom_field_to_quote_item" type="ModuleName\CompanyName\Plugin\ToQuoteItem" />
</type>
</config>
Create ModuleName\CompanyName\Plugin\ToQuoteItem.php
<?php
namespace ModuleName\CompanyName\Plugin;
use Magento\Quote\Model\Quote\Item;
class ToQuoteItem
{
public function afterBeforeSave(Item $subject)
{
$subject->setCustomField("YOUR_VALUE");
}
}

Related

How to inject pure javascript into head in Magento 1.9 extension?

I'm developing a Magento extension that deals with analytics. One of the things it needs to do is pass events (such as checkout_cart_add_product_complete and catalog_controller_product_view) from the backend PHP observer class to the frontend javascript.
I have the events working in myextension's config.xml:
<events>
...
<catalog_controller_product_view>
<observers>
<myextension>
<type>model</type>
<class>myextension/observer</class>
<method>productViewContent</method>
</myextension>
</observers>
</catalog_controller_product_view>
<checkout_cart_add_product_complete>
<observers>
<myextension>
<type>model</type>
<class>myextension/observer</class>
<method>productAddToCart</method>
</myextension>
</observers>
</checkout_cart_add_product_complete>
...
</events>
I have the Observer.php model implemented to write to the log currently:
public function productViewContent($observer) {
$product = $observer->getProduct();
Mage::log('('.$product->getId() .') '. $product->getName().' has been viewed.', null, 'product.log');
}
public function productAddToCart($observer) {
$product = $observer->getProduct();
Mage::log('('.$product->getId() .') '. $product->getName().' has been added to cart.', null, 'product.log');
}
and I'm seeing the correct product details in the product.log.
Now where I am stuck is in this class:
class Mynamespace_Myextension_Block_Html_Head extends Mage_Page_Block_Html_Head {
// some session code here
$this->addJs('myextension/file.js');
}
I've successfully detected that myextension/file.js is loading, but I need to be able to pass dynamic event info to the JS file. I was hoping to be able to do something like:
class Mynamespace_Myextension_Block_Html_Head extends Mage_Page_Block_Html_Head {
// some session code here
$this->addRawHTML('<script>var myevent='addtocart';var myproduct=$product_id</script>');
$this->addJs('myextension/file.js');
}
...so that myevent and myproduct are available to the code that runs in file.js. Obviously there is no such method addRawHTML.
Hopefully that makes sense. I tried
echo "<script>alert('add_to_cart');</script>";
in the my extension's Head.php block and I get the alert as expected, but it of course is printed before the first line of the html which is not the correct way to do it.
Also, I am currently using the session to write details about the event from the Observer so that the _prepareLayout method in Mynamespace_Myextension_Block_Html_Head can read them and forward them on to the JS in the layout. Is there a better way?
In a nutshell, I'm not able to modify layout.xml/theme.xml because this is an extension, and I'm not able to use addJs alone because I need the JS file to be dynamically generated or detect JS variables written dynamically into the page.
Is it considered bad practice to call add JS with a PHP file?
$this->addJs('myextension/file.php?myevent=add_to_cart&product_id='.$product_id);
and render the JS file dynamically using PHP? That might work but it seems like a hack.
Thanks for your advice.
UPDATE: I have managed to get something working, but in a "non-magento-way". I followed something like this answer, basically generating Javascript on the fly from the observer directly:
public function generateLayout($observer)
{
// I do this if there's an event in a custom session variable
$layout = $observer->getEvent()->getLayout();
$headBlock = $layout->getBlock('head');
$block = $layout->getBlock('head');
$block->setText("
<script>
var myevent = '$event';
var myproduct = '$product';
</script>
");
$headBlock->append($block);
}
I would still like to approach it properly using Magento best practice.
As you might know, Magento uses a javascript variable var spConfig (in view pages) for storing product data to be used in javascript for changing prices and other thigs.
Can't you use such a js variables that store product/cart info and use them in js file that you are including (that is file.js)

Need to remove the title from a specific magento category page

The problem
I need to remove the category name or title from specific non-product category pages but can't find the reference or block names to remove it with layout updates. I have already found the code that I could override and comment out to remove the title from every category page but that won't work since I need the title on most category pages.
What I'm trying to do
In the past I've been able to turn on the template path hints with block names, spend a second researching and found a great way to remove a block. This is the kind of code I have used before:
<reference name="Mage_Page_Block_Html_Breadcrumbs">
<remove name="breadcrumbs"/>
</reference>
TL;DR
I just need a simple way to remove the category title name from specific categories. If my idea about update xml is junk I'll take any suggestions. Thanks for any help.
I'd start by creating a custom module, overriding the Mage_Catalog_Block_Category_View block and doing something like this:
In app/local/Yournamespace/Titlemodule/etc/config.xml
<config>
<!-- .... -->
<global>
<blocks>
<titlemodule>
<class>Yournamespace_Titlemodule_Block</class>
</titlemodule>
<catalog>
<rewrite>
<product_view>Yournamespace_Titlemodule_Block_Category_View</product_view>
</rewrite>
</catalog>
</blocks>
<!-- ... -->
</config>
and in app/local/Yournamespace/Titlemodule/Block/Category/View.php
class Yournamespace_Titlemodule_Block_Category_View extends Mage_Catalog_Block_Category_View
{
protected function _prepareLayout()
{
parent::_prepareLayout();
$category = $this->getCurrentCategory(); // if needed
$headBlock = $this->getLayout()->getBlock('head');
// custom logic here
$headBlock->setTitle($this->__('New title, or none'));
return $this;
}
}
It will give you plenty of possibilities running custom logic before manipulating any blocks on the page.

what's the function of $this->getCanViewOrder() in success.phtml in magento

I am trying to figure out what the following function is checking:
<?php if ($this->getCanViewOrder() && $this->getCanPrintOrder()) :?>
<?php echo $this->__('<strong>Click here to print</strong> an invoice or a copy of your order confirmation.', $this->getPrintUrl()) ?>
In the success.phtml file in Magento the "Click here to print" link doesn't show anymore on the thank you page. Where is this function located?
Update: I revised this answer greatly after doing a bit more research.
For the record, it looks like getCanPrintOrder is one of Magento's magic methods for getting object data. You'd set its value with setCanPrintOrder, and if you haven't called that before, getCanPrintOrder will just return null. You could also set it by calling setData('can_print_order').
It looks like the only place it's being set is in the Onepage checkout success block, Mage_Checkout_Block_Onepage_Success, in the _prepareLastOrder method:
protected function _prepareLastOrder()
{
$orderId = Mage::getSingleton('checkout/session')->getLastOrderId();
if ($orderId) {
$order = Mage::getModel('sales/order')->load($orderId);
if ($order->getId()) {
$isVisible = !in_array($order->getState(),
Mage::getSingleton('sales/order_config')->getInvisibleOnFrontStates());
$this->addData(array(
'is_order_visible' => $isVisible,
'view_order_id' => $this->getUrl('sales/order/view/', array('order_id' => $orderId)),
'print_url' => $this->getUrl('sales/order/print', array('order_id'=> $orderId)),
'can_print_order' => $isVisible,
'can_view_order' => Mage::getSingleton('customer/session')->isLoggedIn() && $isVisible,
'order_id' => $order->getIncrementId(),
));
}
}
}
Which is called from the _beforeToHtml method, which would be called when that page is rendered.
Pulling the string a bit further, we see that the can_print_order is determined by the $isVisible variable, and that's set by this line:
$isVisible = !in_array($order->getState(),
Mage::getSingleton('sales/order_config')->getInvisibleOnFrontStates());
It's checking if the order state is one of the states that are visible on front. These are ultimately set in the config.xml file for the core Magento sales module.
<config>
<global>
<sales>
<order>
<states>
<new translate="label">
<label>New</label>
<statuses>
<pending default="1"/>
</statuses>
<visible_on_front>1</visible_on_front>
</new>
...
</states>
</order>
</sales>
</global>
</config>
All of the states are visible_on_front by default, so unless you've changed them, or something has overridden them, that shouldn't be your problem. I'd double-check this by dumping the value of getCanPrintOrder in success.phtml.
A hacky workaround would be overriding the template file and adding
$this->setCanPrintOrder(true);
$this->setCanViewOrder(true);
Anywhere above the if condition. Or just removing the checks altogether.

SimpleXML (Zend_Config_Xml actually) and foreach : which tag am I iterating?

I'm implementing a little event manager in order to use the Observer pattern. To subscribe my observers to my events, I'm using the following xml file :
<?xml version="1.0" encoding="UTF-8"?>
<configData>
<subscriptions>
<subscription>
<eventName>event_name</eventName>
<class>My_Observer_Class</class>
<function>myFunction</function>
</subscription>
<subscription>
<eventName>other_event_name</eventName>
<class>My_Observer_Otherclass</class>
<function>myOtherFunction</function>
</subscription>
</subscriptions>
</configData>
I'm using a foreach to loop on the subscriptions :
foreach($subscriptions->subscription as $subscription) {
/* using $subscription->eventName etc... */
}
And everything is ok, each $subscription item has it's eventName etc...
But here comes my problem :
<?xml version="1.0" encoding="UTF-8"?>
<configData>
<subscriptions>
<subscription>
<eventName>event_name</eventName>
<class>My_Observer_Class</class>
<function>myFunction</function>
</subscription>
</subscriptions>
</configData>
Here I have only one <subscription> node. And my foreach loops on the subscription children !
To solve this problem, I'd like to know how I can check if the xml file contains several <subscription> tags, or just one...
Any help will be appreciated :)
Edit : Is there a way to use xpath with my Zend_Config_Xml object ?
You can use Xpath.
Please try below code, i have tested it with both of sample XML's you provided.
<?php
$subscriptions = simplexml_load_file('test.xml');
$scTag = $subscriptions->xpath("//subscription");
foreach($scTag as $subscription) {
echo $subscription->eventName;
/* using $subscription->eventName etc... */
}
?>
hope this help !
Just to clarify, this is an issue with Zend_Config_XML which is not present in PHP's native SimpleXML.
Given your second example as $xml, I can run the following and get the word 'subscription' as expected:
$configData = simplexml_load_string($xml);
foreach($configData->subscriptions->subscription as $subscription)
{
echo $subscription->getName();
}

Add a date picker to system.xml on custom module

As stated in the subject, I am trying to add a date field with its date picker in the System > Configuration area for a custom module (thus using etc/system.xml).
I tried to get inspiration from the thread below :
Magento - Add a button to system.xml with method attached to it
but no success.
I'm sure this is a question of creating the right block or method to create a custom html field but I cannot read thru the Magento Matrix :)
I am stuck at the step where I need to code the class (Datefield.php):
<?php
class Namespace_Module_Block_Datefield extends Mage_Adminhtml_Block_System_Config_Form_Field {
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
// ----> Am I wrong in calling ..._Abstract? Should I call Varien_Data_Form_Element_Date? I've tried but no success either...
$this->setElement($element);
$html = // ------------------> what to put here? Call a block or some other method?
->setFormat('d-m-Y')
->setLabel($this->__('Choose date'))
->toHtml();
return $html;
}
}
?>
Do you have a trick on how to do that ?
Thanks a lot.
Hervé
EDIT 02/19/2014: added validation
I found what I think is a more elegant way of doing this. Actually, satrun77 methods is ok but we must place a file in Varien/Data/Form/Element/ which can be overwritten if someone else working on the project unluckily uses the same file/class name. Moreover, this method places the file in the module directories which is, I think, better than distributing files all over the directory tree.
In system.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
....
<fields>
...
<run translate="label">
<label>Date</label>
<frontend_type>text</frontend_type> <!-- Use text instead of "myDateSelection" -->
<frontend_model>module/adminhtml_system_config_date</frontend_model> <!-- Call a module specific renderer model -->
<sort_order>20</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<validate>required-entry</validate> <!-- Optional -->
<show_in_store>1</show_in_store>
</run>
</fields>
...
</config>
Create a new file :
app/code/[local, community]/Namespace/Module/Block/Adminhtml/System/Config/Date
with the content below:
class Namespace_Module_Block_Adminhtml_System_Config_Date extends Mage_Adminhtml_Block_System_Config_Form_Field
{
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
{
$date = new Varien_Data_Form_Element_Date;
$format = Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT);
$data = array(
'name' => $element->getName(),
'html_id' => $element->getId(),
'image' => $this->getSkinUrl('images/grid-cal.gif'),
);
$date->setData($data);
$date->setValue($element->getValue(), $format);
$date->setFormat(Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT));
$date->setClass($element->getFieldConfig()->validate->asArray());
$date->setForm($element->getForm());
return $date->getElementHtml();
}
}
Create class file in app/code/local/Varien/Data/Form/Element/. Make sure the file name is prefixed with something that identify your module (this is just to differentiate your custom code from Magneto core files)
class Varien_Data_Form_Element_MyDateSelection extends Varien_Data_Form_Element_Date
{
public function getElementHtml()
{
// define image url
$this->setImage(Mage::getDesign()->getSkinUrl('images/grid-cal.gif'));
// define date format
$this->setFormat('yyyy-MM-dd');
return parent::getElementHtml();
}
}
Inside your module system.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
....
<fields>
...
<run translate="label">
<label>Run now</label>
<frontend_type>myDateSelection</frontend_type>
<sort_order>20</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</run>
</fields>
...
</config>
Placing custom code inside lib/ folder or app/Mage/Core/ folder is not the best way to create custom code for Magento. These folders are for core code and not custom code.
This approach creates the least amount of code and does not change any of the core files. So, there isn't any harm from having extra file inside the lib/ folder. But you need to remember that you have extra file for your module in the lib/.
Hope this helps

Resources