首页 > 解决方案 > 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.

标签: laravelencryptionlaravel-6accessor

解决方案


只是建议尝试覆盖 JsonResource 的构造函数并将 $resource 参数类型转换为您的模型类。它适用于其他事情,不确定它是否能解决您的问题,需要测试

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;
  }

  ....

编辑: 我对构造函数进行了更多尝试,修改后的版本应该可以正常工作。我没有任何加密数据可以使用,但从逻辑上讲这应该可以。


推荐阅读