Eloquent models comparation troubles - php

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).

Related

Laravel 8 Dynamic relationships

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.

Doctrine ORM ManyToMany query by Object using Criteria

I have this relations:
Exercise - Muscle
Every Exercies can be related to multiple muscles and every muscle can be related to many muscles.
So they are defined as a ManyToMany relation.
Now i want to query all the muscles related to a particular exercies.
I tried something like this:
$muscle = $muscleRepository->find(9);
$c = new Criteria();
$c->where(Criteria::expr()->in('muscles', [$muscle]));
$res = $er->matching($c);
dd($res->toArray());
But this gives me a notice "Notice: Trying to access array offset on value of type null".
The notices appears whenever I try to do something with the LazyLoadCollection that comes from the ->matching call.
I've tried var_dumping and echoing things but the result is the same, so dd is not to be blamed.
I also tried contains and memberOf operators but with no luck.
Now I got things working writing an ugly workaround for this which involves removing the criteria and applying a ->filter function on the LazyLoadCollection but I would really like to handle this using Criteria class which seems to be made for this specific purpose.
Any idea?
Thanks

F3: Is there a way to convert an array into an object with additional fields?

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.

how to get collection results as array with ids

it should be simple but I am missing something,
lets say this simple eloquent:
Post::select('id')->take(5)->get();
I want to get simple array with the results id's so it will look like this:
[1,2,3,4,5]
but i am getting something like this:
[{"id":"1"},{"id":"2"},{"id":"3"},{"id":"4"},{"id":"5"}]
flatten() not working and I am getting the same results:
Post::select('id')->take(5)->get()->flatten();
http://laravel.com/docs/master/collections#method-flatten
The flatten
method flattens a multi-dimensional collection into a single
dimension:
what i am missing? I remember there is a short line laravel way of getting this results without iterate through the array and create a new one
just got it, its the lists() that do the magic so the answer is:
Post::select('id')->take(5)->lists('id');
Update:
as of laravel 5.2 lists() become deprecated
The lists method on the Collection, query builder and Eloquent query
builder objects has been renamed to pluck. The method signature
remains the same.
the new method name is pluck which work the same:
Post::select('id')->take(5)->pluck('id');

Laravel - Single query then splitting Vs Two queries

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.

Categories