Tambahkan atribut khusus ke model Laravel / Eloquent saat dimuat?

219

Saya ingin dapat menambahkan atribut / properti khusus ke model Laravel / Eloquent ketika dimuat, mirip dengan bagaimana hal itu dapat dicapai dengan metode RedBean $model->open() .

Sebagai contoh, saat ini, di controller saya, saya punya:

public function index()
{
    $sessions = EventSession::all();
    foreach ($sessions as $i => $session) {
        $sessions[$i]->available = $session->getAvailability();
    }
    return $sessions;
}

Alangkah baiknya untuk menghilangkan loop dan memiliki atribut 'tersedia' sudah diatur dan diisi.

Saya sudah mencoba menggunakan beberapa peristiwa model yang dijelaskan dalam dokumentasi untuk melampirkan properti ini ketika objek dimuat, tetapi sejauh ini tidak berhasil.

Catatan:

  • 'tersedia' bukan bidang di tabel yang mendasarinya.
  • $sessionssedang dikembalikan sebagai objek JSON sebagai bagian dari API, dan oleh karena itu memanggil sesuatu seperti $session->available()pada templat bukanlah pilihan
coatesap
sumber

Jawaban:

316

Masalah ini disebabkan oleh fakta bahwa Model's toArray()metode mengabaikan setiap accesor yang tidak langsung berhubungan dengan kolom dalam tabel yang mendasari.

Seperti yang dikatakan Taylor Otwell di sini , "Ini disengaja dan untuk alasan kinerja." Namun ada cara mudah untuk mencapai ini:

class EventSession extends Eloquent {

    protected $table = 'sessions';
    protected $appends = array('availability');

    public function getAvailabilityAttribute()
    {
        return $this->calculateAvailability();  
    }
}

Atribut apa pun yang tercantum dalam properti $ appends akan secara otomatis dimasukkan dalam array atau bentuk JSON dari model, asalkan Anda telah menambahkan accessor yang sesuai.

Jawaban lama (untuk versi Laravel <4.08):

Solusi terbaik yang saya temukan adalah menimpa toArray()metode dan baik kejelasan mengatur atribut:

class Book extends Eloquent {

    protected $table = 'books';

    public function toArray()
    {
        $array = parent::toArray();
        $array['upper'] = $this->upper;
        return $array;
    }

    public function getUpperAttribute()
    {
        return strtoupper($this->title);    
    }

}

atau, jika Anda memiliki banyak pengakses khusus, lewati semuanya dan terapkan:

class Book extends Eloquent {

    protected $table = 'books';

    public function toArray()
    {
        $array = parent::toArray();
        foreach ($this->getMutatedAttributes() as $key)
        {
            if ( ! array_key_exists($key, $array)) {
                $array[$key] = $this->{$key};   
            }
        }
        return $array;
    }

    public function getUpperAttribute()
    {
        return strtoupper($this->title);    
    }

}
coatesap
sumber
Pertanyaan dan jawaban terbaik untuk hari ini. Saya sedang mengerjakan implementasi yang berbeda tentang cara mencapai ini dan sebelum memposting pertanyaan di laravel.io saya menemukan ini! yay !!!
Gayan Hewa
Dan adakah cara untuk tidak menambahkannya ke json hanya untuk beberapa kasus?
Jordi Puigdellívol
3
Atribut pabean ini tampaknya tidak muncul saat memanggil model melalui suatu hubungan. (Mis: Model \ Perusahaan :: dengan ('orang')). Ada ide?
Andrew
@ JordiPuigdellívol Di Laravel 5, Anda dapat menggunakan protected $hidden = []untuk menambahkan kolom / pengakses yang telah dikecualikan.
junkystu
124

Hal terakhir pada halaman doc Laravel Eloquent adalah:

protected $appends = array('is_admin');

Itu dapat digunakan secara otomatis untuk menambahkan pengakses baru ke model tanpa pekerjaan tambahan seperti memodifikasi metode seperti ::toArray().

Cukup buat getFooBarAttribute(...)accessor dan tambahkan foo_barke $appendsarray.

trm42
sumber
4
Ah, menarik. Fitur ini telah ditambahkan ke Laravel sejak pertanyaan saya diposting ( github.com/laravel/framework/commit/… ). Ini adalah jawaban yang tepat untuk siapa saja yang menggunakan v4.08 atau lebih tinggi.
coatesap
3
Ini tidak akan tersedia untuk Anda jika Anda menggunakan hubungan untuk menghasilkan konten untuk pengakses Anda. Dalam hal ini, Anda mungkin harus menggunakan toArraymetode overriding .
gpmcadam
2
Sepertinya dokumentasi yang Anda maksud telah dipindahkan ke sini: https://laravel.com/docs/5.5/eloquent-serialization .
mibbler
40

