Magento - check if customer attribute already exists - magento

I have a custom module which is called on the customer_save_observer_executed event and specifically when a customer updates their details in their account page (name, password, email etc) I have added a custom attribute called display_name.
When a customer submits this form i need to check if the display_name currently exists for any other customer. If it doesn't then setDisplayName(...) else do nothing/display error message.
I was hoping to do this with this snippet:
$customer_check = Mage::getModel('customer/customer')
->getCollection()
->addAttributeToSelect('display_name')
->addAttributeToFilter('display_name',$new_name)->load();
if ( is_object($customer_check) && count($customer_check) >= 2) {
// dont allow - duplicate customer displayname
}
else {
// allow update....
}
My current code in Model -> Observer.php
class xxxxxxx_Model_Observer
{
public function xxxxxxxx(Varien_Event_Observer $observer)
{
// to stop event being fired twice
if(Mage::registry('customer_save_observer_executed')){
return $this;
}
$postData = Mage::app()->getRequest()->getPost();
$customer = $observer->getCustomer();
// if updating NOT a new customer
if($customer instanceof Mage_Customer_Model_Customer && !$customer->isObjectNew()) {
// check display name is posted
if(!empty($postData['display_name'])){
$current_name = $customer->getDisplayName();
$new_name = $postData['display_name'];
// duplicate check
$customer_check = Mage::getModel('customer/customer')
->getCollection()
->addAttributeToSelect('display_name')
->addAttributeToFilter('display_name',$new_name)->load();
if ( is_object($customer_check) && count($customer_check) >= 2) {
// dont allow - duplicate customer displayname
}
else {
if( $postData['display_name'] !== $current_name ) {
$customer->setDisplayName($postData['display_name']);
}
}
}
}
Mage::register('customer_save_observer_executed',true);
}
}
but this just updates the display_name even if i deliberately set it to a duplicate of another customers
UPDATE
Looking into this further it looks like the module function itself is not being run or nothing inside it is taking affect, as whatever i put in it doesn't work. It is infact the default behaviour that is setting the displayname not my module. My module is activated and my config file uses the customer_save_commit_after event as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<xxx_xxxxx>
<version>0.0.1</version>
</xxx_xxxxx>
</modules>
<global>
<models>
<xxx_xxxxx>
<class>xxx_xxxxx_Model</class>
</xxx_xxxxx>
</models>
<events>
<customer_save_commit_after>
<observers>
<xxx_xxxxx>
<class>xxx_xxxxx/observer</class>
<method>xxxxxx</method>
<type>singleton</type>
</xxx_xxxxx>
</observers>
</customer_save_commit_after>
</events>
</global>
</config>

This probably is the case because all data from the form submitted is set on the customer model in Magento's Customer module (code taken from Mage_Customer_AccountController)
/**
* Forgot customer account information page
*/
public function editAction()
{
$this->loadLayout();
$this->_initLayoutMessages('customer/session');
$this->_initLayoutMessages('catalog/session');
$block = $this->getLayout()->getBlock('customer_edit');
if ($block) {
$block->setRefererUrl($this->_getRefererUrl());
}
$data = $this->_getSession()->getCustomerFormData(true);
$customer = $this->_getSession()->getCustomer();
if (!empty($data)) {
$customer->addData($data);
}
if ($this->getRequest()->getParam('changepass') == 1) {
$customer->setChangePassword(1);
}
$this->getLayout()->getBlock('head')->setTitle($this->__('Account Information'));
$this->getLayout()->getBlock('messages')->setEscapeMessageFlag(true);
$this->renderLayout();
}
Here addData() is called on the customer model. To prevent this rename your form's input to something temporary, like <input name="display_name_tmp"/>, then in your controller do something like
public function xxxxxxxx(Varien_Event_Observer $observer)
{
// to stop event being fired twice
if(Mage::registry('customer_save_observer_executed')){
return $this;
}
$postData = Mage::app()->getRequest()->getPost();
$customer = $observer->getCustomer();
// if updating NOT a new customer
if($customer instanceof Mage_Customer_Model_Customer &&
!$customer->isObjectNew()) {
// check display name is posted
if(!empty($postData['display_name_tmp'])){
$current_name = $customer->getDisplayName();
$new_name = $postData['display_name_tmp'];
// duplicate check
$customer_check = Mage::getModel('customer/customer')
->getCollection()
->addAttributeToSelect('display_name')
->addAttributeToFilter('display_name',$new_name)->load();
if ( is_object($customer_check) && count($customer_check) >= 2) {
// dont allow - duplicate customer displayname
}
else {
if( $postData['display_name'] !== $current_name ) {
$customer->setDisplayName($postData['display_name_tmp']);
}
}
}
}
Mage::register('customer_save_observer_executed',true);
}

