Leandro
Leandro

Reputation: 377

How to use Jenssegers Optimus in Laravel Models?

I am using Jenssegers Optimus package to obfuscate my URLS.

Currently, I am calling it in every single controller that deals with get requests. Problem is, I need to constantly encode and decode my IDs in almost all methods in my controllers.

E.g.:

use Jenssegers\Optimus\Optimus;

    class ResponseController extends Controller
    {
        protected $optimus;

        public function __construct(Optimus $opt)
        {
            $this->optimus = $opt;

        }

        public function index()
        {

            $labels = Label->get();

            foreach ($labels as $key => $label){
              $label->id = $this->optimus->encode($label->id);
              $labels[$key] = $label;
            }

            return view('responses/index', compact('labels'));
        }

        public function show($id)
        {
            $id = $this->optimus->decode($id);

            $label =  Label::get($id);

       }

    }

I thought of creating Accessors & Mutators to always encrypt the IDs of the models I need to obfuscate in the URL. So I'd put them in a trait to reuse the code. I tried:

  use Jenssegers\Optimus\Optimus;

    trait EncodeId{

       public function getIdAttribute($value, Optimus $optimus)
       {

        return $optimus->encode($value);
       }

    }

Then I added this trait to my model. However, Laravel would throw an error complaining about Optimus $optimus in the method definition. It said $optimus was expected to be a type of Jenssegers\Optimus\Optimus even though I am declaring it. That works for controllers just fine, but it doesn't work for models apparently. Or I shouldn't try to use a trait in this case.

Here's the actual error:

FatalThrowableError in EncodeId.php line 10:
Type error: Argument 2 passed to App\Label::getIdAttribute() must be an instance of Jenssegers\Optimus\Optimus, none given, called in /home/../vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 2734

It would be really nice if I could use Optimus obfuscation on the model level rather than calling its encode and decode functions multiple times in my controllers.

There's another package called FakeID that is meant to do that. I tried to implement it in my project but it didn't work. I am pretty sure I could handle it myself since it seems a simple task.

Upvotes: 1

Views: 889

Answers (1)

felipsmartins
felipsmartins

Reputation: 13549

Get mutators (AKA accessors) are called along with a single argument. That's why you are getting expected to be a type of Jenssegers\Optimus\Optimus error as Jenssegers\Optimus\Optimus is not injected by framework when calling acessors or mutators (like controllers does).

Just read this snippet from source code (line 2632):

  public function getAttributeValue($key)
    {
        $value = $this->getAttributeFromArray($key);
        // If the attribute has a get mutator, we will call that then return what
        // it returns as the value, which is useful for transforming values on
        // retrieval from the model to a form that is more useful for usage.
        if ($this->hasGetMutator($key)) {
            return $this->mutateAttribute($key, $value);
        }

        //...    
    }

And now the call to $this->mutateAttribute($key, $value); (line 2736)

 protected function mutateAttribute($key, $value)
    {
        return $this->{'get'.Str::studly($key).'Attribute'}($value);
    }

Did you understand now? Acessor/get mutator are called along with just one argument: $value.

Solution

You could to try something like this:

public function getIdAttribute($value)
{
    return app(Optimus::class)->encode($value);
}

Invoking Optimus instance from container (app()) would do the trick.

Upvotes: 2

Related Questions