1

I'm using Laravel 6 with a SQL Server 2017 database backend. In the database I have a table called PersonPhoto, with a Photo column and a Thumbnail column where the photos and thumbnails are stored as VARBINARY.

I have defined the following Eloquent model, with two Accessors to convert the images to base64 encoding:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PersonPhoto extends Model
{
    protected $connection = 'mydb';
    protected $table = 'PersonPhoto';
    protected $primaryKey ='PersonID';

    public function person(){

        return $this->belongsTo('App\Person', 'PersonID');

    }

    public function getPhotoAttribute($value){

        return base64_encode($value);

    }


    public function getThumbnailAttribute($value){

        return base64_encode($value);

    }

}

This works fine in Blade templates, however when I try to serialize to JSON or an Array I get a "Malformed UTF-8 characters, possibly incorrectly encoded" error, as if the Accessors are being ignored and the raw data is being serialized. To workaround this, I have altered the model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PersonPhoto extends Model
{
    protected $connection = 'mydb';
    protected $table = 'PersonPhoto';
    protected $primaryKey ='PersonID';

    //Added to hide from and add fields to serializer
    protected $hidden = ['Photo', 'Thumbnail'];
    protected $appends = ['encoded_photo', 'encoded_thumbnail'];

    public function person(){

        return $this->belongsTo('App\Person', 'PersonID');

    }

    public function getPhotoAttribute($value){

        return base64_encode($value);

    }

    public function getThumbnailAttribute($value){

        return base64_encode($value);

    }


    //Added these new accessors
    public function getEncodedPhotoAttribute(){

         return base64_encode($this->Photo);

    }

    public function getEncodedThumbnailAttribute(){

        return base64_encode($this->Thumbnail);

    }

}

This hides the original Photo and Thumbnail fields from the serializer and includes the two new accessors. This appears to work and solves my issue.

Questions: 1) Is Laravel's serializer ignoring my Accessors as I suspect, and is this by design? 2) Although my workaround works, is this a reasonable approach or am I likely to run into problems? Is there a better way of doing it?

Thanks

Leonard H. Martin
  • 2,734
  • 4
  • 23
  • 26

1 Answers1

2

I think you have two issues:

First, Laravel serialization requires that you append any accessors you want included — even if an attribute of the same name already exists. You did not explicitly append the desired values in the first example.

https://laravel.com/docs/5.8/eloquent-serialization#appending-values-to-json

Second, Laravel doesn't always like capitalized attribute names. It happily expects everything to be lowercase (snake_case) and based on some quick testing, seems to have some trouble associating a proper $value to pass to an accessor when case is involved.

However, you can modify your accessor to call the attribute directly instead of relying on Laravel to figure out what you are asking for and achieve the desired results.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PersonPhoto extends Model
{
    protected $connection = 'mydb';
    protected $table = 'PersonPhoto';
    protected $primaryKey = 'PersonID';

    // add the desired appends for serialization
    protected $appends = ['Photo','Thumbnail'];

    public function person()
    {
        return $this->belongsTo('App\Person', 'PersonID');
    }

    public function getPhotoAttribute()
    {
        // access the attribute directly
        return base64_encode($this->attributes['Photo']);
    }


    public function getThumbnailAttribute()
    {
        // access the attribute directly
        return base64_encode($this->attributes['Thumbnail']);
    }

}

EDIT: I actually see that you did something similar in your second example with $this->Thumbnail and $this->Photo. My example is of the same concept, but without relying on magic methods.
__get/__set/__call performance questions with PHP

matticustard
  • 4,850
  • 1
  • 13
  • 18
  • Thanks for your reply. I am encouraged that we both had similar solutions to this! I've since decided on a different approach as - on reflection - it was crazy for me to send an entire object graph down the wire when only a few fields were actually needed. But thank you for your time, it's much appreciated and I feel as if I've learnt something today.. – Leonard H. Martin Sep 21 '19 at 21:49