Codeigniter Datamapper many to many list - php

I have 2 many to many relationship tables; Posts and Categories. One post can have many categories. My question is how can I show list of posts with their categories?
Like that:
My post 1 (cat1, cat2, cat3)
My post 2 (cat2, cat3)
My post 3 (cat1)
I've tried these methods;
// Create post object
$p = new Post();
// Get 30 posts
$p->get(30);
// Loop through all posts
foreach ($p as $post)
{
// Get the current user's group
$post->category->get();
foreach($post->category as $category) {
// ...
}
}
Don't like this, because if I'm get 30 posts, then on every post loop again make a query and find category again and again.
and tried this:
$p = new Post();
$p->include_related('category', array('id', 'name'), TRUE, TRUE)->get(30);
foreach($p as $post) {
// ...
foreach($post->category as $category) {
// ...
}
}
This is more close, but this one problem is I setting limit get(30) so if my per post have 2 categories than showing 15 post + 15 categories.
What is the true method for many to many listing?

Well in this case i will opt for caching both tables in php associative arrays, and then loop just the arrays.

Related

Attach result of for loop to eloquent output in Laravel

Good day. Like I explained in this post Using Query Builder and MySql to get categories and sub - categories, I want to make something meaningful from this table. I decided to go the Eloquent way. I am able to get the distinct categories as shown below. How do i loop through each title belonging to a a particular category?
For example, get it in the form:
CatA
Title 1
Title 5
CatB
Title 2
Title 3
Title 4
My code is shown below
public function type(Request $request){
$details = [];
$categories = Category::distinct()->get(['category']); //Get the distinct categories from the category column in the table
foreach($categories as $category){
$titles = Category::where('type',$category->type);
$details = ['cat'=>$category->type,[
'title' => $titles->title,
'pk' => $titles->pk
]];
}
return response()->json([
"Data" => $details
]);
}
Not getting a result. Is there a better way this can be done please?
You can make many to many relationship with post and category. Then get all categories with posts.
Example:
$categories = Category::with('posts')->all();
Then you can loop through categories and posts
foreach($categories as $category){
foreach($category->posts as $post){
echo $post->title;
}
}
Documentation: https://laravel.com/docs/8.x/eloquent-relationships#many-to-many

Laravel pagination with specific foreign key

