Thêm một thuộc tính tùy chỉnh cho một mô hình Laravel / Eloquent khi tải?


219

Tôi muốn có thể thêm thuộc tính / thuộc tính tùy chỉnh vào mô hình Laravel / Eloquent khi được tải, tương tự như cách có thể đạt được với phương thức của RedBean $model->open() .

Ví dụ, tại thời điểm này, trong bộ điều khiển của tôi, tôi có:

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

Sẽ thật tuyệt nếu có thể bỏ qua vòng lặp và có thuộc tính 'có sẵn' đã được đặt và được điền.

Tôi đã thử sử dụng một số sự kiện mô hình được mô tả trong tài liệu để đính kèm thuộc tính này khi đối tượng tải, nhưng không thành công cho đến nay.

Ghi chú:

  • 'có sẵn' không phải là một trường trong bảng bên dưới.
  • $sessionsđang được trả về dưới dạng đối tượng JSON như một phần của API và do đó, việc gọi một cái gì đó như $session->available()trong mẫu không phải là một tùy chọn

Câu trả lời:


316

Vấn đề được gây ra bởi thực tế ModeltoArray()phương pháp của nó bỏ qua mọi bộ truy cập không liên quan trực tiếp đến một cột trong bảng bên dưới.

Như Taylor Otwell đã đề cập ở đây , "Đây là cố ý và vì lý do hiệu suất." Tuy nhiên, có một cách dễ dàng để đạt được điều này:

class EventSession extends Eloquent {

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

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

Bất kỳ thuộc tính nào được liệt kê trong thuộc tính $ appends sẽ tự động được bao gồm trong dạng mảng hoặc dạng JSON của mô hình, miễn là bạn đã thêm trình truy cập phù hợp.

Câu trả lời cũ (đối với phiên bản Laravel <4.08):

Giải pháp tốt nhất mà tôi đã tìm thấy là ghi đè toArray()phương thức và giải thích đặt thuộc tính:

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

}

hoặc, nếu bạn có nhiều bộ truy cập tùy chỉnh, hãy lặp qua tất cả chúng và áp dụng chúng:

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

}

Câu hỏi và câu trả lời tốt nhất cho ngày hôm nay. Tôi đã làm việc trên các triển khai khác nhau về cách đạt được điều này và ngay trước khi đăng câu hỏi lên laravel.io tôi đã tìm thấy điều này! vâng
Gayan Hewa

Và có cách nào để không thêm chúng vào json chỉ trong một số trường hợp không?
Jordi Puigdellívol

3
Các thuộc tính hải quan này dường như không xuất hiện khi gọi mô hình thông qua mối quan hệ. (Ví dụ: Model \ Company :: with ('people')). Bất kỳ ý tưởng?
Andrew

@ JordiPuigdellívol Trong Laravel 5, bạn có thể sử dụng protected $hidden = []để thêm các cột / bộ truy cập để loại trừ.
rác rưởi

124

Điều cuối cùng trên trang tài liệu Eloquent của Laravel là:

protected $appends = array('is_admin');

Điều đó có thể được sử dụng tự động để thêm các bộ truy cập mới vào mô hình mà không cần bất kỳ công việc bổ sung nào như sửa đổi các phương thức như ::toArray().

Chỉ cần tạo getFooBarAttribute(...)accessor và thêm foo_barvào $appendsmảng.


4
Ah thú vị. Tính năng này đã được thêm vào Laravel kể từ khi câu hỏi của tôi được đăng ( github.com/laravel/framework/commit/ mẹo ). Đây là câu trả lời đúng cho bất cứ ai sử dụng v4.08 trở lên.
coatesap

3
Điều này sẽ không có sẵn cho bạn nếu bạn đang sử dụng các mối quan hệ để tạo nội dung cho người truy cập của bạn. Trong trường hợp này, bạn có thể phải dùng đến toArrayphương thức ghi đè phương thức.
gpmcadam

2
Dường như tài liệu mà bạn đề cập đã được chuyển đến đây: https://laravel.com/docs/5.5/eloquent-serialization .
mibbler

40

Nếu bạn đổi tên của bạn getAvailability()phương pháp để getAvailableAttribute()phương pháp của bạn sẽ trở thành một accessor và bạn sẽ có thể đọc nó bằng cách sử ->availablethẳng vào mô hình của bạn.