Related

Magento : add the same product to cart with different prices

I've programmaticaly allowed the customer to edit the price of a product.
the problem is when i add product with 20$ and adding again the same product with 30$ in the shopping cart page it displays the product -| qty=2 -| total price=60$
so this is not logic the total price must be 50$ and it should not set qty to 2
i know the problem is in the SKU is there a solution for this i don't wanna modify the SKU?
for more details this is my previous question
So this is doable, but unfortunately there is no way to do it without a pretty significant rewrite. I'm assuming that you have some way to identify the different between the two quote items besides price. If that's the case:
sales/quote_item: representProduct()
the rewrite configuration in your module:
<?xml version="1.0"?>
<config>
<global>
<models>
<sales>
<rewrite>
<!--Your full class name should be specified-->
<quote_item>Namespace_Module_Model_Sales_Quote_Item</quote_item>
</rewrite>
</sales>
</models>
</global>
</config>
the original function:
public function representProduct($product)
{
$itemProduct = $this->getProduct();
if (!$product || $itemProduct->getId() != $product->getId()) {
return false;
}
/**
* Check maybe product is planned to be a child of some quote item - in this case we limit search
* only within same parent item
*/
$stickWithinParent = $product->getStickWithinParent();
if ($stickWithinParent) {
if ($this->getParentItem() !== $stickWithinParent) {
return false;
}
}
// Check options
$itemOptions = $this->getOptionsByCode();
$productOptions = $product->getCustomOptions();
if (!$this->compareOptions($itemOptions, $productOptions)) {
return false;
}
if (!$this->compareOptions($productOptions, $itemOptions)) {
return false;
}
return true;
}
our function:
public function representProduct($product) {
$parentResult = parent::representProduct($product);
//if parent result is already false, we already have a different product, exit.
if ($parentResult === false) {
return $parentResult;
}
$itemProduct = $this->getProduct();
/*
* do our check for 'same product' here.
* Returns true if attribute is the same thus it is hte same product
*/
if ($product->getSomeAttribute() == $itemProduct->getSomeAttribute()) {
return true; //same product
} else {
return false; //different product
}
}
If this function returns true, the products will be combined in the cart as the same item and thus you'll just increase your qty by the new qty amount.
If this function returns false, the products will look different and will appear as different items in the cart.
Add Same product with different "SKU" and u can give different price for those products.
this is the class :
<?php
class WebDirect_CustomPrice_Model_Sales_Quote_Item extends Mage_Sales_Model_Quote_Item
{
public function representProduct($product) {
$parentResult = parent::representProduct($product);
//if parent result is already false, we already have a different product, exit.
if ($parentResult === false) {
return $parentResult;
}
$itemProduct = $this->getProduct();
/*
* do our check for 'same product' here.
* Returns true if attribute is the same thus it is hte same product
*/
if ($product->getPrice() == $itemProduct->getPrice()) {
return true; //same product
} else {
return false; //different product
}
}
}

Disable full page caching for specific block

