Laravel how to loop through array in controller and combine relationship - php

In my laravel-application I have a blogs- and an author-table. On the index page, where you see all published blog posts, I want the authors name to appear. So I tried to do this:
public function index()
{
$blogs = Blog::where("publishes_on", "<=", Carbon::now())
->orderBy('publishes_on', 'desc')
->published()
->get();
foreach ($blogs as $blog) {
$author = Author::where('id', $blog->author_id)->get();
}
return view('app.blog.index', compact('blogs', 'author'));
}
For some reason I do not know, this gives me the last added author to my application and on each post, the name of that author is displayed on all posts.
What am I doing wrong here?

In Blog model add author relation
public function author()
{
return $this->belongsTo(Author::class);
}
In controller
$blogs = Blog::where("publishes_on", "<=", Carbon::now())
->orderBy('publishes_on', 'desc')
->published()
->with('author:id,name')
->get();
In view you can use
#foreach($blogs as $blog)
// blog related data
Author: {{ $blog->author->name ?? '' }}
#endforeach

No need for the foreach loop
Blog::with('author')->where( [...]`
In your view
$blog->author->name
Make sure you define author() as a relationship on the Blog model:
https://laravel.com/docs/master/eloquent-relationships
e.g.
class Blog {
function author(){
return $this->belongsTo(Author::class);
}
}

Related

Adding product to wishlist laravel

I'm creating a functionality which allows the user to add product in wishlist, but I'm getting an error Trying to get property of non-object when I click the (whishlist blade), the error comes from this line <h4>USD {{$wishlist->product->price }}</h4> if I remove the $product it displays no price how do i fix this?
Wishlist Controller
public function index()
{
$user = Auth::user();
$wishlists = Wishlist::where("user_id", "=", $user->id)->orderby('id', 'desc')->paginate(10);
return view('wishlist', compact('user', 'wishlists'));
}
Blade
#if (Auth::user()->wishlist->count() )
#foreach($wishlists as $wishlist)
<h2>USD {{$wishlist->product->price }}</h2>
<h4>USD {{$wishlist->product->name }}</h4>
#endforeach
#endif
Wishlist.php
class Wishlist extends Model
{
protected $table = "wishlist";
protected $fillable=['product_id','user_id'];
public function user(){
return $this->belongsTo(User::class);
}
public function product(){
return $this->belongsTo(Product::class);
}
}
User.php
public function wishlist(){
return $this->hasMany(Wishlist::class);
}
Product.php
public function wishlist(){
return $this->hasMany(Wishlist::class);
}
You should first change how you check the count of wishlist, since it runs a heavy query that recovers all wishlists then count them. And also remove the $ in $product as #lucasArbex suggested
#if ($wishlists->count() )
#foreach($wishlists as $wishlist)
<h2>USD {{$wishlist->product->price }}</h2>
<h4>USD {{$wishlist->product->name }}</h4>
#endforeach
#endif
Also change your controller and use the relation on your user
public function index()
{
$user = Auth::user();
$wishlists = $user->wishlist()->with('product')
->orderby('id', 'desc')
->paginate(10);
return view('wishlist', compact('user', 'wishlists'));
}
Firstly, you should access the product relation like so (removing $):
$wishlist->product->price
Secondly, you should eager load the wishlist's product using the ::with() query builder:
public function index()
{
$user = Auth::user();
$wishlists = Wishlist::with('product')
->where('user_id', $user->id)
->orderby('id', 'desc')
->paginate(10);
return view('wishlist', compact('user', 'wishlists'));
}
Also, if I am correct, your product relation is wrong.
Your wishlist should have many products (rather than the other way around).
In your frontend, you will need to loop through all of the wishlist's products:
#foreach($wishlist->products as $product)
{{ $product->price }}
#endforeach
Change the relation in your Wishlist class to hasMany:
public function products()
{
return $this->hasMany(Product::class);
}

How to create a relationship between User and Posts in Laravel

I have a blog and want to include the Users Name when shown to the public.
When creating the blog I make sure to include the user_id in the blogs table
In my Blog model I have the following:
public function users()
{
return $this->belongsTo(User::class);
}
In my Users model I have:
public function blogs()
{
return $this->hasMany(Blog::class);
}
In my Blog Controller I have:
public function index(User $user)
{
$users = User::get();
$blogs= DB::table('blogs')->where('user_id', '=', $users->id)->orderBy('id', 'DESC')->paginate(6);
return view('blogs.index',compact('blogs'));
}
Then in my view:
#foreach($blogs as $blog)
<h1>{{$blog->title}}</h1>
Source:{{$blog->users->first_name}} // This does not work
Source:{{$blog->first_name}} // This does not work either
#endforeach
I thought I could do something like this to show the names:
{{ $blogs->users->first_name }} {{ $blogs->users->last_name }}
But this isn't working either...
Try this:
#foreach($blogs as $blog)
<h1>{{$blog->title}}</h1>
{{$blog->user->first_name}}
#endforeach
And on your Blog Model
public function user()
{
return $this->belongsTo(User::class);
}
In your Blog controller the variable $blog needs to be $blogs. You also have extra characters (right parenthesis) in your Blade. It should be:
#foreach($blogs as $blog)
Source: {{ $blog->user->first_name }}
...
#endforeach
Blog Model
This function replaces the old "users" function, as only one user is returned (belongsTo is a singular relationship).
class Blog extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
User Model
public function blogs()
{
return $this->hasMany('App\Blog');
}
Controller Function
And, as such, you can cut down your controller code, including removing the redundant elements.
public function index(User $user)
{
$blogs = Blog::where('user_id', '=', $user->id)->orderBy('created_at','desc')->paginate(6);
return view('blogs.index', compact('blogs'));
}
The way you did is called Query Builder
$blogs= DB::table('blogs')->where('user_id', '=', $users->id)->orderBy('id', 'DESC')->paginate(6);
Query Builder does not support lazy loading, cause lazy loading is only supported for the Eloquent method
$blog->users->first_name
For Eloquent way you can try this instead:
$blogs = Blog::where('user_id', $user->id)->get()
foreach($blogs as $blog){
dd($blog->user); // you will get the user detail here
}
For lazy loading have a performance issue when come to load heavy data so to prevent lazy loading can use this
$blogs = Blog::with('user')->where('user_id', $user->id)->get()
For more information can look at Eloquent Relationship Documentation
For query builder, the only way to link your user is use join, which will be something like this
$blogs = DB::table('blogs')
->join('users', 'users.id', '=', 'blogs.user_id')
->get();
foreach($blogs as $blog){
dd($blog->first_name) // user first name
}
For more information can look at Query Builder Join
BlogController.php
public function index(){
$blogs = Blog::with('user')->get();
return view('blogs.index')->with('blogs',$blogs);
}
Blog.php
public function user()
{
return $this->belongsTo('App\User');
}
User.php
public function blogs()
{
return $this->hasMany('App\Blog');
}

How to get 5 latest post for each genre Laravel API?

I'm building an application (my course project) with Laravel 5.4 as API and Angular 5. My project is a music blog. I need to fulfill requirements such as to have categories and subcategories.
So, I have genres, bands and posts. For my home page I want to display 5 latest posts for each category.
My tables
genres(id, name, slug);
bands(id, name, slug, genre_id);
posts(id, title, content, band_id, slug, created_at, updated_at).
My relationships
Genre.php
protected $fillable = ['name', 'slug'];
public function bands()
{
return $this->hasMany('App\Models\Band');
}
public function posts()
{
return $this->hasManyThrough('App\Models\Post', 'App\Models\Band');
}
Band.php
protected $fillable = ['name','slug','genre_id'];
public function genre()
{
return $this->belongsTo('App\Models\Genre');
}
public function posts()
{
return $this->hasMany('App\Models\Post');
}
Post.php
protected $fillable = ['title','content','image','band_id','slug'];
public function band()
{
return $this->belongsTo('App\Models\Band');
}
In my HomeController I've tried:
$latest5PostsPerGenre = Genre::with([
'posts' => function($query) {
$query->take(5)
->orderBy('id', 'desc')
->get(['posts.id', 'title']);
}])->orderBy('name')
->get();
But it limits the total number of posts to 5 I got for all genres. So, some genres don't have posts at all.
$latest5PostsPerGenre = Genre::with('latest5Posts')
->orderBy('name')
->get();
with such methods in Genre model:
public function latest5Posts()
{
return $this->hasManyThrough('App\Models\Post', 'App\Models\Band')
->orderBy('id', 'desc')
->take(5)
->get();
}
or
public function latest5Posts()
{
return $this->posts()->latest()->take(5)->get();
}
But I got BadMethodCallException Method addEagerConstraints does not exist.
I even tried something like this:
$genres = Genre::with('posts')->orderBy('name')->get();
$latest5PostsPerGenre = [];
foreach ($genres as $genre) {
$genrePosts['posts'] = [];
$posts = $genre->posts()->orderBy('id', 'desc')->take(5)->get();
foreach ($posts as $post) {
$singePost = [];
$singePost['id'] = $post->id;
$singePost['title'] = $post->title;
$singePost['bandName'] = $post->band->name;
array_push($genrePosts['posts'], $singePost);
}
array_push($latest5PostsPerGenre[$genre->name], $genrePosts);
}
or like this
$genres = Genre::get();
foreach ($genres as $genre) {
$post[$genre->name] = $genre->posts()
->take(5)
->orderBy('id', 'desc')
->get();
}
But I understand it performs a lot of queries to db and is not right.
I tried to create method scopeNPerGroup in Model.php according to the link https://softonsofa.com/tweaking-eloquent-relations-how-to-get-n-related-models-per-parent/, but it gives be a bunch of sql errors.
I was thinking about some complex, nested sql query with Eloquent, but don't clearly understand how to write it.
As a result, I'm expecting to have each genre name with 5 latest posts containing id, title and band name for each post in order to write the link for each post in my Angular frontend like rnmblog.com/api/{genre-slug}/{band-slug}/{post-id} or {post-slug} to get single post.
For my home pageĀ I want to display 5 latest posts for each category.
Your first attempt already does this, isn't it? Just improved it a bit
$latest5PostsPerGenre = Genre::with([
'posts' => function($query) {
$query->with('bands')
->take(5)
->orderBy('id', 'desc')
->get();
}])
->orderBy('name')
->get();

Laravel displaying many to many relationship items

I have set up a many to many relationship in Laravel and have the database table populated with data. The relationship setup looks like this...
users.php
---------
public function houses()
{
return $this->belongsToMany('App\House')
->withTimestamps();
}
house.php
---------
public function users()
{
return $this->belongsToMany('App\User')
->withTimestamps();
}
In my /house/show.blade.php I am trying to display the saved connections like this...
$houses = House::with('App\User')->all();
foreach ($houses as $house) {
echo 'Found House';
}
It is giving me an error saying that $houses can not be found. Where am I going wrong?
You should indicate the relationship in the with method like this :
$houses = House::with('users')->get();
And one more thing it's better to get houses in the controller and pass them to the view :
$houses = House::with('users')->get();
return view('someView')->withHouses($houses);
And in the view do it like this :
#foreach ($houses as $house)
{{ $house->addres }}
#endforeach
To get only the houses taht has the users try this :
$houses = House::has('users')->get();
And to add some conditions on the users you can do it like this :
$houses = House::whereHas('users', function ($query) {
$query->where('name', 'some user name'); // to add some conditions on the user :)
})->get();
You should try this:
$houses = House::with('users')->get();
foreach ($houses as $house) {
echo 'Found House';
}
OR
In controller:
use House;
$houses = House::with('users')->get();
return view('someView',compact('houses'));
In Blade file:
#foreach ($houses as $house)
{{ $house->name }}
#endforeach

Make 'Related To' section based on same Category in laravel

I have a website where i post recipes. each one of them has a category, and i want to display 2-3 more posts with the same category of that post.
how can i build a query to display it? i have a Post Model and Category model that has a belongsToMany relation between the two, and it fills a pivot table, that i bind those categories to a post.
This is the function in my BController, this is the function that passes data to the views where users can access and view.
public function slug(Request $request, $slug)
{
if (Auth::check()) {
$fav = DB::table('post_user')->whereUserId(Auth::id())->pluck('post_id')->all();
}
/*get search query from slug page*/
$query=$request->get('q');
/*display the results of the search*/
if ($query) {
$posts = $query ? Post::search($query)
->orderBy('id','desc')
->paginate(7) : Post::all();
return view('home', compact('posts','fav'));
}
/*the $url veriable is for share buttons*/
else {
$url = $request->url();
$post = Post::where('slug', '=', $slug)->first();
return view('b.s', compact('post','url'));
}
}
this is my Post model:
public function categories(){
return $this->belongsToMany('App\Category');
}
this is in Category model:
public function posts(){
return $this->belongsToMany('App\Post');
}
The pivot table is like so:
$table->increments('id');
$table->integer('post_id')->unsigned();
$table->foreign('post_id')->references('id')
->on('posts')->onDelete('cascade');
$table->integer('category_id')->unsigned()->nullable();
$table->foreign('category_id')->references('id')
->on('categories')->onDelete('cascade');
You can use whereHas to add constraints on related table as:
// get the post usnig slug
$post = Post::where('slug', '=', $slug)->first();
// get the related categories id of the $post
$related_category_ids = $post->categories()->pluck('categories.id');
// get the related post of the categories $related_category_ids
$related_posts = Post::whereHas('categories', function ($q) use($related_category_ids) {
$q->whereIn('category_id', $related_category_ids)
})
->where('id', '<>', $post->id)
->take(3)
->get();
Update
Pass the $related_posts to your view and use it as:
#foreach ($related_posts as $related_post)
<li>{{ related_post->title }}</li>
#endforeach
On possible solution is to include a piece of code to get the required categories.
If I correctly understand your model, you can have several categories. So, we need to take all Categories of your post, and keep only the id ; and we have to exclude the Post ID of the current object :)
$post = Post::where('slug', '=', $slug)->first();
// get Category IDs. There are others ways to do it.
$categoriesId = [];
foreach( $post->categories as $category ) {
$categoriesId[] = $cateogyr->id;
}
// get similar posts
$others = Post::
whereIn('categories', $categoriesId)
->where('id', '!=', $post->id)
->take(3)
->get();
In your case, you have a pivot table:
$others = Post::
with(array('YourPivot' => function($query) use($categoriesId)
{
whereIn('categories', $categoriesId)
})
->where('id', '!=', $post->id)
->take(3)
->get();

Categories