当前位置:  开发笔记 > 编程语言 > 正文

Laravel 5.1 Eloquent ORM随机返回不正确的关系 - *主要更新*

如何解决《Laravel5.1EloquentORM随机返回不正确的关系-*主要更新*》经验,为你挑选了0个好方法。

我有一个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] => 
)

如您所见,没有user_id Customer,但Laravel已经返回了一个User对象.

更重要的是,如果我手动触发完全相同的OrderSuccess事件,上面的内容是不可重现的 - 它不会发送电子邮件,也不会加载User对象.

正如我之前所说,这个问题很少发生 - 通过呼叫中心平均每天约有40个订单,没有用户帐户的客户,突出显示的问题可能每周只发生一次或两次.

我对Laravel不太熟悉,不知道这里可能存在什么问题 - 它是某种形式的模型缓存,Eloquent ORM的问题,还是系统中的其他一些问题?

任何想法赞赏 - 我可能会在Laravel github问题跟踪器中发布此问题,如果它似乎是某种形式的错误.

更新关于提出的一些答案/评论,我试图删除任何潜在的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),也会检索不相关的用户.

更新2由于第一次更新没有任何帮助,我恢复使用原始的Eloequent方法.为了尝试另一个解决方案,我从事件处理程序中取出了我的事件代码,并将其放在我的控制器中 - 我之前使用OrderSuccess事件触发Event::fire(new OrderSuccess ($order));,而是我注释了这一行,并将事件处理程序代码放在控制器方法中:

$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%)订单已通过帐户信用支付给没有用户帐户的客户,但没有导致发送不正确的电子邮件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();是产生在我的ID credit_transactions以某种方式被使用Laravel检索用户对象表.从上面的处理程序中可以看出,我不会$order在任何时候更新我的对象.

Laravel如何使用(记住,仍然是随机的,一些<50%的时间)我新创建的id $credit_transaciton来填充$order->customer->user模型对象?

推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站