I'm using Laravel 8. I have relationships strings but I don't how to load the relationships inside a loop. I'd like to explode() my relationships on the dot and load each relationship to get the column
$userPostTitle = 'post.title';
$userAvatarFilename = 'profil.avatar.filename';
The idea is to use explode() on the dot explode('.', $userPostTitle) and add bracket object dynamically for each relationship :
// for the userPostTitle we need two level
$postTitle = $user->{explode[0]}->{explode[1]};
// for the userAvatarFilename we need three level
$avatarFilename = $user->{explode[0]}->{explode[1]}->{explode[2]};
How it's possible to add dynamically the exploded relationship ? {rel1}->{rel2}->{rel3} etc... Maybe there is a better solution than using explode()
You have to keep in mind that your solution possibly does a lot of queries if you haven't preloaded the relations beforehand. However I think you can use the Laravel helper object_get() for your problem:
$relation = 'profile.avatar.filename';
$avatarFilename = object_get($user, $relation);
// Which roughly translates to calling `$user->profile->avatar->filename`
It also accepts a third parameter which is a default value if the property turns out to be null.
Related
I'm building timeline for my app and got this issue, that my collection returns me only 11 values.
I have tried to change merged data and it always returns me 11 values that makes me very confused, bc their is no limits in my code.
public function timeline($company_id)
{
// USER COMPANY
$company = auth()->user()->companies()->findOrFail($company_id);
//GETTING DATA TO MERGE COLLECTION
$equities = Share::where('company_id', $company_id)->get();
$equityGrants = EquityGrant::whereIn('share_id', $equities->pluck('id')->toArray())->get();
$warrants = Warrant::where('company_id', $company_id)->orderBy('created_at', 'DESC')->get();
$warrantGtrants = WarrantGrant::whereIn('warrant_id', $warrants->pluck('id')->toArray())->get();
$convertibles = Convertible::where('company_id', $company_id)->get();
// CREATING COLLECTION
$operations = $equities->merge($equityGrants)->merge($warrants)->merge($warrantGtrants)->merge($convertibles);
$operations = $operations->sortByDesc('created_at');
// RETURNS ME ONLY 11 VALUES
return $operations->values()->all();
}
I tried to merge() less instances, like $operations = $equities->merge($equityGrants)->merge($warrants)->merge($warrantGtrants) but always maximum 11 values. I need to return all data for my timeline.
HELP ME Please! :)
Cheers, love :)
SOLUTION:
function concat() instead of merge() fixed the problem.
Using merge() on collections in Laravel will cause overiding all elements with the same ids comming from Eloquent query.
I believe that is why you are getting only 11 elements because this is the count off all elements having distinct ids.
According to the documentation of merge, when a key matches, it will be overwritten by the last item with that key (the collection you are merging in).
A solution to your problem would be the keyBy method (documentation). If you use keyBy('created_at'), it will give you an array where the keys are the created_at timestamps.
Although it sounds like they would be unique, there is a great change that some of the linked resources (like warrant and warrentGrants) are created at the exact same moment. If that is the case, you should try to find another key or something that is unique across all resources.
SOLUTION: function concat() instead of merge() fixed the problem.
I have some difficulties with eloquent models in use-case like that:
$skill1 = new Skill();
$skill1->title = "Test";
$skill1->save();
$skill3 = Skill::findOrFail($skill1->id);
$this->assertEquals($skill1, $skill3);
Asserting gives error, because objects are not equals
Comparasion result here
In case we getting object from relation there are lots of new fields (e.g. original->relation)
I know, that there is "is" method in Eloquent models and it works right, but if I need to use core PHP function like in_array (for example, it is using in Collection's "contains" method), comparasion will not work.
The question is "How to compare these two models and use it like that:"
$this->assertTrue(collect([$skill1])->contains($skill3));
Maybe I can overload compare operator, maybe I can make smthng like IComparable in C#?
Thanks.
Compare the attributes, not the whole models:
$this->assertEquals($skill1->getAttributes(), $skill3->getAttributes());
If you compare a newly created model with a model from the database, $wasRecentlyCreated is different (among other things).
I am trying to append an Eloquent collection with another Eloquent collection in Laravel 5.3.
This is what I've done so far:
$entries = Entry::all();
$posts = Post::all();
$entries->merge($posts);
I tried to use merge() as shown in the code above, but it seems that I'm stuck with this kind of problem (since some of them have the same id with the same value):
Collection merge eating up some rows
Any ideas?
For versions < 5.4 you can merge the two eloquent collections by resetting keys with toBase like this:
$mergedCollection = $entries->toBase()->merge($posts);
For versions >= 5.4 you can use concat as suggested by Jason.
I believe you may be looking for concat(). This will append one container to the end of another container, regardless of the keys of either.
$mergedCollection = $entries->concat($posts);
Here's the link: laravel7.X
$collection = collect(['Desk', 'Chair']);
$merged = $collection->merge(['Bookcase', 'Door']);
$merged->all();
if you need to merge big_products and small_products:
$products = $bigProducts->values()->merge($smallProducts->values());
The merge method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection.
But
If the given items's keys are numeric, the values will be appended to the end of the collection:
Thus, all you need is to get rid of keys and you can do it with ->values() function.
Tested on Laravel-6
The merge() method receives an array, so you have to do something like
$entries->merge($posts->toArray());
Laravel Collections: merge() method
I am using Fat Free PHP to return a query using a join. Because of this the results have extra fields from another table. I want to be able to convert the array to objects (ie arrayToObjects) but I want the additional fields to persist. I would also like this to return an instance of my class, not an stdClass.
I tried adding the additional fields to the php model but it loses the values when I pass the mysql result into arrayToObjects().
Is this achievable?
This is a simple but dirty trick I learned some time ago.
// Create an array
$array = range('a', 'z');
// Convert array to object
$object = json_decode(json_encode($array));
And voila! An object with all the values you want it to have. Nothing fancy about it.
I am not huge fan of my eventual solution but I added the values manually to the object from the array.
For example:
object = new Object();
object->id = array['id']
object->name = array['name']
You could add a custom setter and getter method to you model, so it can load and save your extra arrays/objects. That's probably the easiest way for you now.
You can also try to use the F3 cortex orm, which has support for relations built in.
I am using laravel framework and I need to get 2 arrays, one with premium themes and one with free themes.. so I would do:
$premium_themes = \App\Theme::where('premium', '=', '1')->get();
$free_themes = \App\Theme::where('premium', '=', '0')->get();
This will work Ok, but will perform two queries on the database. Since I'm an optimization geek, I think it might be better to have a single query... which I would get all themes by using:
$themes = \App\Theme::all();
And then I'd to process this in php to split based on the theme premium property.
So I have 2 questions:
1) A single query is better than 2 queries in this case, or am I over-thinking this?
2) Is there a fast simple way to split the resulting collection into two collections based on the premium property? (I know Laravel has many shortcuts but I'm still new to the framework)
Single query would be better as both of the queries will go over all the rows in the database. Except the 2 queries to split them will go over them for a second time.
You can simply filter them like so;
The simple one line solution $themes = \App\Theme::all()->groupBy('premium');.
Or into separate collections if you need to filter by another element etc just add more to the following;
$themes = \App\Theme::all();
$premium = new Collection;
$free = new Collection;
$themes->each(function ($item) use ($premium, $free){
if($item->premium == '1'){
$premium->push($item);
}
else {
$free->push($item);
}
});
And your items will be filtered into the relevant Collection. Be sure you use the Collection class at the top.
The only reason I can think to keep it as separate queries would be if you need to paginate the data - not something you can do easily if its all mixed together.
A "short cut" would be to use the collection filter() method, I put short cut in quotes because it's not short per-se, more syntatic sugar - but Larvel is nothing if not full of sugar so why not?
Code would look something like this:
$allThemes = \App\Theme::all();
$premiumThemes = $allThemes->filter(function($theme)
{
return $theme->premium;
});
$freeThemes = $allThemes->filter(function($theme)
{
return !$theme->premium;
});
Edit: I'd recommend using Matt Burrow's answer, but I'll leave mine here as the solution is different.