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.
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.
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
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();
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With