I'm working with magento EE that has full page caching feature. There is a block that is updated dynamically, but I can't seem to disable its caching.
What I want to achieve ideally: disable caching only for particular block so it would be rendered again each time the page loads.
Things I tried:
Include unsetData to layout file
<action method="unsetData"><key>cache_lifetime</key></action>
<action method="unsetData"><key>cache_tags</key></action>
Set function _saveCache to return false
protected function _saveCache($data, $id, $tags = array(), $lifetime = null) {
return false;
}
Set different values for cache_lifetime
public function __construct()
{
$this->addData(array(
‘cache_lifetime’ => 0,
‘cache_tags’ => array(Mage_Catalog_Model_Product::CACHE_TAG),
));
}
Perhaps I'm missing something in full page caching mechanics?
Well, I found a couple of good posts and implement my caching with etc/cache.xml, that wraps my block with container object.
My cache.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<placeholders>
<namespace_block_unique_node>
<block>module/block_class</block>
<name>name_of_block_in_my_layout</name>
<template>path/to/my/template</template>
<placeholder>UNIQUE_PLACEHOLDER_HERE</placeholder>
<container>Namespace_Module_Model_Caching_Container_BlockName</container>
<cache_lifetime>86400</cache_lifetime>
</namespace_block_unique_node>
</placeholders>
</config>
I used here as block the block that should not be cached, as name name of block in my layout, and as container I've choose my container.
Code for container:
<?php
class Namespace_Module_Model_Caching_Container_BlockName extends Enterprise_PageCache_Model_Container_Abstract
{
protected function _getCacheId()
{
return 'NAMESPACE_MODULE_BLOCKNAME' . $this->_getIdentifier();
}
protected function _getIdentifier()
{
return microtime();
}
protected function _renderBlock()
{
$blockClass = $this->_placeholder->getAttribute('block');
$template = $this->_placeholder->getAttribute('template');
$block = new $blockClass;
$block->setTemplate($template);
$layout = Mage::app()->getLayout();
$block->setLayout($layout);
return $block->toHtml();
}
protected function _saveCache($data, $id, $tags = array(), $lifetime = null) { return false;}
}
Here I putmicrotime() function to identify block, but inside my module I used cookie variables related to my module. I believe that saves redundant reloading of a block when nothing was really changed.
The thing that I didn't found in other tutorials is that I had to create layout variable and assign it to my block, otherwise I was getting only my block instead of whole page.
Here is the solution for disabling FPC for a specific controller (could be extended to specific action as well).
First create an Observer to listen on the controller_action_predispatch event:
public function processPreDispatch(Varien_Event_Observer $observer)
{
$action = $observer->getEvent()->getControllerAction();
// Check to see if $action is a Product controller
if ($action instanceof Mage_Catalog_ProductController)
{
$request = $action->getRequest();
$cache = Mage::app()->getCacheInstance();
// Tell Magento to 'ban' the use of FPC for this request
$cache->banUse('full_page');
}
}
Then add the following to your config.xml file for the module. This goes in the section:
<events>
<controller_action_predispatch>
<observers>
<YOUR_UNIQUE_IDENTIFIER>
<class>YOURMODULE/observer</class>
<method>processPreDispatch</method>
</YOUR_UNIQUE_IDENTIFIER>
</observers>
</controller_action_predispatch>
</events>
Now Magento will serve up your page every time and bypass FPC for the request.
And You can also refer: http://mikebywaters.wordpress.com/2011/12/14/disable-magento-full-page-cache-on-a-per-controller-basis/
cache_lifetime must be set to null to disable cache for this block
public function __construct()
{
$this->addData(array(
‘cache_lifetime’ => null,
));
}

capture Order completion status in observer in magento

Hi I want to catpture the order information when the oder is completed or close.I try different events like (sales_order_place_after) but didn't complete my requirements.I am completing orders form admin side and i create observer for capturing the information like
Xml :
<events>
<sales_order_place_after>
<observers>
<extra_options>
<class>My_Module_Model_Observer</class>
<method>salesConvertQuoteItemToOrderItem</method>
</extra_options>
</observers>
</sales_order_place_after>
</events>
Observer :
public function salesConvertQuoteItemToOrderItem($observer)
{
$order = $observer->getOrder();
$orders = $observer->getEvent()->getOrder();
if($order->getState() == Mage_Sales_Model_Order::STATE_COMPLETE){
echo "<pre>";
print_r($orders);exit;
}
}
Can anyone help ? Thanks in advance
You are doing everything right except you are listening to wrong event. You have to use sales_order_save_after instead.
Using sales_order_save_after is still good, but it just requires you also to check for the state (as Mischa suggests):
public function salesOrderSaveAfter($observer)
{
$order = $observer->getEvent()->getOrder();
if($order->getState() != Mage_Sales_Model_Order::STATE_COMPLETE) {
return $this;
}
if($order->getData('state') == $order->getOrigData('state')) {
return $this;
}
// do your stuff
return $this;
}
This works fine for me.

Overridden Controller in Magento, One Function not working

