I want to get all the books under a certain category. The category has many subjects and books are linked to subjects.
My code:
class BookCategory extends Model {
public function subjects() {
return $this->hasMany('BookSubject', 'category_id', 'id');
}
public function getBooksAttribute() {
return Books::whereHas('subjects', function ($query) {
return $query->join('book_category', 'book_category.id', '=', 'book_subject.subject_id')
->where('book_category.id', $this->id);
})->get();
}
}
and my Books model:
class Books extends Model {
public function subjects() {
return $this->belongsToMany('BookSubject', 'book_by_subject', 'book_id', 'subject_id');
}
}
If I do:
$cats = \App\Models\BookCategory::all();
foreach ($cats as $c) {
echo $c->books->count();
}
It's always returning 0 for all the rows. What I'm I doing wrong?
I believe the problem is in your subquery:
return $query->join('book_category', 'book_category.id', '=', 'book_subject.subject_id')
->where('book_category.id', $this->id);
I'm pretty sure book_subject.subject_id should be book_subject.category_id
Hard to tell without seeing your db schema.
Related
How can I fetch data along with blog category and blog tags from Blogs table using with in query.
Below is my model and controller code, I am getting Get Blogs Api Error instead of the blogs data.
Blog Controller
public function getBlogs()
{
try {
$blogs = Blog::where('status', 1)
->with('category')
->with('tag')
->with('user')
->with('comment')
->orderBy('id', 'desc')
->paginate(5);
return response()->json($blogs);
} catch (\Illuminate\Database\QueryException $e) {
$e = "Get Blogs Api Error";
return response()->json($e);
}
}
Blog Model
class Blog extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function category()
{
return $this->hasMany(Category::class);
}
public function tag()
{
return $this->hasMany(Tag::class);
}
public function comment()
{
return $this->hasMany(Comment::class);
}
}
User Model
public function blog_user()
{
return $this->hasMany(Blog::class);
}
Blog Category Model
public function blog_category()
{
return $this->belongsTo(Blog::class);
}
Blog Tag Model
public function blog_tag()
{
return $this->belongsTo(Blog::class);
}
Blog Comment Model
public function blog_comment()
{
return $this->belongsTo(Blog::class);
}
Database table structure
blogs table structure
blog_categories table structure
blog_tags table structure
First of all change names to plural. not singular. as you are using one to many. and use belongsToMany() method. not hasMany().
public function categories(){
return $this->belongsToMany(Category::class);
}
and change the name of pivot table to blog_category not blog_categories. It will work. and your BlogCategory model will look like this.
class BlogCategory extends Model {
protected $table = 'blog_category';
public function blog() {
return $this->belongsTo( Blog::class );
}
}
now you can get blogs like this.
$blogs = Blog::with( 'categories' )->get();
and this is how you will fetch blog for any category.
$category = BlogCategory::where( 'category_id', $category->id )->first();
dd( $category->blog );
I'm developing a simple survey system, and I'm having problem with getting the right data.
I'm trying to retrieve all categories with questions and answers, that are assigned to a specific survey.
ERD:
The following code nearly works, however it does not filter the questions that are assigned to a specific survey.
$categories = Category::whereHas('questions.surveys', function ($query) use ($id) {
$query->where('surveys.id', $id);
})->with('questions', 'questions.answers', 'questions.surveys')
->get();
Question Model:
class Question extends Model
{
public function answers()
{
return $this->belongsToMany('App\Models\Surveys\Answer', 'question_answers');
}
public function category()
{
return $this->belongsTo('App\Models\Surveys\Category');
}
public function surveys()
{
return $this->belongsToMany('App\Models\Surveys\Survey', 'survey_questions');
}
}
Category Model:
class Category extends Model
{
public function questions()
{
return $this->hasMany('App\Models\Surveys\Question');
}
}
Survey Model
class Survey extends Model
{
public function questions()
{
return $this->belongsToMany('App\Models\Surveys\Question', 'survey_questions');
}
}
For this you need to constrain your eager loads as well:
$categories = Category::with([
'questions' => function ($query) use ($id) {
$query->with('answers', 'surveys')
->whereHas('surveys', function ($query) use ($id) {
$query->where('id', $id);
});
},
])->whereHas('questions.surveys', function ($query) use ($id) {
$query->where('id', $id);
})->get();
This way you're saying only get you the categories that are related to a specific survey and only get the question that relate to that category and the specific survey.
I have 2 tables (students, tests) and a pivot table (student_test).
From a specific student, I want to get all rows from the tests including the related columns from student_test. If there are no test results the values of the columns of student_test are NULL.
I have tried to do it like this:
public function all_tests()
{
$collection = new Collection();
foreach (Tests::all() as $test) {
if (($model = $this->tests->find($test->id)) === null) {
$model = $test;
}
$collection->push($model);
}
return $collection;
}
I have the following models.
app/Http/Models/Student.php:
class Student extends Model
{
protected $table = 'students';
// Attributes: id, first_name, last_name
public function tests()
{
return $this->belongsToMany(Test::class);
}
}
app/Http/Models/Test.php:
class Student extends Model
{
protected $table = 'tests';
// Attributes: id, name
public function students()
{
return $this->belongsToMany(Student::class);
}
}
I want to return a collection of Test models with the extra column (test_result) from student_test table.
Laravel is only using the table student_test to get the model either from tests table or students table its not taking any extra columns into account.
I think you need an extra model called Result that has a many to one relation with a test and a one to many relation with a student
Add the model with the result columns
Add this to the Student model
public function results()
{
return $this->hasMany(Result::class);
}
Add this to the Test model
public function results()
{
return $this->hasMany(Result::class);
}
Add this to the Result model
public function test()
{
return $this->belongsTo(Test::class);
}
public function student()
{
return $this->belongsTo(Student::class);
}
And add the correct columns.
This way you can do:
$student->results->whereHas('test', function ($query) use ($test) {
$query->where('id', '=', $test->id);
})->get();
Or:
$test->results->whereHas('student', function ($query) use ($student) {
$query->where('id', '=', $student->id);
})->get();
i think this code can be help you.
you must use 'with' by relation model
$collection = new Collection();
foreach (Tests::all()->with('tests') as $test) {
if (($model = $this->tests->find($test->id)) === null) {
$model = $test;
}
$collection->push($model);
}
return $collection;
Inspired by Mike's solution, I came up with the following:
$student_id = 1;
$test_results = Test::crossJoin('students')
->leftJoin('results', [
['students.id', '=', 'results.student_id'],
['test.id', '=', 'results.test_id'],
])
->where('students.id', $student_id)
->get();
I have a polymorphic model, named FeedItem which is has polymorphic relationships to an Alert and Publication model.
There is also another model, named Category, which the Alert and Publication models have a hasMany relationship to.
FeedItem
public function feedable()
{
return $this->morphTo();
}
Publication
public function feedItem()
{
return $this->morphOne('FeedItem', 'feedable');
}
public function categories()
{
return $this->hasMany(Category::class);
}
Alert
public function feedItem()
{
return $this->morphOne('FeedItem', 'feedable');
}
public function categories()
{
return $this->hasMany(Category::class);
}
I want to be able to get all FeedItems where the feedable models have a given category.
I've tried:
$items = FeedItem::with(['feedable.categories' => function($item) {
$item->whereHas('feedable.categories', function ($q) {
$q->where('categories.name', 'my-category');
})->orderBy('id', 'desc')
->paginate();
However, the issue is that the feedable relation never gets eager loaded, which prevents the categories relationship scoping from being performed.
For example, if I do this:
$items = FeedItem::with(['feedable.categories' => function($item) {
dd('This should be displayed');
$item->whereHas('feedable.categories', function ($q) {
$q->where('categories.name', 'my-category');
})->orderBy('id', 'desc')
->paginate();
The dd() statement is never called.
Can anyone provide a way of doing this sort of query?
I'm having trouble on the eager loading.
Let's say I have models of Members, TrainingCategory, TrainingCategoryResult and Registration
Member Model:
public function registration() {
return $this->hasMany('Registration', 'member_id');
}
public function trainingResults(){
return $this->hasMany('trainingResult', 'member_id');
}
public function trainingCategoryResults() {
return $this->hasMany('TrainingCategoryResult', 'member_id');
}
TrainingCategory Model:
public function trainings() {
return $this->hasMany('Training', 'id');
}
public function trainingCategoryResults() {
return $this->hasMany('trainingCategoryResult', 'category_id');
}
TraningCategoryResult Model:
public function category() {
return $this->belongsTo('TrainingCategory', 'id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
Registration Model:
public function course() {
return $this->belongsTo('Course', 'course_id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
I am trying to eager load all the registration info and its related info including the TraningCategoryResult info but I not sure how to get that TraningCategoryResult which required two foreign keys (category_id and member_id), is there any way to do that?
Here is my code atm:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
});
$members= $members
->with('registration.course',
'registration.course.traningCategories',
->get(['member.id']);
Thank you.
This will not work Member::with('categoryResult')->with('registration')->get()
You can make a new relation in Member Model
public function categoryResult()
{
return $this->belongsTo('Category')->with('Registration');
}
//and then call
Member::with('categoryResult')->get();
You could use a few options to achieve that:
OPTION 1: create a variable relationship
Change your relation in the Member model
public function trainingCategoryResults($category_id = null) {
if(empty($category_id))
return $this->hasMany('TrainingCategoryResult', 'member_id');
else
return $this->hasMany('TrainingCategoryResult', 'member_id')
->where('category_id', $category_id);
}
The code above might have limitations and it doesn't take advantage of many laravel features, but it will work.
OPTION 2: Access from relationship
You can keep everything as is, and load as follow:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
})
->where('id', $member_id) // set the id of the memeber
->with(array(
'traningCategoryResults' => function($q)use($category_id){
$q->where('category_id', $category_id); // This makes sure you get only desired results
}
))
In this way you will have only what you need, assuming you know the $category_id