I am experiencing some odd results when using Magento's
Mage::getSingleton('customer/session')->getCustomer();
I have the below code to first check if the customer is logged in, and if so to get the firstname of the logged in customer:
if(!$this->helper('customer')->isLoggedIn())
{
$name = Mage::getSingleton('customer/session')->getCustomer()->getFirstname();
print 'Hello '.$name;
// ...
}
Most of the time this works fine and if Joe Bloggs is logged in then it outputs:
Hello Joe
But every now and then, and it seems to be when a high amount of customers are logged in I get unexpected outputs with other names
Hello Lucy
or
Hello John
Is Mage::getSingleton('customer/session')->getCustomer() a foolproof way of getting the customers details or is it possible it's getting another logged in customers details? Or have I got a problem with my sessions mixing up?
As per the core code in Mage_Customer_Model_Session::getCustomer:
/**
* Retrieve customer model object
*
* #return Mage_Customer_Model_Customer
*/
public function getCustomer()
{
if ($this->_customer instanceof Mage_Customer_Model_Customer) {
return $this->_customer;
}
$customer = Mage::getModel('customer/customer')
->setWebsiteId(Mage::app()->getStore()->getWebsiteId());
if ($this->getId()) {
$customer->load($this->getId());
}
$this->setCustomer($customer);
return $this->_customer;
}
...there's no way that another customer's data could be loaded (by core Magento).
It would firstly return the customer model if it's already loaded, if not then load it if there's an ID present already, and finally it would return an empty customer model if nothing else.
It's possible that your sessions are getting mixed up. I suggest you check your configuration - are you using databases, files, Redis etc for session storage?
Related
I have very similar code that is functioning without a hitch elsewhere in my Laravel app, but for some reason the below code is creating two $paypal_object database entries that are identical except for the payment_id field:
DonateController.php
public function mimic()
{
try {
//This block is the addOrder function from the pizza tutorial
$paypal_object = new Paypal();
//The user who is making the payment
$paypal_object->user()->associate(Auth::user());
$paypal_object->amount = 50.00;
$paypal_object->description = "new subscription";
$paypal_object->state = $payment->getState();
$paypal_object->payment_id = $payment->getId();
$paypal_object->save();
} catch (Exception $ex) {
$message = $ex->getMessage();
$messageType = "error";
}
exit;
}
Database Results (with test data)
I've condensed the above code from my controller a little. If you'd like to see more of my code, let me know and I'd be happy to provide it. My theory right now is that for some reason my mimic() method is getting run twice, but I'm not sure how to test to see if that's true beyond including this in the above code, but it's not giving me any results this time:
echo '<script>console.log(' . json_encode("Testing to see how many times this message appears.") . ');</script>';
Even if it is running twice, I'm not sure how that's happening or where to check. I'm guessing it could well be another problem entirely, but I don't know what.
Right now, I'm accessing this method by pinging its route:
Route::get('/paypal/mimic', 'DonateController#mimic');
but for every 1 ping I make, I get 2 database entries as shown in the above image.
Paypal model:
class Paypal extends Eloquent
{
/**
* Get the user that made the paypal payment.
*/
public function user()
{
# Defines an inverse one-to-many relationship
return $this->belongsTo('User');
}
}
User model:
public function paypal(){
# User has many paypal payments - although just one subscription
# Defines a one-to-many relationship
return $this->hasMany('Paypal');
}
Thanks in advance for any help.
I have an Acl that i am trying to implement on my site which is database driven. I have used this code here creating ACL with database. I am using a module based structure and this example happens to not be. So i placed everything that is supposed to go in application model, in the core module model. This script authenticates everyone that views the site and if they do not have a account it logs them in as guest. My issue is that it authenticates the user as Guest but i am getting an error on the mapper getbyId, it has the user id as 1 (guest) why is it returning and error? Is it something with the module based structure i am using?
I tried posting on the site itself but have not gotten an answer yet, it was developed a while ago - blog might be dead.
thank you for your help!
Updated 6:44 EST May 2nd:
I think my problem lies here:
private static function getAuthAdapter($loginField='login')
{
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users')
->setIdentityColumn($loginField)
->setCredentialColumn('password')
->setCredentialTreatment('MD5(CONCAT(?,salt))');
return $authAdapter;
}
I already have an established data connection through my application.ini and Bootstrap.
protected function _initDatabases()
{
$this->bootstrap('multidb');
$resource = $this->getPluginResource('multidb');
$databases = Zend_Registry::get('config')->resources->multidb;
foreach ($databases as $name => $adapter)
{
$db_adapter = $resource->getDb($name);
Zend_Registry::set($name, $db_adapter);
}
}
So now the question would be - How do i change the getAuthAdapter to use what i already have?
thanks again
Updated 9:38 EST May 3rd:
The above now is not an issue. actually it never was. As i dug further, when i created the Guest User it did not encrypt the password and did not add salt. So now i am logged in but still have same error.
This error comes from not getting the role_id.
if (!$auth->hasIdentity()){
$auth = Zend_Auth::getInstance();
$role_id = $auth->getIdentity()->role_id;
echo "Role Id =" . $role_id; exit;
} else {
echo "No Identity"; exit;
}
The above returns - Role Id =
So i have Identity why can i not get role_id - where role_id is the column name.
thanks again.
Updated 10:17 May 3:
For every ones knowledge. This is fixed. I have not explanation other than it was in some kinda of funk. I cleared the identity and it logged back on as Guest and the site came up.
So problem solved for me. This is a really great script so if you are looking for a data driven ACL dont hesitate to use the link above.
thanks
I am building my first Laravel 4 Application (PHP).
I find myself needing to call somthing like this often in most of my Models and Controllers...
$this->user = Auth::user();
So my question is, is calling this several times in the application, hitting the Database several times, or is it smart enough to cache it somewhere for the remainder of the request/page build?
Or do I need to do it differently myself? I glanced over the Auth class but didnt have time to inspect every file (16 files for Auth)
Here is the code for the method Auth::user().
// vendor/laravel/framework/src/Illuminate/Auth/Guard.php
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Auth\UserInterface|null
*/
public function user()
{
if ($this->loggedOut) return;
// If we have already retrieved the user for the current request we can just
// return it back immediately. We do not want to pull the user data every
// request into the method becaue that would tremendously slow the app.
if ( ! is_null($this->user))
{
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if ( ! is_null($id))
{
$user = $this->provider->retrieveByID($id);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->getRecaller();
if (is_null($user) and ! is_null($recaller))
{
$user = $this->provider->retrieveByID($recaller);
}
return $this->user = $user;
}
To me, it looks like it will get the user from the database only once per request. So, you can call it as many times as you want. It will only hit the DB once.
Auth::user() only hits the DB once, so it's not a problem invokes it many times. Btw, you can cache useful information of the user that you want to access frequently.
I'm creating an audit trail module that i will put in a larger system; and i've created a table to store the trail entries , as an "auditor" what i want to see the currently logged on user, the page where he/she is in, what action he/she did, and what were the changes and when...
these are basically what i want to see; my audit trail table looks like:
User| Timestamp| Module Name| Action| Old Value| New Value| Description
i basically had no problem getting the user, by
Yii::app()->session['username'];
the page/module and action by getting the controller's :
$this->module->getName();
$this->action->id;
My problem lies with the changes old value to new value, the edits done by the user.
i could sort of "sniff" out what edits/ changes he/she did by literally copying the variables and passing it through my function where i create the log.. How do i do this dynamically?
i sort of want to detect if a certain model's properties or attributes has been changed and see what changes were made so that i could get a detail log...Thanks ! sorry, i'm really trying hard to explain this.
In each model that you want to observe you can write a afterFind() method, where you store the current DB attributes into some private variable, e.b. _dbValues. Then in beforeSave() you verify the current attributes with the ones in _dbValues and create an audit record if there was a change.
After you have this working, you can take it a step further and create a behavior from it. You'd put the private variable, the afterFind() and the beforeSave() method there. Then you can attach that behavior to many records.
Quick example:
class Book extends CActiveRecord
{
private $oldAttrs = array();
public static function model($className = __CLASS__)
{
return parent::model($className);
}
public function tableName()
{
return 'book';
}
protected function afterSave()
{
// store history
if (!$this->isNewRecord) {
$newAttrs = $this->getAttributes();
$oldAttrs = $this->getOldAttributes();
// your code
}
return parent::afterSave();
}
protected function afterFind()
{
// Save old values
$this->setOldAttributes($this->getAttributes());
return parent::afterFind();
}
public function getOldAttributes()
{
return $this->oldAttrs;
}
public function setOldAttributes($attrs)
{
$this->oldAttrs = $attrs;
}
}
Your solution is good, but what if there're 2 threads that call ->save() at the same time?
Assume that:
the 1st thread find record, save the A status.
the 2nd thread find record, save the A status.
then 1st thread change record to B, call ->save(). System will log A->B
then 2nd thread change record to C, call ->save(). System will log A->C
summary, there're 2 log: A->B, A->C. If this is not problem for you, just ignore it and do the above solution.
I am using the customer_save_after event in magento, and all is working fine apart from 1 annoying thing - it is always fired twice.
There are no other modules rewriting this and I can find no other reason for this happening. When I look through all of the events getting fired at this time and this event is definately getting fired twice.
Anyone explain this?
I am writing a web service that hooks into this and its turning out to be quite inefficient to duplicate things.
I've noticed this double-save behaviour too. The way to prevent issue with your observer is to set a flag in the request that can be checked e.g.
if(Mage::registry('customer_save_observer_executed')){
return $this; //this method has already been executed once in this request (see comment below)
}
...execute arbitrary code here....
/* Customer Addresses seem to call the before_save event twice,
* so we need to set a variable so we only process it once, otherwise we get duplicates
*/
Mage::register('customer_save_observer_executed',true);
I ran into this as well and did a stack trace in the observer for each method, and can tell you at least ONE reason why it fires twice (there may be others):
When a new user creates an account, createPostAction() runs when the form is submitted. This action does a save() on the customer.
THEN, after the customer has been created, setCustomerAsLoggedIn() is called by createPostAction(). This in turn calls setCustomer(), which has this little bit of code:
if ((!$customer->isConfirmationRequired()) && $customer->getConfirmation()) {
$customer->setConfirmation(null)->save(); // here is the second save
$customer->setIsJustConfirmed(true);
}
Those are the two save()s which dispatch the save event. I only know this for sure for account creation in Magento 1.5. I doubt if it gets fired twice when creating users in the Admin area, or when a user edit's their information... but I don't know for sure.
I hope this helps!
Be careful with Jonathans solution, 'customer_save_observer_executed' stays in the session, so event will not be fired again in the browser session. So it's generally a bad idea, because it will not allow to register two or more customers in a row(actually, it will, but events will not be fired)
I suggest the following solution:
public function customerRegister(Varien_Event_Observer $observer)
{
$customer = $observer->getEvent()->getCustomer();
if (!$customer->getId())
return $this;
if(Mage::registry('customer_save_observer_executed_'.$customer->getId()))
return $this;
//your code goes here
Mage::register('customer_save_observer_executed_'.$customer->getId(),true);
}
I used a static var:
private static $_handleCustomerFirstSearchCounter = 1;
public function Savest($observer) {
if (self::$_handleCustomerFirstSearchCounter > 1) {
return $this;
}
$customerData = Mage::getSingleton('customer/session')->getCustomer();
$model = Mage::getModel('customerst/customerst')
->setQueryText(Mage::app()->getRequest()->getParam('q'))
->setCustomerId($customerData->getId())
->setCustomerName($customerData->getName())
->save();
self::$_handleCustomerFirstSearchCounter++;
}
The difference between these 2 events is one of them can't get customer info, while the other can. So the solution is
public function email_CustomerRegister(Varien_Event_Observer $observer){
$customer = Mage::getSingleton('customer/session')->getCustomer();
$customer_email = $customer->getEmail();
if(empty($customer_email)){
return;
}
// do something
}