Laravel 4 getting multiple collections - php

I am writing a function in my controller to return a json response. This is my function:
public function play($id){
$quiz = Quiz::find($id);
$questions = $quiz->question()->get();
return Response::json(array(
'quiz' => $quiz,
'questions' => $questions));
}
This works perfectly, however, I also have answers related to questions.
In my Question model I have this answer() function:
public function answer(){
return $this->hasMany('Answer');
}
And in my Answer model I have of course my question() function:
public function question(){
return $this->belongsTo('Question');
}
How do I get my answers? I can't use;
$answers = $question->answer()->get;
And I can't use
//get the questions using query, but this returns nothing
$questions = Question::where('quiz_id', '=', $id);
$answers = $questions->answer()->get;
I hope I'm clear enough, I tried searching but I can't find anything, anybody please help :)?

You can use eager loading to get a result where answers are nested inside questions:
$questions = $quiz->question()->with('answer')->get();
If you just want to get answers of one single question do this:
$question = Question::find(1);
$answers = $question->answer()->get();
Or simply use the dynamic property:
$answers = $question->answer;
Sidenote: I suggest you name hasMany relations in plural. answers instead of answer. This makes semantically more sense and tells you clearly if you get a collection or a single model as result.

Related

Laravel 8: How to return questions that have answers by withCount()

I'm working with Laravel 8 to develop my forum, and I want to return the questions that have answers.
So I added withCount() to my method which goes here:
public function index()
{
$questions = Question::latest()->limit(5)->get();
$topAnswered = Question::withCount('answers')->get();
dd($topAnswered);
}
So $topAnswered should contain the questions that have already answered but it returns also some questions that have not answered yet!
So I need to fix that but don't know how to do that...
Note that I have this relation at the Question Model:
public function answers()
{
return $this->hasMany(Answer::class);
}
And this one also at the Answer Model:
public function question()
{
return $this->belongsTo(Question::class);
}
So if you know, how to return only the questions that have already answered, please let me know, I would really appreciate that.
Thanks in advance.
I think you'll be needing the having function be used as another selector in there
$topAnswered = Question::withCount('answers')->having('answers_count', '>', 0)->get();
You can do it with whereHas
Question::whereHas('answers', function(Builder $query) {
$query->where(...) //You condition where some questions that have not answered yet
})->get();
Or with has:
Question::has('answers', 0)->get();
You can read this section of the Laravel documentation.
I think using has() is the best solution:
public function index()
{
$topAnswered = Post::has('answers_count', '>', 0)->withCount('answers')->take(5)->get();
dd($topAnswered);
}

Sorting users through relation in laravel