Tài liệu: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators

EDIT: Vì thuộc tính của bạn là "ảo", mặc định nó không được bao gồm trong biểu diễn JSON của đối tượng của bạn.

Nhưng tôi đã tìm thấy điều này: Bộ truy cập mô hình tùy chỉnh không được xử lý khi -> toJson () được gọi?

Để buộc thuộc tính của bạn được trả về trong mảng, hãy thêm nó làm khóa cho mảng thuộc tính $.

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

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

Tôi đã không kiểm tra nó, nhưng sẽ khá đơn giản để bạn thử trong thiết lập hiện tại của bạn.


Điều này hoạt động nhiều như nó ->availablecó sẵn trên $sessionđối tượng, nhưng khi $sessionsđược chuyển đổi trực tiếp thành chuỗi JSON (nó là một phần của API), sẽ không có cơ hội sử dụng điều này.
coatesap

Tôi không chắc tôi hiểu cách thức hoạt động của bạn. Nếu EventSession::all()trả về một đối tượng JSON từ API, bạn không thực sự sử dụng mô hình Laravel, phải không? Xin lỗi, tôi bối rối về cách bạn triển khai mô hình của bạn.
Alexandre Danault

EventSession là một đối tượng Eloquent tiêu chuẩn ( class EventSession extends Eloquent). ::all()chỉ là một ví dụ. EventSession::find(170071)sẽ là một người khác Chúng được gọi tại nhiều điểm khác nhau trong suốt Trình điều khiển phiên ( SessionController extends \BaseController) sẽ được gọi thông qua các URL như '/ session / 170071'.
coatesap

Tôi nghĩ rằng khóa có thể nằm trong đối tượng tích hợp sẵn của Eloquent để chuyển đổi JSON diễn ra. Ngay cả khi bạn thêm một thuộc tính tùy chỉnh, chẳng hạn như public $availablemô hình, điều này không được hiển thị khi đối tượng được chuyển đổi.
coatesap

3
Điều này có sẵn kể từ khi phát hành Laravel 4.0.8 vào ngày 8 tháng 10 năm 2013. Xem tài liệu chính thức: laravel.com/docs/eloquent#converting-to-arrays-or-json (tìm kiếm protected $appends = array('is_admin');)
Ronald Hulshof

23

Tôi đã có một cái gì đó tương tự: Tôi có một hình ảnh thuộc tính trong mô hình của tôi, cái này chứa vị trí của tệp trong thư mục Storage. Hình ảnh phải được trả về mã hóa 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
Nó phải là image_data chứ không phải imageData trong các thuộc tính $ & $ nối thêm. Và bạn cũng có thể bỏ qua biến thuộc tính $.
Madushan Perera

16

bạn có thể sử dụng setAttributehàm trong Model để thêm thuộc tính tùy chỉnh


9

Giả sử bạn có 2 cột có tên First_name và last_name trong bảng người dùng của bạn và bạn muốn lấy tên đầy đủ. bạn có thể đạt được với đoạn mã sau:

class User extends Eloquent {


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

bây giờ bạn có thể lấy tên đầy đủ là:

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

7

Bước 1: Xác định các thuộc tính trong $appends
Bước 2: Xác định trình truy cập cho các thuộc tính đó.
Thí dụ:

<?php
...

class Movie extends Model{

    protected $appends = ['cover'];

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

3

Trong mô hình đăng ký của tôi, tôi cần biết đăng ký có bị tạm dừng hay không. đây là cách tôi đã làm

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

sau đó trong mẫu xem, tôi có thể sử dụng $subscription->is_pausedđể có kết quả.

Các getIsPausedAttributelà định dạng để thiết lập một thuộc tính tùy chỉnh,

và sử dụng is_pausedđể có được hoặc sử dụng thuộc tính trong chế độ xem của bạn.


2

trong trường hợp của tôi, việc tạo một cột trống và thiết lập trình truy cập của nó hoạt động tốt. người truy cập của tôi điền tuổi người dùng từ cột dob. Hàm toArray () cũng hoạt động.

public function getAgeAttribute()
{
  return Carbon::createFromFormat('Y-m-d', $this->attributes['dateofbirth'])->age;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.