How to show related items with pagination in Eloquent and Blade? - php

AdminsController
public function index(){
$someMessages=$this->blog->paginate(5);
$users=User::all();
// return 'Welcome Admin';
return View::make('admin.admin',['someMessages'=>$someMessages,'users'=>$users]);
}
admin.blade.php
Here need to display username from 'users' variable based on uid stored in 'someMessages' object.
#extends('layout.default')
#section('title')
<title>Welcome Admin</title>
#stop
#section('content')
#foreach($someMessages as $message)
<blockquote>{{$message['blog']}}
<small>
<cite>
</cite>
</small>
</blockquote>
#endforeach
{{ $someMessages->links()}}
#stop

If you have a Eloquent relation between blog messages and users defined correctly you don't need to query the users separately. What you want to do is just go with the eager loading:
//in case you call it author in your "relation definition"
$someMessages = $this->blog->with('author')->paginate(5);
And then in your Blade template:
#foreach($someMessages as $message)
<div>
<div>{{ $message->blog }}</div>
<div>{{ $message->author->name }}</div>
</div>
#endforeach
If you haven't declared the relationship yet - it's easy. Open up your user eloquent model class and add a method:
public function blogs()
{
return $this->hasMany('Blog', 'uid');
}
In the blog eloquent model class you'd add:
public function author()
{
return $this->belongsTo('User', 'uid');
}
More about eloquent and relationships can be found here.
If that's not the case you can still filter your users collection to get the one you want:
#foreach($someMessages as $message)
<?php
$user = $users->filter(function($user) use ($message)
{
return $user->id == $message->uid;
})->first();
?>
<div>
<div>{{ $message->blog }}</div>
<div>{{ $user->name }}</div>
</div>
#foreach
What happens here is you filter your users collection based on $message->uid value and take the first one from it (it should always be just one or none since user IDs are unique).
Although you need to understand that in this case you will get all the users from database and filter through them for every blog message you're outputting. Eager loading is a much better idea here and I'd stick to it if possible.

Related

i cant display posts' category with manyToMany relationship

i cant display posts' category with manyToMany relationship. i build relationship but can't display it.
// here is my post model
public function getCategory(){
return $this->belongsToMany(Category::class,
'post_categories','id','post_id');
}
// here is my controller
public function Allindex(){
$posts=Post::all();
return view('allposts',compact('posts'));
}
//here is my allposts blade
<div class="media-body">
<h4 class="media-heading">{{$post->pivot['name']}}</h4>
{{$post->created_at}}
</div>
You are passing the wrong param in a model relationship.
public function getCategory(){
return $this->belongsToMany(Category::class,'post_categories','post_id','category_id');
}
Now you can access it as below.
#foreach($post->getCategory as $category)
{{ $category->name }}
#endforeach
Or
{{ $post->getCategory->pluck('name')->implode(',') }}
Laravel is not automatically loading your relationship, the keyword you are looking for is eager loading.
You can eager load relationships with the with method like so:
$posts = Post::with('getCategory')->get();

Property [name] does not exist in laravel

I want to show my product in a single page but I have "Property [name] does not exist on this collection instance." error
blade:
<div class="title">
<h2>{{ $singleproduct->name }}</h2>
</div>
<div class="single-product-price">
<h3>{{ $singleproduct->price }}</h3>
</div>
<div class="single-product-desc">
<p>{!! $singleproduct->explain !!} </p>
</div>
controller:
public function show()
{
$singleproduct = Singleproduct::get();
return view('UI.store.SingleProduct' , compact('singleproduct' ));
}
route:
Route::get('/singleproduct/{product}' , 'admin\SingleproductController#show');
You have a Collection of potentially many or no Singleproducts. If you only want 1 you would use first.
Most likely because this is a show route you want a specific Singleproduct, which I will guess you are passing an 'id' via the URL.
public function show($product)
{
$singleproduct = Singleproduct::findOrFail($product);
return view('UI.store.SingleProduct', compact('singleproduct'));
}
Now in the view you know that singleproduct is definitely an instance of Singleproduct and is accessible.
You need to select only one product not all of them,
so you can update your show method like this
using the route model binding you can read more about this awesome feature in laravel docs
public function show(Singleproduct $product)
{
return view('UI.store.SingleProduct' , compact('product' ));
}

Laravel - getting relation in loop - best practices

Just an example:
let's say I have Post model, and the Comment model. Post, of course, have Comments, one-to-many relation.
I have to display list of posts with comments below it.
I'll get my posts in the controller:
$posts = Post::get(), I'll pass it to the blade view and then I'll loop through it
#foreach($posts as $post)
{{ $post->title }}
{{ $post->comments }}
#endforeach
where $post->comments is some relation
public function comments()
{
return $this->hasMany(Comment::class);
}
As we know, that query will be executed many times.
Now my question: how we should optimize it?
Return Cache::remember in the getter?
Get (somehow?) those comments, when getting the posts in one query? Something like join query? I know that I can write that kind of query, but I'm talking about Eloquent's query builder. And then how get the comments within the loop? Wouldn't {{ $post->comments }} call the relation again instead of getting stored data?
Different solution?
You can do $posts = Post::with('comments')->get() to eager load the comments with the post. Read more about it in the documentation: https://laravel.com/docs/5.7/eloquent-relationships#eager-loading
Also, to display the comments you would want to add another foreach loop. It would look something like this:
#foreach($posts as $post)
{{ $post->title }}
#foreach($post->comments as $comment)
{{ $comment->title }}
#endforeach
#endforeach
You’ve probably cached some model data in the controller before, but I am going to show you a Laravel model caching technique that’s a little more granular using Active Record models
Note that we could also use the Cache::rememberForever() method and rely on our caching mechanism’s garbage collection to remove stale keys. I’ve set a timer so that the cache will be hit most of the time, with a fresh cache every fifteen minutes.
The cacheKey() method needs to make the model unique, and invalidate the cache when the model is updated. Here’s my cacheKey implementation:
public function cacheKey()
{
return sprintf(
"%s/%s-%s",
$this->getTable(),
$this->getKey(),
$this->updated_at->timestamp
);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function getCachedCommentsCountAttribute()
{
return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
return $this->comments->count();
});
}
yes u can do like that in controller
$minutes = 60;
$posts = Cache::remember('posts', $minutes, function () {
return Post::with('comments')->get()
});
in blade u can get like that
#foreach($posts as $post)
{{ $post->title }}
#foreach($post->comments as $comment)
{{ $comment->title }}
#endforeach
#endforeach
for more information read this article

