For Magento 1.9 I'm working on a module where I defined a custom REST route to retrieve all categories with subcategories. When I call <MAGE>/api/rest/eoarestapi/categories?type=rest the function _retrieveCollection() from the class Namespace_Restapi_Model_Api2_Category_Rest_Guest_V1 is being called. So far so good.
Now I am having the problem, that it returns the response in XML only and when I set the header explicitly to Accept: application/json, then I get the error 406 Not Acceptable An appropriate representation of the requested resource /api/rest/products could not be found on this server. Available variants: api.php , type application/x-httpd-php
This seems very strange to me as I can remember having worked with JSON response back in Magento 1.8.
As a fix I found this and that solution to retrieve JSON which works, but it doesn't seem to be a nice solution as it looks like it disables XML response completely.
Is there a better way to enable JSON output from the REST API in Magento 1.9?
Does anybody have some background knowledge on this?
First I defined #return mixed in my API interface
and than in your model I do this
$response = ['success' => 'ok', 'message' => 'json format'];
header('Content-Type: application/json');
echo json_encode($response); exit;
I achieved this by overriding my request model. My Steps are following:
1:Declare new module : Create app/etc/modules/Custom_RestJson.xml
<?xml version="1.0"?>
<config>
<modules>
<Custom_RestJson>
<active>true</active>
<codePool>local</codePool>
</Custom_RestJson>
</modules>
</config>
2. Create /app/code/local/Custom/RestJson/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Custom_RestJson>
<version>1.0.0</version>
</Custom_RestJson>
</modules>
<global>
<models>
<Custom_RestJson>
<class>Custom_RestJson_Model</class>
</Custom_RestJson>
<api2>
<rewrite>
<request>Core_Mage_Api2_Model_Request</request>
</rewrite>
</api2>
</models>
</global>
</config>
3. Create /app/code/local/Custom/RestJson/Model/Api2/Request.php
<?php
class Custom_RestJson_Model_Api2_Request extends Mage_Api2_Model_Request{
public function getAcceptTypes()
{
$qualityToTypes = array();
$orderedTypes = array();
foreach (preg_split('/,\s*/', $this->getHeader('Accept')) as $definition) {
$typeWithQ = explode(';', $definition);
$mimeType = trim(array_shift($typeWithQ));
// check MIME type validity
if (!preg_match('~^([0-9a-z*+\-]+)(?:/([0-9a-z*+\-\.]+))?$~i', $mimeType)) {
continue;
}
$quality = '1.0'; // default value for quality
if ($typeWithQ) {
$qAndValue = explode('=', $typeWithQ[0]);
if (2 == count($qAndValue)) {
$quality = $qAndValue[1];
}
}
$qualityToTypes[$quality][$mimeType] = true;
}
krsort($qualityToTypes);
foreach ($qualityToTypes as $typeList) {
$orderedTypes += $typeList;
}
return array_keys(array("application/json" => 1));
}
}
Related
I am overriding the Mage_Core_Model_Encryption in my module to override hash, getHash, and validateHash methods.
Since urls in the admin side of things use hashing as well, I am overriding the Mage_Adminhtml_Model_Url getSecretKey method so that it will use (faster) md5 hashing in the urls.
The problem I have noticed is that when I clear my cache (config only is enabled) and load the admin/index my encyptor is not registered. It takes until the third request for it to be seen.
I have debugging statements in Mycompany_Encryption_Adminhtml_Url which show that it is immediately loaded with the module. After clearing my cache, I can see my debug statements are working.
When I use the following statement:
var_dump('Class name: '.get_class(Mage::helper('core')->getEncryptor() ) );
I see returned to admin/index (upon refreshing the page) that the class name is Mage_Core_Model_Encryption, then on the third refresh it shows me my class, Mycompany_Encryption_Model_Encryption.
Why does this take three requests? I have been searching all over and haven't found/figured out the problem yet. By all appearances, I have this configured correctly ( and have experimented with a bunch of alternatives).
Can anyone help me solve this issue?
(Below are config, Url class, and Encryption class snippets). Big thanks in advance for any help.
Update, am using modman, here is my modman too:
# Modman file allows Modman to generate links in Magento.
code app/code/local/Mycompany/Encryption/
Mycompany_Encryption.xml app/etc/modules/Mycompany_Encryption.xml
Here is my config:
<config>
<modules>
<Mycompany_Encryption>
<version>0.1</version>
<depends>
<Mage_Core />
</depends>
</Mycompany_Encryption>
</modules>
<phpunit>
<suite>
<modules>
<Mycompany_Encryption />
</modules>
</suite>
</phpunit>
<global>
<helpers>
<core>
<encryption_model>
<class>Mycompany_Encryption_Model_Encryption</class>
</encryption_model>
</core>
</helpers>
<models>
<mycompany_encryption>
<class>Mycompany_Encryption_Model</class>
</mycompany_encryption>
<core>
<rewrite>
<encryption>Mycompany_Encryption_Model_Encryption</encryption>
</rewrite>
</core>
<adminhtml>
<rewrite>
<url>Mycompany_Encryption_Model_Adminhtml_Url</url>
</rewrite>
</adminhtml>
</models>
</global>
</config>
Url class:
class Mycompany_Encryption_Model_Adminhtml_Url extends Mage_Adminhtml_Model_Url
{
/**
* Generate secret key for controller and action based on form key
*
* #param string $controller Controller name
* #param string $action Action name
* #return string
*/
public function getSecretKey($controller = null, $action = null)
{
$salt = Mage::getSingleton('core/session')->getFormKey();
$p = explode('/', trim($this->getRequest()->getOriginalPathInfo(), '/'));
if (!$controller) {
var_dump('Not Controller');
$controller = !empty($p[1]) ? $p[1] : $this->getRequest()->getControllerName();
}
if (!$action) {
var_dump('Not Action');
$action = !empty($p[2]) ? $p[2] : $this->getRequest()->getActionName();
}
$secret = $controller . $action . $salt;
// Here are my debug statements showing when class/method become available.
var_dump('Method exists: '.method_exists(Mage::helper('core')->getEncryptor(), 'urlHash'));
var_dump('Class name: '.get_class(Mage::helper('core')->getEncryptor() ) );
/* This is what I want to return - but sends error 500 until instantiated. */
//return Mage::helper('core')->getEncryptor()->urlHash($secret);
return false;
}
}
Encryption class:
class Mycompany_Encryption_Model_Encryption extends Mage_Core_Model_Encryption
{
public function hash($data)
{
return password_hash($data, PASSWORD_BCRYPT);
}
public function validateHash($password, $hash)
{
return password_verify($password, $hash);
}
public function getHash($value)
{
return $this->hash($value);
}
public function urlHash($value)
{
return md5($value);
}
}
Using the n98-magerun tool, I was able to config:dump and search for the global/helpers/core node.
With the above configuration, I was getting this badly formed config dump:
$ n98-magerun config:dump |cat |grep -A1 -B2 encryption_model
<helpers>
<core>
<encryption_model>Mage_Core_Model_Encryption<class>Mycompany_Encryption_Model_Encryption</class></encryption_model>
</core>
It appears to clear up if I remove the <class> tags in my config.xml:
<helpers>
...
<core>
<!-- Class tags were surrounding my encryption_model value before -->
<encryption_model>Mycompany_Encryption_Model_Encryption</encryption_model>
</core>
...
</helpers>
With this change, I clear cache and get a better config dump:
$ n98-magerun cache:clean config
config cache cleaned
$ n98-magerun config:dump |cat |grep -A1 -B2 encryption_model
<helpers>
<core>
<encryption_model>Mycompany_Encryption_Model_Encryption</encryption_model>
</core>
Please comment with a doc link or provide info if you know why this was happening! Thanks!
I'm pretty new to Magento, and having problems trying to set a custom shipping method programmatically. I'm converting an xml from a thrid party into an order, and everything else (that I've worked on so far) is working fine. Also I'm having problems spelling "programmatically" but I won't ask for you help with that.
I set up my custom shipping method as follows:
To activate the shipping module in
app/etc/modules/Extension_Shipping.xml
<?xml version=
"1.0"?>
<config>
<modules>
<Extension_Shipping>
<active>true</active>
<codePool>local</codePool>
<depends>
<Mage_Shipping/>
</depends>
</Extension_Shipping>
</modules>
</config>
then to configure it in
app/code/local/Extension/Shipping/etc/config.xml
<?xml version=
"1.0" ?>
<config>
<modules>
<Extension_Shipping>
<version>0.1.0</version>
</Extension_Shipping>
</modules>
<global>
<models>
<extension_shipping>
<class>Extension_Shipping_Model</class>
</extension_shipping>
</models>
</global>
<default>
<carriers>
<extension_myrate>
<active>1</active>
<model>extension_shipping/carrier_myrate</model>
<title>Extension Shipping</title>
<name>Default Rate</name>
</extension_myrate>
</carriers>
</default>
</config>
then to add the class in
app/code/local/Extension/Shipping/Model/Carrier/MyRate.php
<?php
class Extension_Shipping_Model_Carrier_MyRate
extends Mage_Shipping_Model_Carrier_Abstract
implements Mage_Shipping_Model_Carrier_Interface
{
protected $_code = 'extension_myrate';
protected $_isFixed = true;
public function collectRates(Mage_Shipping_Model_Rate_Request
$request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
$result = Mage::getModel('shipping/rate_result');
$method = Mage::getModel('shipping/rate_result_method');
$method->setCarrier('extension_myrate');
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethod('extension_myrate');
$method->setMethodTitle($this->getConfigData('name'));
$method->setPrice(5);
$method->setCost(2);
$result->append($method);
return $result;
}
public function getAllowedMethods()
{
return array('extension_myrate' => $this->getConfigData('name'));
}
}
Basically I followed http://www.magentotricks.com/creating-a-customer-magento-shipping-method/
I thought it all worked fine, as the shipping method now shows up in the checkout screen, and can be set both by customers or in the admin "create new order." However, I'm not able to set it programmatically.
In my controller I'm trying to set the shipping method using the code
$shippingAddress = $quote->getShippingAddress()->addData($addressData);
$shippingAddress->setCollectShippingRates(true)->collectShippingRates()
->setShippingMethod('extension_myrate')
->setPaymentMethod('checkmo');
To add to my confusion, it worked once. but only once out of quite a few test orders. changing the "setShippingMethod" to "freeshipping_freeshipping" or "flatrate_flatrate" makes it update the shipping to those correctly. because of that and because I'm new and still having problems with the file structure, I'm guessing my problem is with setShippingMethod('extension_myrate') but I'm not entirely sure. Any advice would be helpful and appreciated.
I'm trying to create my first Magento extension and I'm stuck at the first step already.
When adding {{block type="rick_printer/print" text="Hello world"}} to a CMS page or block, I want "Hello world" to be displayed.
Unfortunately nothing happens. Here's my code:
app\code\local\Rick\Printer\etc\config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Rick_Printer>
<version>0.0.1</version>
</Rick_Printer>
</modules>
<global>
<blocks>
<rick_printer>
<class>Rick_Printer_Block_Print</class>
</rick_printer>
</blocks>
</global>
</config>
app\etc\modules\Rick_Printer.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Rick_Printer>
<active>true</active>
<codePool>local</codePool>
</Rick_Printer>
</modules>
</config>
app\code\local\Rick\Printer\Block\Print.php
class Rick_Printer_Block_Print extends Mage_Core_Block_Abstract
{
protected function _construct()
{
$this->setTemplate('rick/printer/view.phtml');
parent::_construct();
}
public function printIt()
{
$text = $this->getText();
if (!$text) {
$msg = "Please provide a text!";
echo $msg;
return array();
}
return $text;
}
}
app\design\frontend\default\default\template\rick\printer\print.phtml
<?php
$text = $this->spinIt();
echo $text;
?>
I know the code is ugly and I'm probably doing it all wrong.
Any help is highly appreciated!
Thanks
Update: After applying the fix from Vinai's answer my app\code\local\Rick\Printer\etc\config.xml looks now like this:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Rick_Printer>
<version>0.0.1</version>
</Rick_Printer>
</modules>
<global>
<blocks>
<rick_printer>
<class>Rick_Printer_Block</class>
</rick_printer>
</blocks>
</global>
</config>
and I'm getting the following error message when accessing the CMS page (which doesn't show):
class Rick_Printer_Block_Print extends Mage_Core_Block_Abstract
{
protected function _construct()
{
$this->setTemplate('rick/printer/view.phtml');
parent::_construct();
}
public function printIt()
{
$text = $this->getText();
if (!$text) {
$msg = "Please provide a text!";
echo $msg; return array();
}
return $text;
}
}
Fatal error: Class 'Rick_Printer_Block_Print' not found in /home/www/xyz/htdocs/app/code/core/Mage/Core/Model/Layout.php on line 491
First, your block class prefix is wrong. Despite the node name being <class>, what you are actually specifying is a class prefix.
Another way to look at it is that this node declares the directory, in which the blocks for the module are located.
The correct way is
<!-- language: xml -->
<global>
<blocks>
<rick_printer>
<class>Rick_Printer_Block</class>
</rick_printer>
</blocks>
</global>
Second, in your template you are calling $this->spinIt(); instead of $this->printIt();.
Just a typo...
Otherwise the code looks okay.
UPDATE
The error message indicates a file system path to class name mismatch. Check the casing and for typos.
Also, since you want your block to render a template, you want to extend Rick_Printer_Block_Print from Mage_Core_Block_Template instead of the _Abstract block class.
I have created a module to add a discount code to the customers cart if they are either a new customer or have not ordered in the last 24 hours, it is for a company supplying lunch for their employees and they receive a subsidiary each day to use of £3.00.
So I have:
app/code/local/Brave/Subsidary/controllers/CartControlller.php
<?php
require_once Mage::getModuleDir('controllers', 'Mage_Checkout').DS.'CartController.php';
class Brave_Subsidary_CartController extends Mage_Checkout_CartController {
const DISCOUNTCODE = 'BCOMSTAFF';
public function addAction() {
parent::addAction();
$this->autoApplySubsidaryDiscount();
}
public function indexAction() {
parent::addAction();
$this->autoApplySubsidaryDiscount();
}
public function autoApplySubsidaryDiscount() {
$customer = Mage::getSingleton('customer/session')->getCustomer();
$orders = Mage::getResourceModel('sales/order_collection')
->addFieldToSelect('*')
->addFieldToFilter('customer_id', $customer->getId());
if(!$orders->getSize()) {
// New Customer...
$this->applyDiscountCoupon();
} else {
// Check last order date
$orderCollection = Mage::getModel('sales/order')->getCollection()
->addFilter('customer_id', $customer->getId())
->setOrder('created_at', Varien_Data_Collection_Db::SORT_ORDER_DESC);
$newestOrder = $orderCollection->getFirstItem();
$dateToday = Mage::getModel('core/date')->timestamp(time());
$lastOrderDate = Mage::getModel('core/date')->timestamp(strtotime($newestOrder->getData('created_at')));
// only apply if last order was more than 24hrs ago..
if( $dateToday > $lastOrderDate ) {
$this->applyDiscountCoupon();
}
}
}
public function applyDiscountCoupon() {
Mage::getSingleton("checkout/session")->setData("coupon_code",self::DISCOUNTCODE);
Mage::getSingleton('checkout/cart')->getQuote()->setCouponCode(self::DISCOUNTCODE)->save();
}
}
?>
app/code/local/Brave/Subsidary/etc/config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Brave_Subsidary>
<version>0.1.0</version>
</Brave_Subsidary>
</modules>
<frontend>
<routers>
<checkout>
<use>standard</use>
<args>
<modules>
<Brave_Subsidary before="Mage_Checkout">Brave_Subsidary</Brave_Subsidary>
</modules>
</args>
</checkout>
</routers>
</frontend>
</config>
Then my module file:
app/etc/modules/Brave_Subsidary.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Brave_Subsidary>
<active>true</active>
<codePool>local</codePool>
</Brave_Subsidary>
</modules>
</config>
The problem is that I am getting a redirect loop on the cart page or from adding an item to the cart, this is probably a real simple issue but trying to Google anything related this is a real needle in a haystack!
The error FireFox tells me is:
Firefox has detected that the server is redirecting the request for this address in a way that will never complete.
The problem is:
You are calling parent:addAction() which redirects back to indexAction which is your indexAction which calles parent:addAction again...thus the redirect loop.
Bad code:
public function indexAction() {
parent::addAction();
$this->autoApplySubsidaryDiscount();
}
You must not call addAction in your above seen custom indexAction!
Solution:
Remove
public function indexAction() {
parent::addAction();
$this->autoApplySubsidaryDiscount();
}
Change
public function addAction() {
$this->autoApplySubsidaryDiscount();
parent::addAction();
}
Done.
But better instead rewriting the controller would be to use an observer...but thats another thread i guess :)
Heyo, been working on a magento theme for a little while now (... and my understanding of the system is slowly but steadily growing). I need to start working on some custom modules soon, and so I'm starting with this little project to get my bearings:
I found a little bit of code to manage a specific task that I have been copying and pasting into template files:
<?php
$ids = $_product->getCategoryIds();
$cat = Mage::getModel('catalog/category')->load($ids[0]);
$isFF = false;
foreach ($ids as $key=>$val) {
$cat = Mage::getModel('catalog/category')->load($val);
$name = $cat->getName();
if ( $name === 'Fast Fulfillment' ) {
$isFF = true;
}
}
if ($isFF) { echo '<span class="ff-logo"></span>'; }
?>
Very simple. I'm just checking to see if a product is in a specific category, and producing an element if it is. (I know there are about 5 solid ways of doing this... this is the one I went with).
I need to test this every time a product block is displayed, and have, up till now, been replicating this code to make it work. It does work, but is backasswards (I shouldn't be putting logic into the view layer).
Ok - so, lets make a simple module to share the functionality:
local/WACI/ProductFlag/etc/config.xml
<config>
<modules>
<WACI_ProductFlag>
<version>0.1.0</version>
</WACI_ProductFlag>
</modules>
<global>
<blocks>
<WACI_ProductFlag>
<class>WACI_ProductFlag_Block_View</class>
</WACI_ProductFlag>
</blocks>
</global>
</config>
etc/modules/WACI_All.xml
<config>
<modules>
<WACI_CustomPageLayouts>
<codePool>local</codePool>
<active>true</active>
</WACI_CustomPageLayouts>
</modules>
<modules>
<WACI_ProductFlag>
<codePool>local</codePool>
<active>true</active>
</WACI_ProductFlag>
</modules>
</config>
Now, for the Class ... I'm not really sure if a Block or a Helper is appropriate. I went with Block, but - idk... I'm probably wrong (tutorials for this stuff vary wildly).
local/WACI/ProductFlag/Block/View.php
<?php
/**
* WACI
*
* #codepool Local
* #category View
* #package WACI
* #module ProductFlag
*/
class WACI_ProductFlag_Block_View extends Mage_Core_Block_Template
{
private $_focus;
private $_isFF = false;
public function getIsFF( $product ){
$this->_focus = "FF";
$isFF = false;
$ids = $product->getCategoryIds();
$cat = Mage::getModel('catalog/category')->load($ids[0]);
foreach ($ids as $key=>$val) {
$cat = Mage::getModel('catalog/category')->load($val);
$name = $cat->getName();
if ( $name === 'Fast Fulfillment' ) {
$isFF = true;
}
}
}
protected function _toHtml(){
$html = parent::_toHtml();
if ($this->_focus === "FF") {
if ($this->_isFF){
$html .= '<span class="ff-logo"></span>';
}
}
return $html;
}
}
?>
Hopefully its clear that I only want to output a string based on the input of any given product. Should I be overriding the _toHtml() to deliver the string? Again. Probably not...
in my local.xml I reference the block:
<catalog_product_view>
<reference name="content">
<reference name="product.info">
<block type="WACI/ProductFlag" name="product.flag" as="productFlag" output="toHtml" />...
... I'm not clear if this instantiates this class? I don't think so. I'm not really sure how to address it in my product/view/media.phtml anyway, given that I need to call a method with a parameter.
I don't think I need a template file, given that I'm just outputting a string, but I don't think I've seen block modules without an associated template. Maybe a helper class is appropriate then?
Whatever the case, it ain't workin.
I either get a fatal error saying my media class has no getIsFF() method (not surprising), or just nothing shows up. My config files are correct, but that's about it, I think.
Jebus. I'm all turned around.
Can someone with Mage skillz clarify this simple issue and point me in the right direction?
Cheers!
No advice? Well = I sussed out this slightly modified and working solution:
local/WACI/ProductFlag/etc/config.xml
<config>
<modules>
<WACI_ProductFlag>
<version>0.1.0</version>
</WACI_ProductFlag>
</modules>
<global>
<blocks>
<productflag>
<class>WACI_ProductFlag_Block</class>
</productflag>
</blocks>
<helpers>
<productflag>
<class>WACI_ProductFlag_Helper</class>
</productflag>
</helpers>
</global>
</config>
local/WACI/ProductFlag/Helper/Flag.php
class WACI_ProductFlag_Helper_Flag extends Mage_Core_Helper_Abstract
{
private $_isFF = false;
public function getIsFF( $product ){
$html = '';
$ids = $product->getCategoryIds();
$cat = Mage::getModel('catalog/category')->load($ids[0]);
foreach ($ids as $key=>$val) {
$cat = Mage::getModel('catalog/category')->load($val);
$name = $cat->getName();
if ( $name === 'Fast Fulfillment' ) {
$this->_isFF = true;
}
}
if($this->_isFF) {
$html = '<span class="ff-logo"></span>';
}
return $html;
}
}
and call it up in any template file via the simple:
<?php echo $this->helper('productflag/flag')->getIsFF($_product); ?>
I'm still not sure if this is exactly appropriate to the magento way of doing things - ie the model calls, I think, should be relegated to their own class and dropped in a Model folder.
Whatever the case - and for anyone else trying to figure this stuff out - as I monkeyed around with it I slowly realized the intent of the config.xml file is to add available factory classes to the blocks/helpers/model pools - and that the path is to the containing directory. The helper call in the template file, then, identifies the "short name" of the directory and then the actual class name.
ie - You could have multiple classes of helpers:
<?php echo $this->helper('productflag/class_one')->someMethod($_product); ?>
<?php echo $this->helper('productflag/class_two')->someOtherMethod($_product); ?>
<?php echo $this->helper('productflag/class_three')->yetAnotherMethod($_product); ?>
So... One step closer.