In my application, I have a posts table, categories tables and category_post table as a post and category have a many to many relationship.
I want to get all posts with their attached categories, but also group them together as on my frontend I want to be a loop over an object and show a list like this,
Cooking
Post Number 1
Post Number 3
Post Number 4
Music
Post Number 2
Post Number 5
Languages
Post Number 6
Post Number 7
I know in my controller I can do this,
$posts = Post::with('categories')->get();
But I don't how to groupBy a relational attribute or if I can structure the returned data in such a way that I can loop over it to form the list below.
you can get the data from db and then group them the way you want:
$posts = Post::with('categories')->get();
$result = $posts->groupBy([
'categories',
function ($item) {
return $item['category_name'];
},
], $preserveKeys = true);
more details about grouping by relation fields in:
https://laravel.com/docs/7.x/collections#method-groupby
As per given your table: posts and categories tables are the main tables and category_post table is a pivot table which contains the relation between posts and categories table :
So, Your relation should be posts->category_post->categories
$posts = Posts::with('category_post')->get();
$grouped = $posts->groupBy('category_post.category_id');
The category_post also related to the categories table for categories data.
In category model you cam setup and belongsToMany relation like the following
function posts()
{
return $this->beongsToMany(Post::class, 'category_post', 'category_id', 'post_id');
}
Then do as Arun A S suggested.
Related
I'm using laravel 7 and
I have 3 tables shown below. I want first three students data instead of all student. tables have many to many relationship.
groups
id
name
students
id
name
group_student_pivot
group_id
student_id
created_at
I have below relationship in models
Groups model
public function students()
{
return $this->belongsToMany(Student::class,'group_student_pivot')->withPivot(['status'])->withTimestamps();
}
Student model
public function groups()
{
return $this->belongsToMany(Group::class,'group_student_pivot')->withPivot(['status'])->withTimestamps();
}
$groups = Group::whereIn('id',$groupIds)->with('students')->get();
In above query I want first 3 students data instead of all students.
You can get 3 records like this:
$groups = Group::whereIn('id',$groupIds)->with('students', function($q){
$q->take(3);
})->get();
You can use with and whereHas method
$groups = Group::with('students')->whereHas('students', function($q){
$q->take(3);
})->whereIn('id',$groupIds)->get();
this is my situation:
I have a collection of products and i want all the categories of those products.
Between Product and Category there is a many-to-many relation already created, and i want to do something like this:
$prods = Product::where(something)->get();
$categories = $prods->categories();
But obviously this doesn't work, and i would avoid getting all categories for each products, and add it to a collection only if is not alredy in it... something like this:
select *
from categories join pivot on categories.id = pivot.id
where pivot.product_id IN (1,2,3,4,5)
where 1,2,3,4,5 are the ids of the products
Is there any way to do it without QueryBuilder (using Eloquent)?
You should define a many-to-many relationship in your Product model
public function categories()
{
return $this->belongsToMany(Category::class);
}
And then you can do this
$ids = [1,2,3,4,5];
$prods = Product::with('categories')->findOrFail($ids);
$categories = $prods->flatMap->categories;
The with call eager loads your categories, and then you retrieve them easily + flatten (to avoid multi-dimensional collection) thanks to flatMap method.
I have 3 tables: reports, fields and report_fields which is a pivot between the other 2. What i need to do is order report_field.field by the position column in the field table.
I tried ordering in the relation in the Models or when using with but I may be doing it wrong.
ex:
$query = Report::with([ 'reportFields.field' => function ($q) {
$q->orderBy('position', 'asc');
//$q->orderByRaw("fields.position DESC");
},
Can someone give a basic example of ordering a 2 level nested relationship?
Edit: I do not need to order by any column in the base table but the list of entries in the pivot table by an column in the second table.
Edit2:
To give an example how the output should be ordered:
Report
ReportField
Field.position = 1
ReportField
Field.position = 2
ReportField
Field.position = 3
You can add your needed ordering on the relation of the first table reports:
public function reportFields()
{
return $this->hasMany(ReportFields::class)
->select('report_fields.*')
->join('fields', 'report_fields.field_id', 'fields.id')
->orderBy('fields.position', 'asc');
}
Let's say I have three tables:
Article: id, ...
Advert: id, display_on_every_subsite, ...
Article_Advert: advert_id, article_id
I have a simply Eloquent's relationship: belongsToMany between Article and Advert - Article_Advert is the pivot table.
The problem is that I need to fetch all Adverts for specified Article(s) AND all Adverts with display_on_every_subsite = 1.
I'm trying to achieve this using unions, this is what I've at this moment:
$this->belongsToMany('Advert', 'Article_Advert', 'article_id', 'advert_id')->union(Advert::allSubpages()->selectRaw('`advert`.*, `advert`.`id` as `pivot_advert_id`, null as `pivot_article_id`')->getQuery());
The problem is that when pivot_article_id is null, Eloquent does not attach fetched rows to any related model.
It's almost like that, just change the following:
// I assume you have this inside Article Model
$articleId = $this->id;
$this->belongsToMany('Advert', 'Article_Advert', 'article_id', 'advert_id')
->union(Advert::allSubpages()
->selectRaw("`advert`.*, `advert`.`id` as `pivot_advert_id`, '$articleId' as `pivot_article_id`")
->where('display_on_every_subsite','=','1')
->getQuery());
Thanks for taking the time to help.
I am building a blog in which I can associate categories. I'm saving the associated cats in the blog table by id.
Eg: category_blog_id = 1, 3, 9
I want to retrieve the categories by there title so thought the best approach was to write an accessor on the blog model.
Could someone point me in the right direction with this?
Should I add use CategoryBlog to the model and then explode the category_blog_id and run a foreach ver it?
That is what I have been trying, but it isn't working quite right yet and I wondered if there is a better, more Laravel-y way to do this?
Many thanks.
Actually, the relation between Post and Category could be many-to-many because a Category can has multiple posts under it and also a Post could be in more than one Category. So, if this is the case then you should create three tables like:
Table - posts:
id | post_title | post_slug | post_content | Others...
Table - categories:
id | category_title | category_slug | Others...
Table - category_post (Pivot table/ maintains relation between posts and categories):
id | category_id | post_id
Then you need two Models as Post and Category:
// Post model
class Post extends Eloquent {
public function categories() {
return $this->belongsToMany('Category');
}
}
// Category model
class Category extends Eloquent {
public function posts() {
return $this->belongsToMany('Post');
}
}
Create categories using something like this:
category::create(array(...)); // Input::all() (Mass Assignment)
Also you may create a Category using:
$category = new category;
$category->category_title = Input::get('category_title');
$category->category_slug = Input::get('category_slug');
// other fields (if have any)
$category->save();
Now create Post and attach categories:
$post = new Post; // Assume that, this is the first post so id would be 1
$post->title = 'My Post'; // Input::get('post_title');
$post->slug = 'mypost'; // Input::get('post_slug');
// Assign other values like post_content etc then
$post->save();
Once the Post is saved then:
// Attach two categories with this post using category id
$post->categories()->sync(array(1, 2)); // These (1, 2) are category ids
So, now you have a Post and this Post belongs to to categories, in other words, this post has been created under two categories by syncing the the post and categories, you are actually making a relation between the post and two categories and these relational data will be saved in category_post table so according to this example you category_post table will contain something like this:
id | category_id | post_id
----------------------------
1 | 1 | 1
2 | 2 | 1
Now you may query using Post model to get all the posts with categories like this:
$posts = Post::with('categories')->get();
Also find a single post by id with categories related to it using something like this:
$post = Post::with('categories')->find(1);
You may access the related categories using this:
$post->categories->get(0); // first category from the collection
$post->categories->get(1); // second category from the collection
If you pass the collection of Post models to your view like this;
$posts = Post::with('categories')->get();
return View::make('post.index')->with('posts', $posts);
Then in the view you may loop all posts and categories like this:
#foreach($posts as $post)
{{ $post->post_title }}
{{ $post->post_content }}
#foreach($post->categories as $category)
{{ $category->title }}
#endforeach
#endforeach
You may also use the Category model like:
// Get all categories with related posts
$categories = Category::with('posts')->get();
// Get a category using it's id with related posts
$category = Category::with('posts')->find(2); // Category id 2
This is the basic idea, read the manual (Eloquent ORM) for more.
Don't ever store delimited values in the database! Instead introduce a many-to-many (pivot) table blog_category in addition to blogs and categories tables. It will allow you to normally maintain and query your data using the means that relational database gives you (e.g. JOINs).
The schema for such table may look like something this:
CREATE TABLE blog_category
(
blog_id INT UNSIGNED NOT NULL,
category_id INT UNSIGNED NOT NULL,
PRIMARY KEY (blog_id, category_id),
FOREIGN KEY (blog_id) REFERENCES blogs (id),
FOREIGN KEY (category_id) REFERENCES categories (id)
);
A Laravel migration for such table may look like
class CreateBlogCategoryTable extends Migration {
public function up() {
Schema::create('blog_category', function(Blueprint $table)
{
$table->integer('blog_id')->unsigned();
$table->integer('category_id')->unsigned();
$table->foreign('blog_id')->references('id')->on('blogs');
$table->foreign('category_id')->references('id')->on('categories');
$table->primary(['blog_id', 'category_id']);
});
}
public function down() {
Schema::drop('blog_category');
}
}
Laravel Eloquent supports many-to-many relationships out of the box:
In Blog model
class Blog extends Eloquent {
public function categories() {
return $this->belongsToMany('Category');
}
}
and in Category model
class Category extends Eloquent {
public function blogs() {
return $this->belongsToMany('Blog');
}
}
Now you can access blogs through categories
$blogs = Category::find(1)->blogs();
or categories to which a specific blog belongs
$categories = Blog::find(1)->categories();