I am learning laravel and i want to try show a single book through a wildcard but it shows an error

here is my route
Route::get('showbooks', 'AdminController#show');
Route::get('showbooks/{book_id}', 'AdminController#singleBook');
here is methods for all and single book
public function show()
{
$allBooks = Book::all();
return view ('admin.showbooks', compact('allBooks'));
}
public function singleBook($book_id)
{
$book = Book::find($book_id);
return view ('admin.singlebook', compact('book'));
}
here is link for single book
#foreach($allBooks as $book)
<h2> Book Id is: {{ $book->book_id }}</h2>
<h4>Book Title is: {{ $book->title }}</h4>
#endforeach
this is my single book view
<div class="col-lg-9">
<h1>{{ $book->book_id }}</h1>
<h3>{{ $book->tile }}</h3>
</div>
You should set primary key in your model. Because by default primary key is taken as "id".
Thats why when you write
$book = Book::find($book_id);
laravel makes sql as
where books.id=blabla
Change your model as below:
class Book extends Eloquent {
protected $primaryKey = 'book_id';
}

Laravel displaying data from 2 tables

Let me explain situation first.
I am on the page with list of skills available such as "Plumber","Carpenter" and "Painter", when I click on one of those skills I want to get a list of handymans that have that skill and once clicked on a handyman I get full details about him.
Skills are displayed however when I click on one of the skills it doesn't want to retrieve any data. Both tables "handymen" and "skills" have many to many relationship and also there is a junction table. What am I doing wrong here?
Route::group(['middleware' => ['web']], function () {
Route::get('home', 'HandymanController#home');
Route::get('search', 'HandymanController#search');
Route::get('details/{handyman}', 'HandymanController#details');
Route::post('assignjob', 'HandymanController#assignJob');
Route::get('addjob', 'HandymanController#addJob');
Route::post('addjform', 'HandymanController#addjForm');
Route::get('jobs', 'HandymanController#jobs');
Route::get('jobsdetails/{jobId}', 'HandymanController#jobsdetails');
Route::get('deletejob', 'HandymanController#deleteJob');
Route::post('deletejform', 'HandymanController#deletejForm');
Add Job View:
#extends('layouts.master')
#section('title', 'Add Job')
#section('header2')
<ul>
<li>Assign job</li>
</ul>
#show
#section('content')
<h1>Handyman details</h1>
<ul>
#foreach ($handymen as $handyman)
<a href= "{{ url("HandymanController#details", $handyman->id) }}">
{{$handyman->name}}
</a>
#endforeach
</ul>
Controller:
function search()
{
$skills = Skill::all();
return view('layouts/search',['skills' => $skills]);
}
function details($skillId)
{
$skill = Skill::find($skillId);
$handymen = $skill->handymen;
return view('layouts/details', ['handymen' => $handymen]);
}
According to your edited question, first you list all the skills in your search view. So, in your search view, you would have something like this:
#foreach ($skills as $skill)
<a href= "{{ action("HandymanController#addjForm", $skill->id) }}">
{{ $skill->name }}
</a>
#endforeach
So you have to define in your routes file this route:
Route::post('addjform/{skill}', 'HandymanController#addjForm');
This will point to Handyman Controller, where you will list all handymen that have that skill:
public function addjForm(Request $request, Skill $skill)
{
$handymen = $skill->handymen;
return view('layouts/skilledHandymen', ['skill' => $skill,'handymen' => $handymen]);
}
For this to work, you have to define in Skill Model, the association:
public function handymen()
{
return $this->belongsToMany(Handyman::class,
'handyman_skill',
'skill_id',
'handyman_id');
}
The controller will point to a view where you will list all handymen that have such skill:
In your case, it would be easier if you define an association in Skill model that links to Handyman:
#foreach ($handymen as $handyman)
<a href= "{{ action("HandymanController#details", $handyman->id) }}">
{{ $handyman->name }}
</a>
#endforeach
When you choose a handyman, it will take you to Handyman controller details:
function details(Request $request, Handyman $handyman)
{
return view('layouts/details', ['handymen' => $handymen]);
}
For this you will define this route:
Route::get('/handyman/{handyman}', 'Handyman#details');
And this will point you finally to the details of chosen handyman, and you can show his details:
<p>{{ $handyman->id }}<p>
<p>{{ $handyman->name }}</p>
The thing that is important to understand here is that you will first have a Collection of skills that will lead you to a Collection of Handymen, and not just a single one. After you choose a Handyman from the second list you will be able to show his details. If you try to jump over this step you will be trying to show details of a list.
Hope this helps...

Categories