Imran Imran - 19 days ago 6
PHP Question

Where to place Laravel Listener logic

I would like to associate an Order (ID saved in user session) when a user signs up.

I have created a event and a listener as documented in Laravel site. The problem I'm having is that I'm not sure where the logic to associate the records should go, I initially put this in the

Order Controller
but the app would try to reference Builder::attachOrderToUser instead.

I moved the method into my Model and it worked however now I'm given the error:


Call to undefined method Illuminate\Database\Query\Builder::attach()


Could someone point out where this logic should actually go?

Method in Order Model:

/**
* Attach Order to Customer
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public static function attachOrderToUser($order_id, $user_id)
{
// @TODO: Wrap around event handler
$user = User::find($user_id);

if($user->order()->attach($order_id))
return true;
else
return false;

}


Here is my listener code:

namespace App\Listeners;

use App\Events\UserQuickSignUpComplete;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

use Auth;
use Session;
use App\Order;
use Illuminate\Http\Request;

class AttachGuestOrderToUser
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}

/**
* Handle the event.
*
* @param UserQuickSignUpComplete $event
* @return void
*/
public function handle(UserQuickSignUpComplete $event)
{
// get currently logged in user (was signed in on method that triggers event)
$user = Auth::user();
// get order
$order = Session::get('last_transaction.order_id');
// attach order to user
Order::attachOrderToUser($order['order_id'], $user['id']);
}
}


Any other suggestions would also be appreciated!

Answer

Attaching two objects together reads as a service to me. Where should such a service go, then, is the question. A controller doesn't feel right, because we're not controlling user actions. A repository doesn't feel right, because we're dealing with two separate model instances (rather than a collection of the same kind of models).

But look at the name of your event listener: \App\Listeners\AttachGuestOrderToUser. That's a very specific name that suggests a specific job. In my mind, that class is already a service. So I'd simply put the logic you need right in that listener.

namespace App\Listeners;

use App\Events\UserQuickSignUpComplete;

class AttachGuestOrderToUser
{
    /**
     * You guessed it: attach a guest order to a specific user.
     *
     * @param  UserQuickSignUpComplete  $event
     * @return void
     */
    public function handle(UserQuickSignUpComplete $event)
    {
        \Auth::user()->order()->attach(\Session::get('last_transaction.order_id'));
    }
}

In general, event listeners are a specific kind of service, so this should intuitively makes sense to future code readers.

Comments