How to detach on many element with Laravel Eloquant - php

I have a relationship many to many with the table Screen and Media with a pivot table Media_Screen.
I want to remove the data with the screens' id in $id_screens from the pivot table.
I did like this :
$id_screens = [4,5];
$screens = App\Screen::whereIn('id', $id_screens)->get();
foreach($screens as $screen)
{
$screen->medias()->detach();
}
It works but I'm wondering if there is not a better way to do? I tried something like this but it didn't work :
$id_screens = [4,5];
$screens = App\Screen::whereIn('id', $id_screens)->get();
$screens->medias()->detach();

You can mass detach the relationship by querying on pivot table. It'll reduces number of queries you do in the loop. In your case, you can use below code for example.
$screens_id = App\Screen::whereIn('id', $id_screens)
->select('id')
->get()
->pluck('id');
\DB::table('Media_Screen')->whereIn('id_screen', $screens_id)->delete();

You may try "high order messages" as documented here:
$id_screens = [4,5];
$screens = App\Screen::find($id_screens);
$screens->each->medias()->detach();

Related

Get comment sub-comments when only one sub-comment

I have comments table where has parent_id
This is Comment table sub_comments relation.
public function sub_comments()
{
return $this->hasMany(self::class, 'parent_id');
}
This code return all comments with related all sub-comments
Comment::with('sub_comments')->get();
But I want to get all comments also sub-comments when sub-comments is single. That mean if comment have 2 or more comments for that comment I did not want get that sub-comments.
Now I use this code
$oneSubcommentCommentIds = Comment::has('sub_comments', '=', 1)->pluck('id');
Comment::with([
'sub_comments' => function ($q) use ($oneSubcommentCommentIds) {
$q->whereIn('parent_id', $oneSubcommentCommentIds);
}
])->get();
but this make one additional query.
Try this:
Comment::with('sub_comments')->has('sub_comments', '=', 1)->get();
Update
Your question wasn't clear, I can't imagine another way to doing this without previosly loaded the relationship or the count of the relationship.. so I'd do this:
// First get all your comments with an aditional count field
$comments = Comments::withCount('sub_comments')->get();
// separate the ones with just one sub_comment from the rest
list($oneSubComment, $theRest) = $collection->partition(function ($comment) {
return $comment->sub_comments_count == 1;
});
// Then load the relationship on just the selected elements
$oneSubComment->load('sub_comments');
// re-join the collection
$comments = $oneSubComment->union($theRest);
What am I doing here?
Adding an additional field to each $comment with the relationship count (it should be something like sub_comments_count)
Partition the resulting collection in two parts: the ones with one comment and the rest. Using the partition() method.
Lazy eager loading the collection.
Re-joining the two collections using the union() method.

Query Laravel Eloquent many to many where all id's are equal

I making a project based on Laravel and have the tables: companies, attributes, and attribute_company related as Many To Many relation when attribute_company use as a pivot table to connect companies and attributes tables.
I get an array of attribute_id's from the client and I need to get results of the companies that has the whole attributes exactly.
The only solution I found is to query whereHas combined with whereIn inside like this:
Company::whereHas('attributes', function (Builder $query) use ($atts_ids) {
$query->whereIn('attribute_id', $atts_ids);
})->get();
This query will return companies if at least one attribute_id found (which is not what I am looking for).
It would be great if anybody can make it clearer for me.
Thank you all in advance :)
One possible solution:
$company = new Company();
$company = $company->newQuery();
foreach($atts_ids as $att_id)
{
$company = $company->whereHas('attributes', function (Builder $query) use ($att_id) {
$query->where('attribute_id', $att_id);
});
}
$company = $company->get();

Eloquent limit relationship fields

I have the following relationships:
TheEpisodeJob hasOne TheEpisode
TheEpisodeJob hasMany TheJobs
I am successfuly retrieving all TheEpisodesJobs and TheSeriesEpisodes with all the fields in database (including sensitive information) using this command:
$jobs = TheEpisodeJob::with('TheEpisode')->get();
I would like to limit TheEpisode fields shown only for this case (public $hidden will not work)
EDIT
Let's say I need only title and description field from TheEpisode.
How can I achieve that?
As #Buglinjo pointed out you can scope the relationship when eager loading, however, if you're going to be doing this to only select specific columns you must included the related column in the select so that Eloquent knows which Model to attach the related data to.
This should give you what you want:
$jobs = TheEpisodeJob::with(['TheEpisode' => function ($query) {
$query->select('jobID', 'title', 'description');
}])->get();
Furthermore, if you then wanted to to get rid of the jobID as well you could do something like:
$jobs->transform(function ($job) {
$job->TheEpisode->transform(function ($item) {
unset($item->jobID);
return $item;
});
return $job;
});
Hope this helps!
As far as I understood you, you want to limit the results according to some more parameters. If I am right, you should add more queries, like:
->where, ->orwhere, ->select, ->whereNull
Here is the link for more queries. Hope it will help )
I saw an update, so then you need
->pluck('title', 'description');
for more information, go to the link above
You should do like this:
$jobs = TheEpisodeJob::with(['TheEpisode' => function($q){
$q->get(['title', 'description']);
//or
$q->pluck('title', 'description');
}])->get();
Note: pluck is getting as array not as Eloquent Object.

