Laravel: How to get Models out of query results? - php

Suppose I have a query that, among other things, returns user id's, this query was built using DB::table()... rather than using the models, so, as a result, I got a collection with arrays for each retrieved row, something like this:
user_id | calculated_data
--------+----------------
1 | 123
2 | 111
3 | 222
... | ...
Supose I store this collection on a $data variable, of course if I do a foreach ($data as $d) { $d->user_id ... } will work.
But I want this query to return something more like what the ORM does, so instead user_ids, return User models so I can do, for example, a $data->user->name
Can this be even done? if so, how?

You can use the hydrate() function, it accepts an array of stdClass objects (or even associative arrays AFAIR) as input and returns a collection of Eloquent Models, so you can do things like this:
$result = DB::table('users')->take(10)->get();
$users = App\User::hydrate($result->all());
You can even get a collection of Eloquent Models directly from a RAW query with the fromQuery() function, i.e.:
$users = App\User::fromQuery('SELECT * FROM users WHERE id > ?', [2])
Update: If in your collection you don't have all the fields to hydrate a model, you can preload all the users you need with one query and modify your collection, i.e.:
$users = App\User::find($data->pluck('user_id'));
$data->transform(function($item) use($users) {
$item->user = $users->where('id', $item->user_id)->first()
return $item;
});

You need to use Eloquent to call value the "ORM way" I did not find anything related to the query builder in relation to ORM.
You could do something like this:
$flights = App\Flight::all();
foreach ($flights as $flight) {
echo $flight->name;
}
And in the ORM way you would get the user like this:
foreach ($flights as $flight) {
echo $flight->user->name;
}
Of course you would need to setup the correct relations.

// initial query will return collection of objects
$query = DB::table('user_something')
->join('users', 'users.id', 'user_something.user_id');
// You could cast it to the model query, by using fromSub.
// Make sure to alias a subquery same as the model's name,
// otherwise it would not be able to parse models data
User::fromSub($query, 'users');
// The most robust way is to get table name from the model
User::fromSub($query, User::make()->getTable());

Related

How I can avoid using `array_map` on my results returning primary keys in Laravel's 5.7 database layer?