Im attempting to get my contact form to redirect to the homepage after form submission. I setup my module and can confirm it is working. In the code below, I get the 'Pre-dispatched' and 'Index Action' log messages but do not get the 'Post Action' and as you can expect, it also does not redirect me to the homepage when it is complete. I do receive the contact e-mail properly. Can anyone tell me why the first two functions are working correctly and postAction() is not?
I copied all of the code from the original controller into my controller for troubleshooting purposes. Everything is default except the addition of log messages and the redirects at the bottom.
class MyCompany_Contacts_IndexController extends Mage_Contacts_IndexController
{
const XML_PATH_EMAIL_RECIPIENT = 'contacts/email/recipient_email';
const XML_PATH_EMAIL_SENDER = 'contacts/email/sender_email_identity';
const XML_PATH_EMAIL_TEMPLATE = 'contacts/email/email_template';
const XML_PATH_ENABLED = 'contacts/contacts/enabled';
public function preDispatch()
{
parent::preDispatch();
Mage::log('Pre-dispatched');
if( !Mage::getStoreConfigFlag(self::XML_PATH_ENABLED) ) {
$this->norouteAction();
}
}
public function indexAction()
{
Mage::log('Index Action.');
$this->loadLayout();
$this->getLayout()->getBlock('contactForm')
->setFormAction( Mage::getUrl('*/*/post') );
$this->_initLayoutMessages('customer/session');
$this->_initLayoutMessages('catalog/session');
$this->renderLayout();
}
public function postAction()
{
parent::postAction();
Mage::log('Post Action.');
$post = $this->getRequest()->getPost();
if ( $post ) {
$translate = Mage::getSingleton('core/translate');
/* #var $translate Mage_Core_Model_Translate */
$translate->setTranslateInline(false);
try {
$postObject = new Varien_Object();
$postObject->setData($post);
$error = false;
if (!Zend_Validate::is(trim($post['name']) , 'NotEmpty')) {
$error = true;
}
if (!Zend_Validate::is(trim($post['comment']) , 'NotEmpty')) {
$error = true;
}
if (!Zend_Validate::is(trim($post['email']), 'EmailAddress')) {
$error = true;
}
if (Zend_Validate::is(trim($post['hideit']), 'NotEmpty')) {
$error = true;
}
if ($error) {
throw new Exception();
}
$mailTemplate = Mage::getModel('core/email_template');
/* #var $mailTemplate Mage_Core_Model_Email_Template */
$mailTemplate->setDesignConfig(array('area' => 'frontend'))
->setReplyTo($post['email'])
->sendTransactional(
Mage::getStoreConfig(self::XML_PATH_EMAIL_TEMPLATE),
Mage::getStoreConfig(self::XML_PATH_EMAIL_SENDER),
Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT),
null,
array('data' => $postObject)
);
if (!$mailTemplate->getSentSuccess()) {
throw new Exception();
}
$translate->setTranslateInline(true);
// Mage::getSingleton('customer/session')->addSuccess(Mage::helper('contacts')->__('Your inquiry was submitted and will be responded to as soon as possible. Thank you for contacting us.'));
$this->_redirect('');
return;
} catch (Exception $e) {
$translate->setTranslateInline(true);
// Mage::getSingleton('customer/session')->addError(Mage::helper('contacts')->__('Unable to submit your request. Please, try again later'));
$this->_redirect('');
return;
}
} else {
$this->_redirect('');
}
}
}
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<MyCompany_Contacts>
<version>0.0.1</version>
</MyCompany_Contacts>
</modules>
<frontend>
<routers>
<contacts>
<args>
<modules>
<MyCompany_Contacts before="Mage_Contacts">MyCompany_Contacts</MyCompany_Contacts>
</modules>
</args>
</contacts>
</routers>
</frontend>
</config>
The problem lies in the parent::postAction(); part within your custom postAction. What you are doing now is posting a form to /post. It does end up in your postAction, but then is directly routed through the parent::postAction().
The parent method, thus Mage_Contacts_IndexController::postAction(), also contains the logic to send an email . Hence you are receiving one. The problem is that at the end of the parent method there is still the redirect $this->_redirect('*/*/');. This prevents the code from reaching your `Mage::log('Post Action.') and the rest of your custom code.
Solution: remove the parent::postAction() and your custom code in the postAction method will be executed and eventually your own redirect to the homepage will be ran.
I think I figured this out. I remmebered that the post data gets analyzed by Akismet before it gets sent so its entirely possible the default Mage_Contacts is already getting extended and is going trough that module first. Added the logging to postAction() in the Akismet controller and that verified it. Thanks for setting me on the right track.

Magento Payment Redirect Order

Is there any functionality in magento payment extention to stop the creation of an order before the success status from payment gateway is returned?
The Extension is designed as a payment redirect (with getOrderPlaceRedirectUrl) but really in the correct controller action, I do post from the action with params, to the gateway and return success if all OK, and return failure if not.
But the order is already created so I must cancel this order, but it should not create this order in the first place.
Maybe if I can design it as a gateway, I can use some payment method to do this?
I thought about the validate or prepareSave methods, but both of them are called twice - on accept payment method and on place order.
Also I thought about events - maybe I can use some event to do this post action and on failture just throw exception?
But I really think that in the payment methods, there has to be some standard functionality that I can use.
This is quite a common issue during Payment Module development.
Magento offers two hooks for payment method classes to provide redirect URL's, one before the order is created, one after.
If a payment method model implements getOrderPlaceRedirectUrl() the customer will be redirected after the confirmation step of the one page checkout, the order entity will be created.
If a payment method model implements the getCheckoutRedirectUrl() method, the customer will be redirected after the payment step of the one page checkout, and no order entity is created.
This is not ideal, but thats what Magento offers out of the box.
How about extending the _validate() method on Mage_Sales_Model_Service_Quote, and throw an error there so that it never gets to the "$transaction->save();" bit.
public function submitOrder()
{
$this->_deleteNominalItems();
// do some check here
$this->_validate();
// End checks
$quote = $this->_quote;
$isVirtual = $quote->isVirtual();
........
try {
$transaction->save();
$this->_inactivateQuote();
Mage::dispatchEvent('sales_model_service_quote_submit_success', array('order'=>$order, 'quote'=>$quote));
} catch (Exception $e) {
...........
}
...........
return $order;
}
Validate function looks like this:
protected function _validate()
{
$helper = Mage::helper('sales');
if (!$this->getQuote()->isVirtual()) {
$address = $this->getQuote()->getShippingAddress();
$addressValidation = $address->validate();
if ($addressValidation !== true) {
Mage::throwException(
$helper->__('Please check shipping address information. %s', implode(' ', $addressValidation))
);
}
$method= $address->getShippingMethod();
$rate = $address->getShippingRateByCode($method);
if (!$this->getQuote()->isVirtual() && (!$method || !$rate)) {
Mage::throwException($helper->__('Please specify a shipping method.'));
}
}
$addressValidation = $this->getQuote()->getBillingAddress()->validate();
if ($addressValidation !== true) {
Mage::throwException(
$helper->__('Please check billing address information. %s', implode(' ', $addressValidation))
);
}
if (!($this->getQuote()->getPayment()->getMethod())) {
Mage::throwException($helper->__('Please select a valid payment method.'));
}
return $this;
}
The extended function can look like this:
public function __construct(Mage_Sales_Model_Quote $quote)
{
$this->_quote = $quote;
parent::__construct($quote);
}
protected function _validate()
{
// Code to test comes here
Mage::throwException(Mage::helper('payment')->__('unsuccessfull.....'));
// Code ends, now call parent
return parent::_validate();
}
As I said - giving sample that I used for this solution at final.
I prefered to observe event to do post request. Really if you'll use method presented here
you will take the same effect, but I prefer to use event observer. So:
First add some data to config.xml to create event observer in frontend section
<events>
<sales_model_service_quote_submit_before>
<observers>
<lacpaycs>
<type>singleton</type>
<class>OS_LacPayCS_Model_Observer</class>
<method>lacpaycs_payment_send</method>
</lacpaycs>
</observers>
</sales_model_service_quote_submit_before>
</events>
then we must create Observer class in OS/LacPayCS/Mode/Observer.php:
class OS_LacPayCS_Model_Observer {
protected $_code = 'lacpaycs';
// Here some our additional functions
/**
* #param Varien_Object $observer
*/
public function lacpaycs_payment_send(Varien_Object $observer)
{
/**
* #var Mage_Sales_Model_Order $order
* #var Mage_Sales_Model_Quote $quote
*/
$order = $observer->getOrder();
$quote = $observer->getQuote();
$payment = $order->getPayment();
if ($payment->getMethodInstance()->getCode() != $this->_code) {
return;
}
$helper = Mage::helper('lacpaycs');
try {
// Here we prepare data and sending request to gateway, and getting response
if (!$this->_validateResponse($response)) {
Mage::throwException('Error '.$this->errorMsg);
}
} catch (Exception $e) {
Mage::throwException($e->getMessage());
}
}
}
So in two words what we doing here $_code is the same that in our payment model and with it we checking in observer if we catched event when customer using our payment method
All another code is simple, so I think it's no need to comment it

Resources