Cast Laravel query results to class

Yes, you can hydrate the results into the classes you want. I found the answer buried deep in a mess of half-answers and confused questions that make up the terrible Laracasts.com forum. Thanks for asking the question here instead of there.

Once you get the results, hydrate them using your model class:

$result = DB::table('foo')->get();
$converted = Foo::hydrate($result);

Edit: Found some documentation on the hydrate method too, if that helps

Edit 2: I found myself in a situation where I needed to cast results from either an array or a collection, depending on the results of a query. When a collection was returned, it was correctly hydrated, but when the result was an array, they were just stdClass. I wrote a quick method added to my master model that took a collection of arrays or objects, or a pagination object, and correctly cast it to the object I wanted.


I've found this page searching for a solution to re-cast a Laravel query to a specific model. In Laravel 7/8 it is possible to cast the result of a query builder to a specific Laravel model which you can then use with eloquent.

You can the table method provided by the DB facade to begin a query:

$query = DB::table('bar');
// You can add table joins, where statements, etc.

and then cast the above query to Foo models using:

$builder = \App\Foo::select('foo.*');
$builder->setQuery($query);

If you use joins etc, you may need to include these on the select. Note that to ensure that foo's id is in the id attribute it needs to be included last.

$builder = \App\Foo::select(['baz.*', 'foo.*']);

Retrieve the results as a collection of \App\Foo Eloquent models as normal.

$foos = $builder->get();

Typically you'd achieve this by setting the PDO Statement fetch_style to PDO::FETCH_CLASS as below

$statement->fetchAll(PDO::FETCH_CLASS, "App\User");

If you look at the method Illuminate\Database\Connection::select you'll see that whilst you can set the fetch_style/fetchMode, you can not the second argument.

public function select($query, $bindings = array(), $useReadPdo = true)
{
    return $this->run($query, $bindings, function($me, $query, $bindings) use ($useReadPdo)
    {
        if ($me->pretending()) return array();

        // For select statements, we'll simply execute the query and return an array
        // of the database result set. Each element in the array will be a single
        // row from the database table, and will either be an array or objects.
        $statement = $this->getPdoForSelect($useReadPdo)->prepare($query);

        $statement->execute($me->prepareBindings($bindings));

        return $statement->fetchAll($me->getFetchMode());
    });
}

Nor can you get access to the statement before fetchAll is called to call PDOStatement::setFetchMode for example.

You could perhaps attempt to extend Illuminate\Database\Connection and utilise that throughout other Database related classes by extending and replacing where necessary but it seems like a hefty task to maintain.

The other option is to use Eloquent which will give you classes back of a particular type but you get the slight additional overhead of hydrating the model objects.

class Foo extends Illuminate\Database\Eloquent\Model {
    protected $table = 'foo';
}

Foo::all()
Foo::where('col', 1)->get()

As of laravel 5.4

Eloquent models dont have the hydrate() method.

This has been moved to the Eloquent Builder class.

https://laravel.com/api/5.4/search.html?search=hydrate

The Eloquent Builder class requires a few things to get this working manually

  1. A query builder to initialize
  2. A model to hydrate with.

Here is my working example in some laravel 5.8 code:

            $instance = new $class;
            $table = $instance->getTable();

            
            $eloquent_builder = new \Illuminate\Database\Eloquent\Builder(
                // the Query Builder!
                DB::connection($connection_name)
                ->table($table)
                ->select($columns)
                ->orderBy($order_by, $order)
            );
            

            // Tell Eloquent what you're using to hydrate the query
            $eloquent_builder->setModel($instance);


            return $eloquent_builder->get();