เพิ่มแอตทริบิวต์ที่กำหนดเองลงในโมเดล Laravel / Eloquent ในการโหลดหรือไม่


219

ฉันต้องการที่จะเพิ่มคุณสมบัติ / คุณสมบัติที่กำหนดเองให้กับโมเดล Laravel / Eloquent เมื่อมีการโหลดคล้ายกับวิธีที่อาจทำได้ด้วยวิธีการของ RedBean $model->open()

ตัวอย่างเช่นในขณะนี้ในคอนโทรลเลอร์ของฉันฉันมี:

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

มันจะเป็นการดีถ้าคุณสามารถข้ามลูปและตั้งค่าแอตทริบิวต์ 'พร้อมใช้งาน' ไว้แล้วและมีประชากรอยู่แล้ว

ฉันได้ลองใช้แบบจำลองเหตุการณ์ที่อธิบายไว้ในเอกสารประกอบเพื่อแนบคุณสมบัตินี้เมื่อวัตถุโหลด แต่ไม่ประสบความสำเร็จ

หมายเหตุ:

  • 'ว่าง' ไม่ใช่เขตข้อมูลในตารางที่ขีดเส้นใต้
  • $sessionsกำลังถูกส่งคืนเป็นวัตถุ JSON ซึ่งเป็นส่วนหนึ่งของ API ดังนั้นการโทรหาสิ่งที่ต้องการ$session->available()ในเทมเพลตไม่ใช่ตัวเลือก

คำตอบ:


318

ปัญหาที่เกิดขึ้นมีสาเหตุมาจากความจริงที่ว่าModel's toArray()วิธีการละเว้น accessors ใด ๆ ที่ไม่เกี่ยวข้องโดยตรงกับคอลัมน์ในตารางต้นแบบ

เทย์เลอร์ออตเวลล์พูดถึงที่นี่ว่า "นี่เป็นความตั้งใจและเหตุผลด้านประสิทธิภาพ" อย่างไรก็ตามมีวิธีง่ายๆในการบรรลุเป้าหมายนี้:

class EventSession extends Eloquent {

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

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

แอททริบิวใด ๆ ที่ระบุไว้ในคุณสมบัติ $ ผนวกจะรวมอยู่ในรูปแบบอาร์เรย์หรือ JSON ของแบบจำลองโดยอัตโนมัติหากคุณเพิ่มผู้เข้าถึงที่เหมาะสม

คำตอบเก่า (สำหรับ Laravel เวอร์ชั่น <4.08):

ทางออกที่ดีที่สุดที่ฉันพบคือการแทนที่toArray()วิธีการและตั้งค่าคุณลักษณะอย่างชัดเจน:

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

}

หรือหากคุณมีอุปกรณ์เสริมที่กำหนดเองมากมายให้วนรอบพวกเขาทั้งหมดและนำไปใช้:

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

}

คำถามและคำตอบที่ดีที่สุดสำหรับวันนี้ ฉันทำงานเกี่ยวกับการใช้งานที่แตกต่างกันเกี่ยวกับวิธีการประสบความสำเร็จและก่อนโพสต์คำถามใน laravel.io ฉันพบสิ่งนี้! เย้ !!!
Gayan Hewa

และมีวิธีที่จะไม่เพิ่มลงใน json เพียงบางกรณีหรือไม่?
Jordi Puigdellívol

3
แอตทริบิวต์ศุลกากรเหล่านี้ดูเหมือนจะไม่ปรากฏเมื่อเรียกใช้โมเดลผ่านความสัมพันธ์ (เช่น: รุ่น \ Company :: ด้วย ('คน')) ความคิดใด ๆ
แอนดรู

@ JordiPuigdellívolใน Laravel 5 คุณสามารถใช้protected $hidden = []เพื่อเพิ่มคอลัมน์ / accessors เพื่อยกเว้น
junkystu

124

สิ่งสุดท้ายในหน้า doc Laravel ฝีปากคือ:

protected $appends = array('is_admin');

ที่สามารถนำมาใช้โดยอัตโนมัติเพื่อเพิ่ม accessors ใหม่กับรูปแบบโดยไม่ต้องทำงานใด ๆ ::toArray()เพิ่มเติมเช่นการปรับเปลี่ยนวิธีการเช่น

เพียงแค่สร้างgetFooBarAttribute(...)accessor และเพิ่มfoo_barไปที่ $appendsอาร์เรย์


4
อาน่าสนใจ เพิ่มคุณลักษณะนี้ใน Laravel แล้วตั้งแต่มีการโพสต์คำถามของฉัน ( github.com/laravel/framework/commit/ ...... ) นี่คือคำตอบที่ถูกต้องสำหรับทุกคนที่ใช้ v4.08 หรือสูงกว่า
coatesap

3
สิ่งนี้จะไม่สามารถใช้ได้กับคุณหากคุณใช้ความสัมพันธ์เพื่อสร้างเนื้อหาสำหรับตัวเข้าถึงของคุณ ในตัวอย่างนี้คุณอาจต้องใช้toArrayวิธีการแทนที่
gpmcadam

2
มันดูเหมือนกับว่าเอกสารที่คุณเรียกว่าได้ถูกย้ายไปที่นี่: https://laravel.com/docs/5.5/eloquent-serialization
mibbler

