Search code examples
laravelobserverstinker

Is there a way to catch when I'm using a laravel tinker session to fiddle with the Models in an Observer class?


I have an Observer set up to Listen to a Model's events in order to keep my Controller clean of Logging messages. My implementation is as follows:

First, a store method that does just what it's supposed to do. Create and save a new model from valid parameters.

# app/Http/Controllers/ExampleController.php
namespace App\Http\Controllers;

use App\Http\Requests\StoreExample;
use App\Example;

class ExampleController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Create and save an Example from validated form parameters.
     * @param  App\Http\Requests\StoreExample  $request
     */
    public function store(StoreExample $request)
    {
        Example::create($request->validated());

        return back();
    }
}

The StoreExample Form Request isn't important. It just validates and checks a gate to authorize the action.

The Observer I have set up logs this action.

# app/Observers/ExampleObserver.php
namespace App\Observers;

use App\Example;

class ExampleObserver
{
    public function created(Example $example): void
    {
        \Log::info(auth()->id()." (".auth()->user()->full_name.") has created Example with params:\n{$example}");
    }
}

The problem I have, is the way my logs depend on the auth() object to be set. Given the auth middleware and the gate it has to check in order to store an Example, there is no way a guest user will set off this code.

However, I do like to use tinker in my local and staging environments to check the behavior of the site but that can set off an error (Well, PHP notice to be more precise) because I can create Example models without being authenticated and the logger will try to fetch the property full_name from the non-object auth()->user().

So my question is as follows: Is there a way to catch when I'm specifically using the Laravel tinker session to handle my models in the Observer class?


Solution

  • Okay, replying to my own question: There IS a way. It requires using a Request object. Since observers do not deal with requests on their own, I injected one in the constructor. request() can be used instead, so no DI is needed.

    Why is a Request important?

    Because a request object has an accessible $server attribute that has the information I want. This is the relevant information I get by returning a dd($request->server) (I'm not gonna paste the whole thing. My Request's ServerBag has over 100 attributes!)

    Symfony\Component\HttpFoundation\ServerBag {#37
      #parameters: array:123 [
        "SERVER_NAME" => "localhost"
        "SERVER_PORT" => 8000
        "HTTP_HOST" => "localhost:8000"
        "HTTP_USER_AGENT" => "Symfony"   // Relevant
        "REMOTE_ADDR" => "127.0.0.1"
        "SCRIPT_NAME" => "artisan"       // Relevant
        "SCRIPT_FILENAME" => "artisan"   // Relevant
        "PHP_SELF" => "artisan"          // Relevant
        "PATH_TRANSLATED" => "artisan"   // Relevant
        "argv" => array:2 [              // Relevant
          0 => "artisan"
          1 => "tinker"
        ]
        "argc" => 2
      ]
    }
    

    So there's all these attributes I can filter by using $request->server('attribute') (returns $request->server->attribute or null, so no risk of accessing an undefined property). I can also do $request->server->has('attribute') (returns true or false)

    # app/Observers/ExampleObserver.php
    namespace App\Observers;
    
    use App\Example;
    
    class ExampleObserver
    {
        /* Since we can use request(), there's no need to inject a Request into the constructor
        protected $request;
    
        public function __construct(Request $request)
        {
            $this->request = $request;
        }
        */
    
        public function created(Example $example): void
        {
            \Log::info($this->getUserInfo()." has created Example with params:\n{$example}");
        }
    
        private function getUserInfo(): string
        {
            // My logic here. 
        }
    }