我有一个Laravel应用程序,为电子商务网站提供适度的流量 . 该网站允许人们通过前端下订单,但它也具有通过呼叫中心通过电话接听订单的后端功能 .
订单与客户相关,客户可以选择是用户 - 用户是登录前端的用户 . 只有通过呼叫中心接收订单才能创建没有用户帐户的客户 .
我遇到的问题很奇怪,我相信可能是某种Laravel错误 .
它只是偶尔发生,但发生的事情是,当通过呼叫中心为没有用户帐户的客户下订单时,订单确认将发送给随机用户 - 字面意思是随机的,据我所知,尽管数据没有关系,但刚从数据库中拔出 .
这些是项目中模型的相关部分:
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');
}
}
这些是上面的数据库迁移(为简洁起见编辑):
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('first_name');
$table->string('last_name');
$table->string('email')->unique();
$table->string('password', 60);
$table->boolean('active');
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
Schema::create('customers', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->nullable->index();
$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')->index();
$table->integer('staff_id')->nullable()->index();
$table->decimal('total', 10, 2);
$table->timestamps();
$table->softDeletes();
});
发送订单确认的逻辑位于在支付订单后触发的事件处理程序中 .
这是 OrderSuccess
事件(为简洁起见编辑):
namespace App\Events;
use App\Events\Event;
use App\Order;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class OrderSuccess extends Event
{
use SerializesModels;
public $order;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
}
可以看出,此事件传递的是 Order
模型对象 .
这是事件处理程序(为简洁起见编辑):
/**
* Handle the event.
*
* @param OrderSuccess $event
* @return void
*/
public function handle(OrderSuccess $event)
{
// set order to paid
$order = $event->order;
$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');
});
}
}
检查 $order->customer->user
对象是否为空,如果为true,则发送订单确认 . 如果它为null(经常是),则不发送确认 .
从上面可以看出,我添加了一个日志来记录发送电子邮件时的对象 . 这是一个错误的例子(为简洁而再次截断):
App\Customer Object
(
[attributes:protected] => Array
(
[id] => 10412
[user_id] =>
[first_name] => Joe
[last_name] => Bloggs
[telephone] => 0123456789
[created_at] => 2015-09-14 13:09:45
[updated_at] => 2015-10-24 05:00:01
[deleted_at] =>
)
[relations:protected] => Array
(
[user] => App\User Object
(
[attributes:protected] => Array
(
[id] => 1206
[email] => johndoe@whoknows.com
[password] => hashed
[remember_token] =>
[created_at] => 2015-09-19 09:47:16
[updated_at] => 2015-09-19 09:47:16
[deleted_at] =>
)
)
)
[morphClass:protected] =>
[exists] => 1
[wasRecentlyCreated] =>
[forceDeleting:protected] =>
)
App\User Object
(
[attributes:protected] => Array
(
[id] => 1206
[email] => johndoe@whoknows.com
[password] => hashed
[remember_token] =>
[created_at] => 2015-09-19 09:47:16
[updated_at] => 2015-09-19 09:47:16
[deleted_at] =>
)
[morphClass:protected] =>
[exists] => 1
[wasRecentlyCreated] =>
[forceDeleting:protected] =>
)
如您所见, Customer
没有user_id,但Laravel已返回 User
对象 .
更重要的是,如果我手动触发完全相同的 OrderSuccess
事件,则上述内容不可重现 - 它不会发送电子邮件,也不会加载 User
对象 .
正如我之前所说,这个问题很少发生 - 通过呼叫中心平均每天约有40个订单,没有用户帐户的客户,突出显示的问题可能每周只发生一次或两次 .
我对Laravel不太熟悉,不知道这里可能存在什么问题 - 它是某种形式的模型缓存,Eloquent ORM的问题,还是系统中的其他一些问题?
任何想法赞赏 - 我可能会在Laravel github问题跟踪器中发布此问题,如果它似乎是某种形式的错误 .
Update 关于提出的一些答案/评论,我试图删除任何潜在的Eloquent ORM问题,手动检索数据,如下所示:
$customer = Customer::find($order->customer_id);
$user = User::find($customer->user_id);
if(!is_null($user)) {
// send email and log actions etc
}
以上仍然产生相同的随机结果 - 即使客户没有 user_id
(在这种情况下它是NULL),也会检索不相关的用户 .
Update 2 由于第一次更新没有任何帮助,我恢复使用原始的Eloequent方法 . 为了尝试另一个解决方案,我从事件处理程序中取出了我的事件代码,并将其放在我的控制器中 - 我之前使用 Event::fire(new OrderSuccess ($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事件系统中的某种错误,不知何故破坏了传递的对象 . 或者其他什么可以发挥作用?
Update 3 似乎我现在说过在事件之外移动我的代码解决问题还为时过早 - 实际上,通过我的日志记录,在过去的两天里我可以看到更多不正确的订单确认被发送出去(总共5个,几乎在3周没有问题) .
我注意到收到恶意订单确认的用户ID似乎正在递增(不是没有间隙,但仍然按升序排列) .
我还注意到每个问题订单都是通过现金和账户信贷支付的 - 大多数只是现金 . 我进一步研究了这个,用户ID实际上是相关信用交易的ID!
以上是试图解决这一问题的第一次铸铁突破 . 仔细观察后,我可以看到问题仍然是随机的 - 有很多(至少50%)订单是通过帐户信用支付给没有用户帐户的客户,但是没有导致发送不正确的电子邮件out(尽管相关的信用交易ID具有用户ID匹配) .
所以,问题仍然是随机的,或者看似如此 . 我的信用兑换事件是这样触发的:
Event::fire(new CreditRedemption( $credit, $order ));
在我的 OrderSuccess
事件之前调用上面的内容 - 正如您所看到的,两个事件都传递给 $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;
}
$credit_transaction->save();
正在我的 credit_transactions
表中生成id某种程度上被Laravel用来检索用户对象 . 从上面的处理程序中可以看出,我不会在任何时候更新我的 $order
对象 .
Laravel如何使用(记住,仍然是随机的,一些<50%的时间)我新创建的 $credit_transaciton
的id来填充 $order->customer->user
模型对象?
5 回答
您的迁移不应该
例如:
正如Laravel Doc中提到的那样?
我无法帮助您了解导致问题的根本原因,但我可以根据您在问题的更新1中提供的逻辑提供可能的解决方案 .
原始逻辑
修订逻辑
由于客户
user_id
可以为null,因此将返回的客户限制为具有user_id
的客户可能更有效 . 这可以通过使用whereNotNull()
方法来实现 . 然后我们可以继续检查客户是否被退回,如果是,则发送电子邮件等 .通过不给应用程序一个机会返回一个null
user_id
的客户,这应该有希望解决你的问题,但遗憾的是它并没有说明为什么它首先发生的原因 .与模型相比,我不确定您的迁移是否特别正确定义 . 如果使用belongsTo和hasOne关系,则应在迁移中使用外键引用 .
现在,只要在创建客户记录时存在实际用户,您就需要设置此列 . 但实际上您不必手动设置此列 . 您可以在使用关系时执行以下操作:
第1步:先保存客户 .
第2步:现在我们将在客户端设置user_id(如果存在) . 为此,您可以在$ user中获取用户对象,然后只需调用
上面的代码将自动在customers表上设置user_id
然后我将检查用户记录是否以下面的方式存在:
没有必要有FK . Eloquent可以根据列的名称来构建关系 .
更改字段的名称 . 该字段的名称应与具有后缀“_id”的表名匹配 . 在customers表中,user_id应为users_id . 在订单中,customer_id应为customers_id .
您可以尝试传递要加入的字段的名称:
我不是Laravel的高级用户,所以这可能不起作用 . 我遇到了同样的问题,我通过重命名所有模型和列来匹配表名(用“s”)来解决它 .
customers表中的
user_id
字段应该可以为空 .