Hi I am new laravel and struggling a bit on understanding how to query relationships. I am trying to make a basic restful api in laravel and have 3 models
class Book extends Model
{
public function author()
{
return $this->belongsTo(Author::class);
}
public function categories()
{
return $this->belongsToMany('App\Category', 'category_book')
->withTimestamps();
}
}
class Author extends Model
{
public function books(){
return $this->hasMany(Book::class);
}
}
class Category extends Model
{
public function books()
{
return $this->belongsToMany('App\Book', 'category_book')
->withTimestamps();
}
}
Table migrations:
Schema::create('books', function (Blueprint $table) {
$table->engine = "InnoDB";
$table->increments('id');
$table->string('ISBN', 32);
$table->string('title');
$table->integer('author_id')->unsigned();
$table->float('price')->default(0);
$table->timestamps();
});
Schema::create('authors', function (Blueprint $table) {
$table->engine = "InnoDB";
$table->bigIncrements('id');
$table->string('name');
$table->string('surname');
$table->timestamps();
});
Schema::create('categories', function (Blueprint $table) {
$table->engine = "InnoDB";
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
Schema::create('category_book', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('category_id')->unsigned();
//$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->integer('book_id')->unsigned();
//$table->foreign('book_id')->references('id')->on('books')->onDelete('cascade');
$table->timestamps();
});
books is the main table and author has a one to many relationship with books. Category has a many to many relationship with books as a book can be in more than one category.
The books table has an author_id field to link it to the authors table. There is also a pivot table called category_books that contains category_id and book_id to link books to categories
But how do I query books so that it returns only books based on the authors name ?
I would also like to do the same thing based on the category name?
I my books controller i have the following but not sure how to do it correctly
public function index(request $request, Author $author, Category $category)
{
$author = $request->author;
$books = Book::find()->author()->where('name', $author);
$books = Book::with(['categories'])->where('name', $category);
return response()->json($books, 200);
}
By author:
$books = App\Book::whereHas('author', function ($query) use ($authorName) {
$query->where('name', $authorName);
})->get();
By category:
$books = App\Book::whereHas('categories', function ($query) use ($categoryName) {
$query->where('name', $categoryName);
})->get();
whereHas allows you to perform where queries on the specified relationship
Together:
$books = App\Book::whereHas('author', function ($query) use ($authorName) {
$query->where('name', $authorName);
})->whereHas('categories', function ($query) use ($categoryName) {
$query->where('name', $categoryName);
})->get();
To filter both author and book at the same query you may use both queries made by gbalduzzi combined:
$books = App\Book::whereHas('author', function ($query) use ($authorName) {
$query->where('name', $authorName);
})->whereHas('categories', function ($query) use ($categoryName) {
$query->where('name', $categoryName);
})->get();
Related
I have Items table that has relation to Histories table.
I want to get count of items that has only latest history.status
I still can't get the exact same count result because its always count all of the histories not the latest one
Here is my code:
create_items_table.php
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->string('code');
$table->string('name');
$table->longText('description')->nullable();
$table->longText('picture')->nullable();
$table->timestamps();
});
create_histories_table.php
$table->foreignId('item_id')->constrained();
$table->string('status')->nullable();
$table->longText('description')->nullable();
$table->dateTime('date')->nullable();
model of Item.php
public function histories(){
return $this->hasMany(History::class);
}
public function latestHistory(){
return $this->hasOne(History::class)->latest();
}
model of History.php
public function item()
{
return $this->belongsTo(Item::class);
}
MyController.php
$items_status['good'] = Item::with('latestHistory')->whereHas('latestHistory', function ($q) {
$q->where('status', 'good');
})->count();
$items_status['broken'] = Item::with('latestHistory')->whereHas('latestHistory', function ($q) {
$q->where('status', 'broken');
})->count();
dd($items_status);
i guess you mean latestOfMany() ?
//Item.php
public function latestHistory() {
return $this->hasOne(History::class)->latestOfMany();
}
Also do you have any solution for count the items that doesn't have
history?
Check docs for doesntHave
$items_status['no_history'] = Item::doesntHave('history')->count();
in my simple blog web application i have some categories and users which stored in database, each category may belongs to one or many user and i try to get them to show each user logged, for implementing this scenario i have this tables:
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->index()->nullable();
$table->foreign('user_id')->references('id')->on('users');
$table->string('name')->nullable();
$table->string('family')->nullable();
$table->string('username')->unique();
$table->rememberToken();
$table->softDeletes();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
});
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('category_id')->index()->nullable();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->string('category_name');
$table->softDeletes();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
});
and then, creating many to many table:
Schema::create('category_user', function (Blueprint $table) {
$table->unsignedBigInteger('user_id')->index();
$table->unsignedBigInteger('category_id')->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->primary(['user_id','category_id']);
});
now how can i get categories of logged user like with this code:
$categories = Category::whereNull('category_id')
->with(['childrenCategories', 'users' => function ($query) {
//$query->where('id',auth()->user()->id);
}])
->withCount('posts')
->get();
You can query the relationship existence with whereHas to get only the categories which belongs to the authenticated user.
$categories = Category::whereNull('category_id')
->whereHas('users', function ($query) {
$query->where('id', auth()->user()->id);
})
->with([
'childrenCategories',
'users' => function ($query) {
$query->where('id', auth()->user()->id);
}
])
->withCount('posts')
->get();
inside Your User Model, add the relationship btw both Models:
public function categories()
{
return $this->belongsToMany('App\Models\Category');
}
inside Your Controller:
$user = App\Models\User::find(1);
now you can get the categories of your user
foreach ($user->categories as $category) {
//
}
https://laravel.com/docs/8.x/eloquent-relationships#many-to-many
Let's consider that I have a model called Sportman and another one Sport who are linked via a pivot table : many to many relationship.
A sample code for their migrations.
# Sportmans migration
Schema::create('sportsmans', function (Blueprint $table) {
$table->increments('id');
$table->string('firstname');
$table->string('lastname');
});
# Sports migration
Schema::create('sports', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('description');
});
Here is their relationship in models :
# Defining association in Sportsman model
public function sports(){
return $this->belongsToMany( Sport::class, 'sportsman_has_sports', 'person_id', 'sport_id' );
}
# Defining association in Sports model
public function sportsman(){
return $this->belongsToMany( Sportsman::class );
}
How can I using Laravel with Eloquent get the sportsman that play:
Only "Footbal"
Box or "Swimming"
Both "Tennis and Basket-ball
Here is what I tried to do for question 2 :
Sportsman::with(['sports:person_id,id,name'->whereHas('sports', function ($query) {
$query->whereIn('name', ['Box', 'Swimming'] );
});
Most difficult is the question 3
You put a subquery on whereHas function...
Sportsman::whereHas('sports', function ($query) {
$query->whereIn('name', ['Box', 'Swimming'] );
})
->with('sports')
->get();
// Below is your last question
Sportsman::where(function ($query) {
$query->whereHas('sports', function ($subquery) {
$subquery->where('name', 'Tennis');
});
$query->whereHas('sports', function ($subquery) {
$subquery->where('name', 'Basket-ball');
});
})
->with('sports')
->get();
I want to get the related posts of a current post by tags but honestly I can't get it.
I will show you my tables structure.
Posts Table:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->text('excerpt')->nullable();
$table->string('stickers')->nullable();
$table->integer('category_id')->nullable()->unsigned();
$table->text('meta_description')->nullable();
$table->text('meta_keywords')->nullable();
$table->string('postimg')->nullable();
$table->string('type')->nullable()->default('common');
$table->boolean('published')->default(false);
$table->softDeletes();
$table->timestamps();
});
}
Tags Table:
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->string('slug')->unique();
$table->softDeletes();
$table->timestamps();
});
}
I have a pivot table to handle tags on posts and posts with those tags.
post_tag table:
public function up()
{
Schema::create('post_tag', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id')->unsigned();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->integer('tag_id')->unsigned();
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
});
}
Everything is working good, one tag have many posts and one post have many tags, is a many to many relationship.
Post Model:
public function tags()
{
return $this->belongsToMany('App\Tag');
}
Tag Model:
public function posts()
{
return $this->belongsToMany('App\Post');
}
I can display all the tags on a post but i want to display the related posts by tags, when i say related posts i meaning to "the current post" that visitor is reading. Let's say that this current post have a microsoft, google, apple, cars tags, i want the related posts to those tags. I don't know if that's possible or is easyer to do ir by categories.
News Controller logic:
Here's where i have all the logic to the post view.
public function getSingle($slug, $id = null)
{
$post = Post::where('slug', '=', $slug)->first();
$topcat = Category::orderBy('created_at', 'desc')->limit(5)->get();
$comment = Comment::find($id);
$tags = Tag::all();
$tags2 = array();
foreach ($tags as $tag) {
$tags2[$tag->id] = $tag->name;
}
// Previous and Next Post
$previous = Post::where('id', '<', $post->id)->orderBy('id', 'desc')->first();
$next = Post::where('id', '>', $post->id)->orderBy('id', 'asc')->first();
// Related Posts Here!
$tags3 = array();
foreach ($post->tags as $tag) {
$tags3[$tag->id] = $tag->name;
}
$related = Post::whereHas('tags', function ($query) use ($tags3) {
$query->where('name', $tags3);
})->get();
// dd($related);
return view('news.single')
->withPost($post)
->withTopcat($topcat)
->withTags($tags2)
->withComment($comment)
->withPrevious($previous)
->withNext($next)
->withRelated($related);
}
I did the $tags3 variable to test it that way but i didn't get what i wanted.
Thanks in advance
This should work:
$post = Post::where('slug', '=', $slug)->first();
$related = Post::whereHas('tags', function ($q) use ($post) {
return $q->whereIn('name', $post->tags->pluck('name'));
})
->where('id', '!=', $post->id) // So you won't fetch same post
->get();
The $post->tags->pluck('name') line creates an array of all the tag names (that belong to the post).
I have three table below:
I want to display all Related job post by category in Single jobpost. and I already have single job post page but in the same single job page I want to display related jobs in the left side. see my picture!
what is controller should be and in the Single job page (view) should be? please help?
My jobController
public function show($id, $company_id)
{
$singleJob = Job::find($id);
$company = Company::find($company_id);
$similarJob = Job::with('company')->where('category_id', $id)->get();
return view('pages.single-job')->with([
'singleJob'=> $singleJob,
'company'=> $company,
'similarJob' => $similarJob,
]);
}
My relationship
job.php
public function company(){
return $this->belongsTo(Company::class);
}
Job.php
public function category(){
return $this->belongsTo(Category::class);
}
//category.php
public function job(){
return $this->hasMany(Job::class);
}
//company.php
public function job(){
return $this->hasMany(Job::class);
}
Job table
Schema::create('jobs', function (Blueprint $table) {
$table->increments('id');
$table->integer('company_id');
$table->string('jobTitle');
$table->longText('jobDescription');
Company Table
Schema::create('company_types', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id');
$table->string('name');
$table->timestamps();
});
Category table
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->string('name');
$table->timestamps();
});
You can use whereHas like this :
public function show($id, $company_id)
{
$singleJob = Job::find($id);
$company = Company::find($company_id);
$similarJobs = Job::with('company')
->whereHas('category', function ($query) use($singleJob) {
$query->where('id', $singleJob->category->id);
})
->get();
return view('pages.single-job')->with([
'singleJob'=> $singleJob,
'company'=> $company,
'similarJobs' => $similarJobs,
]);
}
And in the view you can use it like this :
#foreach ($similarJobs as $similarJob)
// Add the job partial, componnent or just your HTML here for showing the Job
#endforeach
For the question in the comment, to find jobs that have a company that belongs to a given industry :
$some_industry_type_id = 1;
$jobsOfIndustryType = Job::whereHas('company', function ($query) use($some_industry_type_id) {
$query->where('industry_type_id', $some_industry_type_id);
})
->get();