Laravel get all related models in one collection/array

Laravel 4
Is there any easy way to get all related models in one collection (only with Eloquent)?
For example, I want to get all of the students, that is related to many classes:
First, I need to get a collection with the relations:
$classes = Class::with('students')->whereIn('code', ['A', 'B'])->get();
Then I need to go through the entire collection to merge the students:
$allStudents = new Collection;
foreach ($classes as $class) {
$allStudents = $allStudents->merge($class->students);
}
Or if I need only one key, for example, id of the student, I'm doing this:
$allStudentsIds = [];
foreach ($classes->fetch('students') as $studentsArray) {
$allStudentsIds = array_merge(
$allStudentsIds, array_pluck($studentsArray, 'id')
);
}
Is there a method to get only related models? Or somehow I can make it through a request to the Student model?
Query based on students and join with classes?
Student::select('student.*')
->join('class_student', 'class_student.student_id', '=', 'student.id')
->join('class', 'class.id', '=', 'class_student.class_id')
->whereIn('class.id', $classes)
->get();
Something like that.
I assumed it is a many to many relationship and class_student is the association table

How to Merge Two Eloquent Collections?

I have a questions table and a tags table. I want to fetch all questions from tags of a given question. So, for example, I may have the tags "Travel," "Trains" and "Culture" attached to a given question. I want to be able to fetch all questions for those three tags. The tricky, so it seems, is that questions and tags relationship is a many-to-many defined in Eloquent as belongsToMany.
I thought about trying to merge the questions Collections as below:
foreach ($question->tags as $tag) {
if (!isset($related)) {
$related = $tag->questions;
} else {
$related->merge($tag->questions);
}
}
It doesn't seem to work though. Doesn't seem to merge anything. Am I attempting this correctly? Also, is there perhaps a better way to fetch a row of rows in a many-to-many relationship in Eloquent?
The merge method returns the merged collection, it doesn't mutate the original collection, thus you need to do the following
$original = new Collection(['foo']);
$latest = new Collection(['bar']);
$merged = $original->merge($latest); // Contains foo and bar.
Applying the example to your code
$related = new Collection();
foreach ($question->tags as $tag)
{
$related = $related->merge($tag->questions);
}
The merge() method on the Collection does not modify the collection on which it was called. It returns a new collection with the new data merged in. You would need:
$related = $related->merge($tag->questions);
However, I think you're tackling the problem from the wrong angle.
Since you're looking for questions that meet a certain criteria, it would probably be easier to query in that manner. The has() and whereHas() methods are used to generate a query based on the existence of a related record.
If you were just looking for questions that have any tag, you would use the has() method. Since you're looking for questions with a specific tag, you would use the whereHas() to add the condition.
So, if you want all the questions that have at least one tag with either 'Travel', 'Trains', or 'Culture', your query would look like:
$questions = Question::whereHas('tags', function($q) {
$q->whereIn('name', ['Travel', 'Trains', 'Culture']);
})->get();
If you wanted all questions that had all three of those tags, your query would look like:
$questions = Question::whereHas('tags', function($q) {
$q->where('name', 'Travel');
})->whereHas('tags', function($q) {
$q->where('name', 'Trains');
})->whereHas('tags', function($q) {
$q->where('name', 'Culture');
})->get();
$users = User::all();
$associates = Associate::all();
$userAndAssociate = $users->merge($associates);
Merge two different eloquent collections into one and some objects happen to have the same id, one will overwrite the other. Use push() method instead or rethink your approach to the problem to avoid that.
Refer to web
Creating a new base collection for each eloquent collection the merge works for me.
$foo = collect(Foo::all());
$bar = collect(Bar::all());
$merged = $foo->merge($bar);
In this case don't have conflits by its primary keys.
I have faced some issue by using merge. So I used concat. You can used it like below.
$users = User::all();
$associates = Associate::all();
$userAndAssociate = $users->concat($associates);
All do not work for me on eloquent collections, laravel eloquent collections use the key from the items I think which causes merging issues, you need to get the first collection back as an array, put that into a fresh collection and then push the others into the new collection;
public function getFixturesAttribute()
{
$fixtures = collect( $this->homeFixtures->all() );
$this->awayFixtures->each( function( $fixture ) use ( $fixtures ) {
$fixtures->push( $fixture );
});
return $fixtures;
}
I'm sorry about that, but since PHP 7.4 you're available to do like this (better use merge).
$foo = Foo::all();
$bar = Bar::all();
/** $foo will contain $foo + $bar */
$foo->push(...$bar);
I would like to add that, i found that the concat method does not seem to override based on ID, while the merge method does. concat seems to work for me, while merge caused issues.

Categories