40

ถ้าคุณเปลี่ยนชื่อของคุณgetAvailability()วิธีการเพื่อให้getAvailableAttribute()วิธีการของคุณจะกลายเป็นเข้าถึงและคุณจะสามารถอ่านได้โดยใช้->availableตรงกับรุ่นของคุณ

เอกสาร: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators

แก้ไข: เนื่องจากแอตทริบิวต์ของคุณเป็น "เสมือน" จึงไม่รวมอยู่ในค่าเริ่มต้นในการแสดง JSON ของวัตถุของคุณ

แต่ฉันพบสิ่งนี้: ตัวเข้าถึงโมเดลที่กำหนดเองซึ่งไม่ได้ประมวลผลเมื่อ -> toJson () เรียกว่า?

เพื่อที่จะบังคับให้ส่งคืนแอททริบิวของคุณในอาเรย์ให้เพิ่มมันเป็นกุญแจสำคัญในอาเรย์ของ $ attribute

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

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

ฉันไม่ได้ทดสอบ แต่น่าจะเป็นเรื่องง่ายสำหรับคุณที่จะลองในการตั้งค่าปัจจุบันของคุณ


สิ่งนี้ทำงานได้มากเท่าที่->availableมีให้บน$sessionวัตถุ แต่เมื่อ$sessionsได้รับการแปลงเป็นสตริง JSON โดยตรง (เป็นส่วนหนึ่งของ API) จะไม่มีโอกาสใช้สิ่งนี้
coatesap

ฉันไม่แน่ใจว่าฉันเข้าใจวิธีการทำงานของคุณ หากEventSession::all()ส่งคืนออบเจ็กต์ JSON จาก API แสดงว่าคุณไม่ได้ใช้โมเดล Laravel จริงๆใช่ไหม ขออภัยฉันสับสนในวิธีที่คุณใช้โมเดลของคุณ
Alexandre Danault

EventSession เป็นวัตถุ Eloquent มาตรฐาน ( class EventSession extends Eloquent) ::all()เป็นเพียงตัวอย่าง EventSession::find(170071)จะเป็นอีก เหล่านี้ถูกเรียกในจุดต่าง ๆ ตลอด SessionController ( SessionController extends \BaseController) ซึ่งจะถูกเรียกผ่าน URL เช่น '/ times / 170071'
coatesap

ฉันคิดว่ากุญแจอาจอยู่ในวัตถุในตัวของ Eloquent เพื่อการแปลง JSON ที่เกิดขึ้น แม้ว่าคุณจะเพิ่มแอตทริบิวต์ที่กำหนดเองเช่นpublic $availableรูปแบบนี้จะไม่ปรากฏเมื่อวัตถุถูกแปลง
coatesap

3
สามารถใช้ได้ตั้งแต่เปิดตัว Laravel 4.0.8 ในวันที่ 8 ตุลาคม 2013 ดูเอกสารอย่างเป็นทางการ: laravel.com/docs/eloquent#converting-to-arrays-or-json (ค้นหาprotected $appends = array('is_admin');)
Ronald Hulshof

23

ฉันมีสิ่งที่คล้ายกัน: ฉันมีรูปภาพคุณลักษณะในโมเดลของฉันซึ่งมีตำแหน่งของไฟล์ในโฟลเดอร์ Storage ภาพจะต้องถูกส่งกลับ base64 เข้ารหัส

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

1
ควรเป็น picture_data ไม่ใช่ pictureData ใน $ attributes & $ appends และคุณสามารถข้ามตัวแปร $ attribute ได้เช่นกัน
Madushan Perera

16

คุณสามารถใช้setAttributeฟังก์ชั่นใน Model เพื่อเพิ่มคุณสมบัติที่กำหนดเอง


9

สมมติว่าคุณมี 2 คอลัมน์ชื่อ first_name และ last_name ในตารางผู้ใช้ของคุณและคุณต้องการดึงชื่อเต็ม คุณสามารถบรรลุด้วยรหัสต่อไปนี้:

class User extends Eloquent {


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

ตอนนี้คุณสามารถได้รับชื่อเต็มเป็น:

$user = User::find(1);
$user->full_name;

7

ขั้นตอนที่ 1: กำหนดแอตทริบิวต์ใน$appends
ขั้นตอนที่ 2: กำหนด accessor สำหรับแอตทริบิวต์นั้น
ตัวอย่าง:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

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

3

ในรูปแบบการสมัครของฉันฉันจำเป็นต้องรู้ว่าการสมัครสมาชิกถูกหยุดชั่วคราวหรือไม่ นี่คือวิธีที่ฉันทำ

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

จากนั้นในเทมเพลตมุมมองฉันสามารถใช้ $subscription->is_pausedเพื่อให้ได้ผลลัพธ์

getIsPausedAttributeเป็นรูปแบบในการตั้งค่าแอตทริบิวต์ที่กำหนดเอง,

และใช้is_pausedเพื่อรับหรือใช้แอตทริบิวต์ในมุมมองของคุณ


2

ในกรณีของฉันการสร้างคอลัมน์ว่างและการตั้งค่า accessor ทำงานได้ดี accessor ของฉันเติมอายุของผู้ใช้จากคอลัมน์ dob ฟังก์ชัน toArray () ทำงานด้วย

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.