Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessor that decrypts model value isn't working

I have a trait that uses accessors and mutators to encrypt model values:

trait Encryptable
{
    public function getAttribute($key)
    {
        $value = parent::getAttribute($key);

        if (in_array($key, $this->encryptable)) {
            $value = Crypt::decrypt($value);
            return $value;
        } else {
            return $value;
        }
    }

    public function setAttribute($key, $value)
    {
        if (in_array($key, $this->encryptable)) {
            $value = Crypt::encrypt($value);
        }

        return parent::setAttribute($key, $value);
    }
} 

Comments Model

protected $fillable = ['content','user_id','commentable_id', 'commentable_type'];
protected $encryptable = [
    'content'
];

CommentController

public function storePostComment(Request $request, Post $Post)
{
    $this->validate($request, [
        'content' => 'required',
    ]);

    $comment = $post->comments()->create([
        'user_id' => auth()->user()->id,
        'content' => $request->content
    ]);
    
    
    dd($comment->content);
    //return new CommentResource($comment);
}

What's happening is that when I pass the return new CommentResource($comment); gives me the comments content encrypted, but dd($comment->content); decrypts the comments content. How do I decrypt the entire comment object so I can output it in a resource?

Edit For CommentResource

class CommentResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'content' => $this->content,
            'owner' => $this->owner,
        ];
    }
} 

Edit 2 for answer

Here's my attempt:

use App\Comment;

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class CommentResource extends JsonResource
{
    
    public function __construct(Comment $resource)
    {
        $this->resource = $resource;
    }
    
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'content' => $this->content,
            'owner' => $this->owner,
        ];
    }
}

Error:

Argument 1 passed to App\Http\Resources\CommentResource::__construct() must be an instance of App\Http\Resources\Comment, instance of App\Comment given, called in /Applications/MAMP/htdocs/my-app/app/Http/Controllers/Api/CommentController.php on line 31

Edit 3 (final edit)

Here's what I figured out:

I tried a bunch of various combinations along with @Edwin Krause answer. I have another model using this encryptable trait and outputting in a resource that works fine.

To give a bit more context to this question I found out there was a problem using assertJsonFragment in a test:

CommentsTest

/* @test **/
public function a_user_can_comment_on_a_post()
{
    $decryptedComment = ['content'=>'A new content']
    $response = $this->json('POST',  '/api/comment/' . $post->id, $decryptedComment);

        $response->assertStatus(201);

        $response->assertJsonStructure([
            'data' => [
                'owner',
                'content'
            ]
        ])
        ->assertJsonFragment(['content' => $decryptedContent['content']]);
}

assertJsonFragment was returning the encrypted content and therefore failing because it was being tested against the decrypted comments content.

I used dd(new CommentResource($comment)); in the controller to check to see if it the content was decrypting, it wasn't.

I tried various different things trouble shooting with dd() in the controller method and even testing in the browser. Still nothing. I added @Edwin Krause code and still nothing on dd()

I finally got lucky and got rid of dd() with @Edwin Krause and changing my controller to:

Working code combined with @Edwin Krause answer in my CommentResource

$comment = Comment::create([
    'user_id' => auth()->user()->id,
    'content' => $request->content,
    'commentable_type' => 'App\Post',
    'commentable_id' => $post->id,
]);

return new CommentResource($comment);

The tests went green. I tried dd(new CommentResource($comment)); and the content was encrypted still. The content output on the broweser and assertJsonFragment worked. I must've tried so many combinations to try and figure this out and I kind of just got lucky.

I'm unsure as to why this is the way it is, but I've already spent hours on this, so I can't troubleshoot why it's breaking. Maybe someone else can.

like image 318
Kyle Corbin Hurst Avatar asked Nov 07 '22 10:11

Kyle Corbin Hurst


1 Answers

Just a suggestion to try and override the constructor of the JsonResource and typecast the $resource parameter to your Modelclass. It work's for other things, not sure if it fixes your issue, that needs to be tested

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;
use App\Comment;

class CommentResource extends JsonResource
{

  public function __construct(Comment $resource)
  {
    $this->resource = $resource;
    $this->resource->content = $resource->content;
  }

  ....

Edit: I Played around a bit more with the constructor and the modified version should actually work. I don't have any encrypted data to play with, but logically this should work.

like image 113
Edwin Krause Avatar answered Nov 11 '22 06:11

Edwin Krause