skip to Main Content

In Laravel Eloquent, I am using select to get specific columns only in a collection:

$tickets = Tickets
           ::select('id', 'title', 'date') // so only these columns
           ->limit(100)
           ->get();

but I also have a custom attribute in the "Ticket" model that uses different columns:

public function getStatusAttribute() {
    if ($this->payment == $this->total & $this->active) {
        return "paid";
    } else {
        return "unpaid";
    }
}

then when doing

foreach ($tickets as $ticket) {
    echo $ticket->id 
    . $ticket->title
    . $ticket->date
    . $ticket->status // <- custom attribute
    ;
}

as I tell query to only select ‘id’, ‘title’, ‘date’, at what point Laravel gets additional columns from database (specifically the ‘payment’, ‘total’, ‘active’)? Does it runs additional queries for each row?

I only see main query (without columns from custom attribute being selected), when trying to log them either with dump(DB::getQueryLog()); or directly in MySQL:

SET GLOBAL log_output = "FILE";
SET GLOBAL general_log_file = "c:/AAA/logfile.log";
SET GLOBAL general_log = 'ON';

So I do not understand at what point Laravel gets additional columns from the database? My concern is to have 1 query and not 100.

2

Answers


  1. hope my solution solves your problem.
    To ensure the status attribute works correctly, modify your query to include the columns needed for the accessor:y and avoid additional queries.

    $tickets = Tickets::select('id', 'title', 'date', 'payment', 'total', 'active')
        ->limit(100)
        ->get();
    

    This way, all the necessary data for the custom attribute is fetched in the initial query.

    Thanks

    Login or Signup to reply.
  2. The status attribute is never queried from the DB. The value is calculated after the query is run when you request the value using $ticket->status or when the instance of the model is serialised.

    If you have retrieved your instance using your query builder code:

        Tickets::select('id', 'title', 'date') // so only these columns
           ->limit(100)
           ->get();
    

    Then I would expect your getStatusAttribute to return "paid" but you need to understand why:

    The query will result in an instance where

    $this->payment is NULL

    $this->total is NULL

    and $this->active is NULL

    Because you’re not getting those values from the database.

    When you call $this->payment == $this->total & $this->active
    You’re saying NULL == NULL & NULL
    Note: Be careful with your logic here as NULL & NULL will evaluate to 0 whereas NULL && NULL will evaluate to false

    Therefore you need to either be more defensive in your getStatusAttribute logic. You could consider returning NULL if the required columns are all NULL but you probably want to make sure those values are always queried.

    $tickets = Tickets::select('id', 'title', 'date', 'payment', 'total', 'active')
        ->limit(100)
        ->get();
    

    The above would do the job but the overhead of querying columns you don’t explicitly need is negligible so you could just drop the select entirely and use:

    $tickets = Tickets::limit(100)
        ->get();
    

    If there are values in there you want to prevent the model showing when serialised I would recommend using the Eloquent Model’s "hidden" property

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search