I have the following table named mytable:
id SERIAL PK
namae VARCHAR
And using laravel 5.7 I need to retrieve the data using the following code (tested in a tinker session):
$yakuzaNames = DB::select('SELECT id from mytable where name like 'omaewa%');
dump($yakuzaNames);
The problem is that once data retrieved in variable $yakuzaNames is in the following format:
[ { id: 1},{id:2},...]
From the results I need to retrieve an array containing integers with the id, therefore I need to manipulate it via array_map:
$yakuzaNames = DB::select('SELECT id from mytable where name like 'omaewa%');
$yakuzaNames = array_map(function($item){ return $item->id },$yakuzaNames);
Or use a foreach loop:
$yakuzaNames = DB::select('SELECT id from mytable where name like 'omaewa%');
$names = [];
foreach($yakuzaNames as $yakuzaName){
$names[] = $yakuzaName->id;
}
But using a loop seems kinda a waste also using some sort of iteration seems waste as well. Is there a way for laravel's database layer be able to return directly the data in the format I want to?
Use pluck method for collection. https://laravel.com/docs/8.x/collections#method-pluck

how to make an eloquent result into an array Laravel

I want to combine two data search results into one array, I use array_merge but there is an array_merge() error:
Argument # 1 is not an array
How to turn $vendor's eloquent results into an array and combine it with $plucked?
$vendor = Vendor::find($id);
$vendor_detail = VendorDetail::where('vendor_id',$id)->get();
$plucked = $vendor_detail->pluck('vendor_profile_value','vendor_profile_name');
$coba = array_merge($vendor,$plucked);
$plucked already an array
I think the problem here is that $vendor is not yet an array
You could do it like this:
$vendor = Vendor::find($id);
$vendor_details = VendorDetail
::select('vendor_profile_value', 'vendor_profile_name')
->where('vendor_id', $id)
->get()
->toArray();
$coba = array_merge($vendor,$vendor_details);
The get() method execute the query returning a Collection instance, in which you can call the toArray() method.
Side note
As far as I can see, you could make use of relationships and eager loading.
If you have a one-to-many relationship defined like this in your Vendor model:
public function details()
{
return $this->hasMany(VendorDetails::class);
}
Then, you could eager load the relationship like this:
$vendor = Vendor::with('details')->find($id);
// ^^^^^^^^^^^^^^
You could even just load the wanted fields:
$vendor = Vendor::with('details:vendor_profile_value,vendor_profile_name')
->find($id);
Then, your object will have a new attribute called "details" containing the related objects (or a collection of the limited selected fields).
You can convert the $vendor to an Array like below.
$vendor = Vendor::find($id)->toArray();

Model return empty array when use select in model (Laravel)

I have a hasOne(Many) relation function like this:
return $this->hasOne('App\Models\ProductTranslation','product_id','id')->where('language_id', $language_id['id']);
Also, I tried to use
return $this->hasOne('App\Models\ProductTranslation','product_id','id')->select('product_translations.name')->where('language_id', '1');
In Controller use this
$value=Product::with('translation:name')->find(1);
I try to receive only one specific column from the table, but it returned an empty array. In debug bar I see the query when I use it in PHPMyAdmin it return only one column as I want.
Is this possible?
P.S (use Laravel 5.8.34)
Updating
I choose 'Entity Layers for Translated Fields and Non-Translated Fields' approach for translation in the project and have a database like this Database picture
If you want to get only that language_id type of translation then you might do like this.
$language_id = 1;
$products = Product::with(array('translation'=>function($query) use($language_id){
$query->where('language_id',$language_id);
}))->get();
and if you want to select name then like this
make sure you've to select id,name id is a must.
$language_id = 1;
$products = Product::with(array('translation'=>function($query) use($language_id){
$query->where('language_id',$language_id)->select('id','name');
}))->get();
Remove where conditions from models.
As per your DB structure in Language it's belongsToMany with role
Languages.php model
public function role(){
returh $this->belongsToMany('App\Models\Role','role_translations','language_id','role_id')
}
$language_id = 1;
$products = Product::with(array('translation.role'=>function($query) use($language_id){
$query->where('role_translations.language_id',$language_id)->select('languages.id','languages.name');
}))->get();

Eloquent order relationship results

I have a simple eloquent query and want to include another table with my results, however, the order of relationship results is incorrect.
Is it possible to order the results without using an SQLRAW statement
$groups = AttributeGroup::with('attribute')->where('page_id', $page->id)->get();
What I would like -
$groups = AttributeGroup::with('attribute')->orderBy('iteration', 'DESC')->where('page_id', $page->id)->get();
I get the error of Unknown column because this column is part of relationship table.
This will order each attribute relation of every attribute group result:
$groups = AttributeGroup::with(['attribute' => function ($query) {
$query->orderBy('iteration', 'DESC');
}])->where('page_id', $page->id)->get();
Is this what you want to achieve?
You can use closures to change the query when using with and has.
$groups = AttributeGroup::with(['attribute' => function($query){
$query->orderBy('iteration');
})->where('page_id', $page->id)->get();
Details are available on https://laravel.com/docs/5.6/eloquent-relationships#constraining-eager-loads

Laravel 5.2: Is it possible to use where clause condition in eloquent object

Fetched the initial data by joining the other table
$results = Model::join('joining other table')
->where('initial condition')
->limit(100)->get();
Now, i need to filter the data by some additional condition.
$new = $results->where('column',value); // Additional Condition
I had tried this but it returns empty collection, even though persist in the $results collection. Is it possible to use the where condition in later part ?.
You can use model relationships, which is very powerful. If you define your relationships between the different models you can call:
User::with(['posts' => function($builder){
$builder->where('published', true) // this is on the relationship (posts)
}])->get();
You can also do the following:
Post::whereHas(['user' => function($builder){
$builder->where('confirmed', true);
}])->get();
That will only return the posts where the associated user is confirmed...
Have you tried:
Model::join('joining other table')
->where('initial condition')
->where('column',value)
->limit(100)->get();

Categories