Jika Anda mengubah nama Anda getAvailability()metode untuk getAvailableAttribute()metode Anda menjadi accessor dan Anda akan dapat membacanya menggunakan ->availablelangsung pada model Anda.

Documents: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators

EDIT: Karena atribut Anda adalah "virtual", itu tidak termasuk secara default dalam representasi JSON objek Anda.

Tapi saya menemukan ini: Accessors model kustom tidak diproses ketika -> toJson () dipanggil?

Untuk memaksa atribut Anda dikembalikan dalam array, tambahkan itu sebagai kunci ke array $ attributes.

class User extends Eloquent {
    protected $attributes = array(
        'ZipCode' => '',
    );

    public function getZipCodeAttribute()
    {
        return ....
    }
}

Saya tidak mengujinya, tetapi seharusnya cukup sepele bagi Anda untuk mencoba pengaturan Anda saat ini.

Alexandre Danault
sumber
Ini berfungsi sebanyak yang ->availabledisediakan pada $sessionobjek, tetapi karena $sessionsakan dikonversi langsung menjadi string JSON (itu bagian dari API), tidak ada kesempatan untuk menggunakan ini.
coatesap
Saya tidak yakin saya mengerti bagaimana barang-barang Anda bekerja. Jika EventSession::all()mengembalikan objek JSON dari API, Anda tidak benar-benar menggunakan model Laravel, bukan? Maaf, saya bingung tentang bagaimana Anda menerapkan model Anda.
Alexandre Danault
EventSession adalah objek Eloquent standar ( class EventSession extends Eloquent). ::all()hanya sebagai contoh. EventSession::find(170071)akan menjadi yang lain. Ini disebut di berbagai titik di seluruh SessionController ( SessionController extends \BaseController) yang akan dipanggil melalui URL seperti '/ sesi / 170071'.
coatesap
Saya pikir kuncinya mungkin terletak pada objek bawaan Eloquent untuk konversi JSON yang terjadi. Bahkan jika Anda menambahkan atribut khusus seperti public $availableke model, ini tidak ditampilkan ketika objek dikonversi.
coatesap
3
Ini tersedia sejak rilis Laravel 4.0.8 pada 8 Oktober 2013. Lihat dokumen resmi: laravel.com/docs/eloquent#converting-to-arrays-or-json (cari protected $appends = array('is_admin');)
Ronald Hulshof
23

Saya memiliki sesuatu yang simular: Saya memiliki gambar atribut dalam model saya, ini berisi lokasi file di folder Storage. Gambar harus dikembalikan base64 disandikan

//Add extra attribute
protected $attributes = ['picture_data'];

//Make it available in the json response
protected $appends = ['picture_data'];

//implement the attribute
public function getPictureDataAttribute()
{
    $file = Storage::get($this->picture);
    $type = Storage::mimeType($this->picture);
    return "data:" . $type . ";base64," . base64_encode($file);
}
Patrique
sumber
1
Seharusnya picture_data bukan pictureData di $ attribute & $ append. Dan Anda dapat melewatkan variabel $ atribut juga.
Madushan Perera
16

Anda dapat menggunakan setAttributefungsi dalam Model untuk menambahkan atribut khusus

jianfeng
sumber
9

Katakanlah Anda memiliki 2 kolom bernama first_name dan last_name di tabel pengguna Anda dan Anda ingin mengambil nama lengkap. Anda dapat mencapai dengan kode berikut:

class User extends Eloquent {


    public function getFullNameAttribute()
    {
        return $this->first_name.' '.$this->last_name;
    }
}

sekarang Anda bisa mendapatkan nama lengkap sebagai:

$user = User::find(1);
$user->full_name;
ShuBham GuPta
sumber
7

Langkah 1: Tentukan atribut pada $appends
Langkah 2: Tentukan accessor untuk atribut itu.
Contoh:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

    //define accessor
    public function getCoverAttribute()
    {
        return json_decode($this->InJson)->cover;
    }
Bedram Tamang
sumber
3

Dalam model langganan saya, saya perlu tahu langganan dijeda atau tidak. di sini adalah bagaimana saya melakukannya

public function getIsPausedAttribute() {
    $isPaused = false;
    if (!$this->is_active) {
        $isPaused = true;
    }
}

maka pada tampilan template, saya bisa menggunakan $subscription->is_pauseduntuk mendapatkan hasilnya.

The getIsPausedAttributeadalah format untuk mengatur atribut khusus,

dan gunakan is_pauseduntuk mendapatkan atau menggunakan atribut dalam tampilan Anda.

li bing zhao
sumber
2

dalam kasus saya, membuat kolom kosong dan mengatur pengaksesnya berfungsi dengan baik. accessor saya mengisi usia pengguna dari kolom dob. Fungsi toArray () berfungsi juga.

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
Hanif Rifa'i
sumber