Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast Laravel query results to class

When creating a query using the syntax DB::table('foo'), it creates a generic class (stdClass). Is there any way to cast the resulting rows to a specific class?

Here is some example code that should explain what I want to do:

$result = DB::table('foo')->get();
$converted = (Foo) $result; // Not going to work

I want to cast all the (stdClass) objects of the array to the Foo class.

like image 922
OskarD90 Avatar asked Aug 07 '15 08:08

OskarD90


3 Answers

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.

like image 109
dKen Avatar answered Nov 01 '22 22:11

dKen


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();
like image 26
Beefjeff Avatar answered Nov 02 '22 00:11

Beefjeff


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()
like image 1
Ben Swinburne Avatar answered Nov 02 '22 00:11

Ben Swinburne