Search code examples
phplaraveleloquentlaravel-9

Laravel 9 Accessor not Returning Value


I have a Reminder model that has a sound_path column. I created an accessor for that column in the model but it's returning null and I doubled checked that the database has a value in that column. What am I doing wrong?

Note: Of course I can call $this->sound_path directly in soundPathUrl mutator without creating the accessor from the first place but I'm interested to know why if I called the accessor is not returning any value.

Reminder model

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Spatie\Translatable\HasTranslations;

class Reminder extends BaseModel
{
    use HasFactory, SoftDeletes, HasTranslations;

    public $translatable = [
        'title__ml',
    ];

    protected $casts = [
        'available_days_for_reminder' => 'json',
        'is_multiple_days_allowed' => 'boolean'
    ];

    protected $appends = ['sound_path_url'];

    /**
     * Get the sound path
     * 
     * @param string  $value
     * @return \Illuminate\Database\Eloquent\Casts\Attribute
     */
    protected function soundPath(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => $value
        );
    }

    /**
     * Get sound path download URL
     *
     * @return \Illuminate\Database\Eloquent\Casts\Attribute
     */
    protected function soundPathUrl(): Attribute
    {
        return new Attribute(
            get: fn () => asset('storage/' . $this->soundPath),
        );
    }
}

Reminder controller

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\Reminder\ReminderCollection;
use App\Http\Resources\Reminder\ReminderResource;
use App\Models\Reminder;
use Exception;
use Illuminate\Http\Request;

class ReminderController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $reminders = Reminder::paginate(5);

        return ReminderCollection::collection($reminders);
    }
}

ReminderCollection API resource

namespace App\Http\Resources\Reminder;

use Illuminate\Http\Resources\Json\JsonResource;

class ReminderCollection extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title__ml,
            'sound' => $this->sound_path_url
        ];
    }
}

Screenshot of Response

Response JSON

At the arrow there should be the value of sound_path.


Solution

  • Your issue is that you are calling $this->soundPath instead of $this->sound_path in soundPathUrl method...

    So, you should have this:

    /**
     * Get sound path download URL
     *
     * @return \Illuminate\Database\Eloquent\Casts\Attribute
     */
    protected function soundPathUrl(): Attribute
    {
        return new Attribute(
            get: fn () => asset('storage/' . $this->sound_path),
        );
    }
    

    Check the documentation and you will see you still have to call your properties using snake_case.