i want to sort the users through voornaam(firstname). but im getting the data via a relation.
How do i make my query so that, the relation users are sorted by firstname by alphabet
my function:
public function sortfirstname($id) {
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get()->all();
//This query ^^
$toernooi = Toernooi::findOrFail($id);
dd($ingeschrevenspelers);
return view('adminfeatures.generatespelerslijst', compact('ingeschrevenspelers', 'toernooi'));
}
What i want to sort
any help is appreciated
thanks in advance
Writing code in your own language doesn't make it very easy for other developers to understand your code.
That being said, you can try the orderBy() method on your relationship
In your model where you define the relationship:
public function relationship()
{
return $this->belongsTo(SomeClass::class)->orderBy('name', 'DESC');
}
Don't fire all() function at the end thus obtaining a Collection instance of result
//query without the all function
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get();
//
$ingeschrevenspelers = $ingeschrevenspelers->sortBy('users.firstname');
An alternative to Jordy Groote's answer if you do not want to modify the Model class itself, you can query it with a closure.
$ingeschrevenspelers = UserToernooi::with(['users' => function($q) {
$q->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();
Reference: https://laravel.com/docs/5.3/eloquent-relationships#constraining-eager-loads
Sidenote: I don't think you need a ->all() when you already did a ->get()
$ingeschrevenspelers = UserToernooi::with(['users' => function($query){
$query->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();

Passing a parameter to a custom find method in cakephp 3.x

I want to build a custom find function that retrieves bands for a given genre, i have tried this but the function can't access to the parameter $genre:
public function findGenre(Query $query, array $options)
{
$genre = $options['genre'];
$bands = $this->find()->contain([
'Genres' => function($q){
return $q->where(['Genres.id' => $genre]);
}
]);
return $bands;
}
I can access the $genre outside the contain() method, but not inside it.
My question is, how can i pass the $genre var to the function($q) inside the contain method.
I found where the problem is, i had to use the keyword use after the function($q), so that part of the code will look like this
$bands = $this->Bands->find()->contain('Genres', function($q) use ($genre){
return $q->where(['Genres.name'=>$genre]);
});
Also,the contain() method returns all the data even if the bands don't belong to a genre, but when i replaced it with matching() it worked just fine.
I hope this will help anyone who is having a similar problem in the future.
I was facing same issue but now it's resolved. I will explain you step by step:
My tables are:
articles: id,name,status,created
tags:id,name,status,created
articles_tags: id,article_id, tag_id
my query is this:
I want to pass my $tag_data['slug'] in matching variable but
directly this variable is not working in query. So I put in simple
$uses variable and now it's working properly.
$uses = $tag_data['slug'];
$contain_article = ['Tags'];
$query = $this->Articles->find('All')
->where(['Articles.status' => '1'])
->contain($contain_article)
->matching('Tags', function ($q) use ($uses) {
return $q->where(['Tags.slug' => $uses]);
});
Please try this :-)

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.

Laravel 4 whereIn and many to many

I have a tag system, where you can add tags to photos and users.
I have a function where the users are able to add their favorite tags, and select images based on those tags
But my problem i am a really big beginner with php and laravel and i do not know how to pass the values to the whereIn function
Model
public function tag()
{
return $this->belongsToMany('Tag', 'users_tag');
}
Controller
// get the logged in user
$user = $this->user->find(Auth::user()->id);
// get tags relation
$userTags = $user->tag->toArray();
// select photos based on user tags
$photos = Photo::whereHas('tag', function($q) use ($userTags)
{
$q->whereIn('id', $userTags);
})->paginate(13);
$trendyTags = $this->tag->trendyTags();
$this->layout->title = trans('tag.favorite');
$this->layout->content = View::make('main::favoritetags')
->with('user', $user)
->with('photos', $photos)
->with('trendyTags', $trendyTags);
When i pass i get an error
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
than i tried to use array_flatten() to clean my array
// get the logged in user
$user = $this->user->find(Auth::user()->id);
// get tags relation
$userTags =array_flatten($user->tag->toArray());
// select photos based on user tags
$photos = Photo::whereHas('tag', function($q) use ($userTags)
{
$q->whereIn('id', $userTags);
})->paginate(13);
$trendyTags = $this->tag->trendyTags();
$this->layout->title = trans('tag.favorite');
$this->layout->content = View::make('main::favoritetags')
->with('user', $user)
->with('photos', $photos)
->with('trendyTags', $trendyTags);
This way it works but not returning the correct tags.
Could please someone could lend me a hand on this?
Sure thing and I'll make a couple recommendations.
To get the user model, you simply have to use $user = Auth::user().
To use whereIn(), it's expecting a 1 dimensional array of user id's. The toArray() function is going to return an array of associative arrays containing all the users and their properties, so it's not going to work quite right. To get what you need, you should use lists('id').
And one last thing that has really helped me is when you are setting up a relation that's going to return a collection of objects (hasMany, belongsToMany()), make the relation name plurual, so in this case you would modify your tag() function to tags().
So with all that in mind, this should work for you.
// get the logged in user
$user = Auth::user();
// get tags relation
$userTags = $user->tags()->lists('id');
// select photos based on user tags
$photos = Photo::whereHas('tags', function($q) use ($userTags)
{
$q->whereIn('id', $userTags);
})->paginate(13);
$trendyTags = $this->tags->trendyTags();
$this->layout->title = trans('tag.favorite');
$this->layout->content = View::make('main::favoritetags')
->with('user', $user)
->with('photos', $photos)
->with('trendyTags', $trendyTags);
And I'd suggest to modify your relation to... though not hugely important.
public function tags()
{
return $this->belongsToMany('Tag', 'users_tag');
}

Categories