Php Laravel 5.1雄辩ORM随机返回不正确的关系-*主要更新*
我有一个Laravel应用程序,它为一个流量适中的电子商务网站提供动力。该网站允许人们通过前端下单,但它也具有通过呼叫中心通过电话接受订单的后端功能 订单与客户相关,客户也可以是用户,用户可以是登录到前端的用户。没有用户帐户的客户只有在通过呼叫中心接受订单时才会创建 我遇到的问题非常奇怪,我相信可能是某种Laravel bug 这只是偶尔发生,但现在发生的是,当通过呼叫中心为没有用户帐户的客户下订单时,订单确认被发送给随机用户-据我所知,字面上是随机的,只是从数据库中拔出,尽管数据中没有关联 以下是项目中模型的相关部分:Php Laravel 5.1雄辩ORM随机返回不正确的关系-*主要更新*,php,laravel,laravel-5,eloquent,laravel-5.1,Php,Laravel,Laravel 5,Eloquent,Laravel 5.1,我有一个Laravel应用程序,它为一个流量适中的电子商务网站提供动力。该网站允许人们通过前端下单,但它也具有通过呼叫中心通过电话接受订单的后端功能 订单与客户相关,客户也可以是用户,用户可以是登录到前端的用户。没有用户帐户的客户只有在通过呼叫中心接受订单时才会创建 我遇到的问题非常奇怪,我相信可能是某种Laravel bug 这只是偶尔发生,但现在发生的是,当通过呼叫中心为没有用户帐户的客户下订单时,订单确认被发送给随机用户-据我所知,字面上是随机的,只是从数据库中拔出,尽管数据中没有关联 以
class Order extends Model
{
public function customer()
{
return $this->belongsTo('App\Customer');
}
}
class Customer extends Model
{
public function orders()
{
return $this->hasMany('App\Order');
}
public function user()
{
return $this->belongsTo('App\User');
}
}
class User extends Model
{
public function customer()
{
return $this->hasOne('App\Customer');
}
}
以下是上述数据库迁移(为简洁起见进行了编辑):
发送订单确认的逻辑在一个事件处理程序中,该事件处理程序在订单支付后被触发
以下是OrderSuccess
事件(为简洁起见进行了编辑):
如图所示,此事件通过一个Order
model对象传递
以下是事件处理程序(为简洁起见进行了编辑):
检查$order->customer->user
对象是否为空,如果为真,则发送订单确认。如果为空(通常为空),则不发送确认
从上面可以看出,我添加了一个日志来记录发送电子邮件时的对象。下面是一个错误的示例(为了简洁起见,再次截断):
如您所见,Customer
没有用户id,但Laravel返回了一个user
对象
更重要的是,如果我手动触发完全相同的OrderSuccess
事件,则上述内容不可复制-它不会发送电子邮件,也不会加载用户
对象
正如我之前所说,这个问题很少发生——通过呼叫中心,没有用户帐户的客户平均每天大约有40个订单,突出显示的问题可能每周只发生一到两次
我对Laravel不够熟悉,不知道这里可能存在什么问题-是某种形式的模型缓存、雄辩的ORM问题还是系统中的其他小精灵
任何想法都值得赞赏-如果这似乎是某种形式的bug,我可能会在Laravel github问题跟踪器中发布这个问题
更新关于提出的一些答案/评论,我已尝试消除任何潜在的有说服力的ORM问题,手动检索数据,如下所示:
$customer = Customer::find($order->customer_id);
$user = User::find($customer->user_id);
if(!is_null($user)) {
// send email and log actions etc
}
以上仍然产生相同的随机结果-即使客户没有用户id
(在本例中为NULL),也会检索不相关的用户
更新2由于第一次更新没有任何帮助,我恢复使用最初的“顺序”方法。为了尝试另一种解决方案,我将事件代码从事件处理程序中取出,并将其放在我的控制器中-我之前使用event::fire(newordersuccess($order)),由OrderSuccess事件触发代码>,我将这行注释掉,并将事件处理程序代码放在控制器方法中:
$order = Order::find($order_id);
//Event::fire(new OrderSuccess ($order));
// code from the above event handler
$order->paid = date('Y-m-d H:i:s');
$order->save();
if(!is_null($order->customer->user)) {
App_log::add('customer_order_success_email_sent', 'Handlers\Events\OrderSuccessProcess\handle', $order->id, print_r($order->customer, true).PHP_EOL.print_r($order->customer->user, true));
// email the user the order confirmation
Mail::send('emails.order_success', ['order' => $order], function($message) use ($order)
{
$message->to($order->customer->user->email, $order->customer->first_name.' '.$order->customer->last_name)->subject('Order #'.$order->id.' confirmation');
});
}
上述变更已经在生产现场进行了一个多星期了——自从变更后,就再也没有出现过一次此类问题
我能得出的唯一可能的结论是,Laravel事件系统中存在某种缺陷,以某种方式破坏了传递的对象。或者会有其他的事情在起作用吗
更新3
我现在说将代码移到活动之外解决了这个问题似乎为时过早——事实上,通过我的日志记录,在过去的2天里,我可以看到发送了更多错误的订单确认(总共5次,在几乎3周没有问题之后)
我注意到,收到恶意订单确认的用户ID似乎在增加(不是没有间隙,而是以升序)
我还注意到,每个有问题的订单都是通过现金和账户信用支付的——大多数订单都是现金支付的。我进一步研究了这一点,用户ID实际上是相关信用交易的ID
以上是解决这一问题的第一个突破。仔细检查后,我发现问题仍然是随机的——有相当多(至少50%)的订单是通过帐户信用为客户支付的,没有用户帐户,但没有导致发送错误的电子邮件(尽管相关的信用交易id与用户id匹配)
所以,这个问题仍然是随机的,或者看起来是随机的。我的信用赎回事件是这样触发的:
Event::fire(new CreditRedemption( $credit, $order ));
上面的调用正好在我的orderssuccess
事件之前-如您所见,这两个事件都传递给$order
模型对象
我的CreditRedemption
事件处理程序如下所示:
public function handle(CreditRedemption $event)
{
// make sure redemption amount is a negative value
if($event->credit < 0) {
$amount = $event->credit;
}
else {
$amount = ($event->credit * -1);
}
// create the credit transaction
$credit_transaction = New Credit_transaction();
$credit_transaction->transaction_type = 'Credit Redemption';
$credit_transaction->amount = $amount; // negative value
$credit_transaction->customer_id = $event->order->customer->id;
$credit_transaction->order_id = $event->order->id;
// record staff member if appropriate
if(!is_null($event->order->staff)) {
$credit_transaction->staff_id = $event->order->staff->id;
}
// save transaction
$credit_transaction->save();
return $credit_transaction;
}
公共函数句柄(CreditRedemption$事件)
{
//确保赎回金额为负值
如果($event->credit<0){
$amount=$event->credit;
}
否则{
$amount=($event->credit*-1);
}
//创建信用交易记录
$credit_transaction=新的credit_transaction();
$credit_transaction->transaction_type='信用赎回';
$credit\u transaction->amount=$amount;//负值
$credit\u transaction->customer\u id=$event->order->customer->id;
$credit\u transaction->order\u id=$event->order->id;
//记录工作人员(如适用)
如果(!为空($event->order->staff)){
$credit\u transaction->staff\u id=$event->order->staff->id;
}
//保存事务
$credi
$customer = Customer::find($order->customer_id);
$user = User::find($customer->user_id);
if(!is_null($user)) {
// send email and log actions etc
}
$order = Order::find($order_id);
//Event::fire(new OrderSuccess ($order));
// code from the above event handler
$order->paid = date('Y-m-d H:i:s');
$order->save();
if(!is_null($order->customer->user)) {
App_log::add('customer_order_success_email_sent', 'Handlers\Events\OrderSuccessProcess\handle', $order->id, print_r($order->customer, true).PHP_EOL.print_r($order->customer->user, true));
// email the user the order confirmation
Mail::send('emails.order_success', ['order' => $order], function($message) use ($order)
{
$message->to($order->customer->user->email, $order->customer->first_name.' '.$order->customer->last_name)->subject('Order #'.$order->id.' confirmation');
});
}
Event::fire(new CreditRedemption( $credit, $order ));
public function handle(CreditRedemption $event)
{
// make sure redemption amount is a negative value
if($event->credit < 0) {
$amount = $event->credit;
}
else {
$amount = ($event->credit * -1);
}
// create the credit transaction
$credit_transaction = New Credit_transaction();
$credit_transaction->transaction_type = 'Credit Redemption';
$credit_transaction->amount = $amount; // negative value
$credit_transaction->customer_id = $event->order->customer->id;
$credit_transaction->order_id = $event->order->id;
// record staff member if appropriate
if(!is_null($event->order->staff)) {
$credit_transaction->staff_id = $event->order->staff->id;
}
// save transaction
$credit_transaction->save();
return $credit_transaction;
}
Schema::create('customers', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->nullable();
$table->foreign('user_id')->references('id')->on('users');
$table->string('first_name');
$table->string('last_name');
$table->string('telephone')->nullable();
$table->string('mobile')->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::create('orders', function(Blueprint $table)
{
$table->increments('id');
$table->integer('payment_id')->nullable()->index();
$table->integer('customer_id')->nullable();
$table->foreign('customer_id')->references('id')->on('customers');
$table->integer('staff_id')->nullable()->index();
$table->decimal('total', 10, 2);
$table->timestamps();
$table->softDeletes();
});
$customer->save();
$customer->user->save($user);
$user_exists = $order->customer()->user();
if($user_exists)
{
//email whatever
}
class Order extends Model
{
public function customer()
{
return $this->belongsTo('App\Customer', 'foreign_key', 'id');
}
}
->unsigned()
$table->integer('user_id')->unsinged()->index();
$table->integer('user_id')->index()->nullable();
$customer = Customer::find($order->customer_id);
$user = User::find($customer->user_id);
if(!is_null($user)) {
// send email and log actions etc
}
$customer = Customer::whereNotNull('user_id')->find($order->customer_id);
if (!$customer->isEmpty()) {
// send email and log actions etc
}