I need to make pagination in Laravel, and as I read laravel documentation here is a several way to do it. but in my cases it is a little bit complexity. So let say I've two table in my DB, table1 and table2(many to many relationship), I want to paginate table1 with specific ids. So I mean if table1 contains ids 1,2,3,4,5 ... n, I want to paginate only rows which id is 2 and 3
I have tried :
private function check_wear($type){
$products_id = array(); //here is ids which $type is set as 'ON'
$wears = DB::select("SELECT prod_id FROM wears WHERE ".$type." = 'on' "); //Select specific products ids. (in our case it is **table1**)
foreach ($wears as $wr){
array_push($products_id,$wr->prod_id);
}
return $products_id; //return array with ids which should be paginate
}
public function hat($locale){
$hat_prod_ids = $this->check_wear('coat');
$p=12;
$products = DB::table('products')->paginate($p); // 12 per page
dd($products);
my code only paginate one table with all data, is there any built-in function to somehow write a logic condition? for pagination
If you need to filter your pagination with elements which you've in $hat_prod_ids, you're able to use WhereIn() method, that one checks if your elements (in our case ids) exist in your $hat_prod_ids and returns true or false
$products = DB::table('products')
->whereIn('id',$hat_prod_ids)
->paginate($p);
add that code and now you will be able to paginate only that ids which is matched in your array

Eloquent - access parent value in nested function

I'm trying to access the parent value, the posts date, in a nested function.
In my application, a user has many posts, with each post being associated to a product (textbook). Each time someone views a page with the product, a new row is added to the product-views table.
I want to return the cumulative amount of times the users products have been seen. I've been able to get all the users posts, then the associated product, and then the count of all the views of that product.
Now I'd like to add another where() condition to only return views that have occured after the post was created. To do so, I need to get the posts date, e.g. views->product->post, while constructing the query like user->posts->product->views.
// getting all of the users available posts
$user = $request->user()->posts()->available()->with([
// with the textbook (product) associated with the post
'textbook' => function ($q) {
// get the count of textbook-page views
return $q->withCount([
// from the related table views
'views' => function ($q) {
// ===!!! Q !!!=== How do I access the posts (parent (grandparent?)) date, so that I only select the rows that have been viewed after the post was created ===!!!===
->where('date-viewed', '>=', 'posts.date');
},
]);
//some cleanup
}])->distinct()->get()->unique('isbn')->pluck('textbook.views_count')->sum();
How do I go backwards in a nested function to access the posts date?
It looks like as Jonas said in the comments, each with() relationship was a new query, so I ended up creating a hasManyThrough() relationship between the posts and views through the product.
// getting all of the users available posts
$user = $request->user()->posts()->available()->withCount([
'views' => function ($q) {
// ===!!! A !!!=== Fixed ===!!!===
->whereRaw('`date-viewed` >= posts.date');
},
])->distinct()->get()->unique('isbn')->pluck('views_count')->sum();

Eloquent HasMany relationship, with limited record count

I want to limit related records from
$categories = Category::with('exams')->get();
this will get me exams from all categories but what i would like is to get 5 exams from one category and for each category.
Category Model
public function Exams() {
return $this->hasMany('Exam');
}
Exam Model
public function category () {
return $this->belongsTo('Category');
}
I have tried couple of things but couldnt get it to work
First what i found is something like this
$categories = Category::with(['exams' => function($exams){
$exams->limit(5);
}])->get();
But the problem with this is it will only get me 5 records from all categories. Also i have tried to add limit to Category model
public function Exams() {
return $this->hasMany('Exam')->limit(5);
}
But this doesnt do anything and returns as tough it didnt have limit 5.
So is there a way i could do this with Eloquent or should i simply load everything (would like to pass on that) and use break with foreach?
There is no way to do this using Eloquent's eager loading. The options you have are:
Fetch categories with all examps and take only 5 exams for each of them:
$categories = Category::with('exams')->get()->map(function($category) {
$category->exams = $category->exams->take(5);
return $category;
});
It should be ok, as long as you do not have too much exam data in your database - "too much" will vary between projects, just best try and see if it's fast enough for you.
Fetch only categories and then fetch 5 exams for each of them with $category->exams. This will result in more queries being executed - one additional query per fetched category.
I just insert small logic inside it which is working for me.
$categories = Category::with('exams');
Step 1: I count the records which are coming in response
$totalRecordCount = $categories->count()
Step 2: Pass total count inside the with function
$categories->with([
'exams' => function($query) use($totalRecordCount){
$query->take(5*$totalRecordCount);
}
])
Step 3: Now you can retrieve the result as per requirement
$categories->get();

How to get a list of posts and check if the user already "Liked" them? (Check existing relation between two models)

I'm having fun with Laravel framework trying to build a little "blog" site that works the following way:
When you enter the webpage, a cached index of the latest / most popular posts is shown
There is a ManyToMany Relationship "user_likes" between the User and Post models, so that if you're logged in then you can "Like" or "upvote" each of these posts with a "heart" button, so that a relation between the two models will be created in the user_likes pivot table.
I have two database tables
users table:
user_id name
1 TheUser
posts table:
post_id title
1 MyPost
2 Another Post
3 Yet another post
user_likes pivot table:
post_id user_id
1 1
3 1
And the User Model Class:
class User extends BaseUser {
protected $table = 'users';
public function likes()
{
return $this->belongsToMany('Post', 'user_likes','user_id','post_id');
}
}
The question is, what's an efficient way of retrieving a list of Post models and checking if the logged in User has already liked / upvoted them?
The thing is that i'd like to check for every Post if the relation between the logged in User and the Post model exists in the most efficient way.
Thanks in advance!
EDIT:
This SQL Fiddle would be a way of achieving what I'd like to do, the problem is that If i'm not wrong, I won't be able to cache the posts, as I'll need to check them every time a new user logs in.
http://www.sqlfiddle.com/#!2/e5bab/2
Using Laravel's Query builder:
DB::table('posts')
->leftJoin('user_likes', function($join) {
$join->on('user_likes.post_id', '=', 'posts.id')
->where('user_likes.user_id', '=', 1);
})
->select('posts.id as post_id', 'posts.title', 'user_likes as did_i_like')
->groupBy('posts.id')
->orderBy('posts.id');
Right now, I'm doing it in my application in a different way:
I have the paginated posts cached, and then each User has a cached collection of the posts ids that they liked, so when they log in, I'll check if each post is in the user's liked post collection:
$liked = Auth::user()->getLikedPosts(); // returns a cached collection of the User liked posts ids
$posts = Post::popular(1); // returns a cached and paginated index of the most popular posts.
Then, I'll check if the liked collection contains the post id:
foreach($posts as $post)
{
.......
if($liked->contains($post->id)
{
// show red Heart
}
else
{
// show gray heart
}
.......
I think It's not a good way of doing it, since the $liked collection will be growing a lot... However, with the new Query, I really don't know how could I cache the posts, so that I don't have to query the Database each time a user logs in or navigates throught the posts pages.
Thanks in advance!
I am honestly not sure if it's good or not, but I did it like this:
Post Model:
public function likes()
{
return $this->hasMany('Like');
}
public function likedByUser()
{
return $this->likes()->where('user_id', Auth::user()->id);
}
than in blades:
#if(isset($post->likedByUser[0]))
style="background-position: right;" data-value="liked"
#endif
I don't much about speed, but I'm paginating 9-12 each time, so It's not that bad, I hope :)
What's an efficient way of retrieving a list of Post models and
checking if the logged in User has already liked / upvoted them?
in your Post model declare a relationship:
public function likedByUser()
{
return $this->belongsToMany('User', 'user_likes', 'post_id', 'user_id')
->where('user_id', Auth::user()->id);
}
Then retrieve posts which has been liked by the logged in user like this:
$posts = Post::has('likedByUser')->get();
Update:
$posts = Post::get();
$firstPost = $posts->first();
$firstPost->has('likedByUser');
Also you may loop and check:
$posts = Post::get();
foreach($posts as $post) {
if($post->has('likedByUser')) {
// liked it
}
}

Categories