hoping something can help. I've been struggling with this one for a while and not sure if its something obvious that I am missing.
I have a user profile setup, where when someone views it, it stores the user id of that person in a table. I would like to create a "Who's visited me" where it will show the user details of that person who has visited their profile.
So far everything is working but I cannot get the Who's visited me working to show the details.
Here is what I have so far
User Model
public function profile()
{
return $this->hasOne(Profile::class);
}
public function profileViews(){
return $this->hasMany(ProfileView::class, 'profile_id');
}
ProfileView Model
protected $fillable = [
'profile_id',
'visitor_id',
];
public function users()
{
return $this->belongsTo(User::class);
}
Profile Controller
public function profile(User $user)
{
$profile_id = $user->id;
ProfileView::updateOrCreate(['visitor_id' => Auth::user()->id, 'profile_id' => $profile_id, 'updated_at' => now()]);
return view('members.users.profile', compact('user' ));
}
Just incase you need it, my migration for the Profile Visitors Tables
Profile View Table
public function up()
{
Schema::create('profile_views', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('visitor_id');
$table->unsignedBigInteger('profile_id');
$table->timestamps();
$table->foreign('visitor_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
Here is what I have in the Who has visited me (this is where I am struggling so was playing about
#foreach(Auth::user()->profileViews as $view)
{<li>{{ $view->user->name }} </li>}
#endforeach
So we only have two models here, User and Profile, with a many-to-many relationship. ProfileView is actually just a pivot between the two, so a class definition isn't needed. But assuming the timestamp is something you'd like to get access to, you'll need to make allowances for that.
I'd suggest something like this:
class User extends Model {
public function profile() {
return $this->hasOne(Profile::class);
}
}
class Profile extends Illuminate\Database\Eloquent\Model {
public function user() {
return $this->belongsTo(User::class);
}
public function views() {
return $this->hasMany(User::class, 'profile_views', 'profile_id', 'visitor_id')
->withPivot('created_at');
}
}
Now, to add a profile view you can edit your controller method like this (I assume the $user is the user whose profile is being viewed.) Rather than creating an instance of the pivot, you attach the relationship. I've detached previous relationships, assuming you only want to keep the most recent one.
public function profile(User $user)
{
$user->profile->views()->detach(Auth::id());
$user->profile->views()->attach(Auth::id());
return view('members.users.profile', compact('user'));
}
And to retrieve the information:
<ul>
#foreach(Auth::user()->profile->views as $view)
<li>{{ $view->name }} # {{ $view->pivot->created_at }} </li>
#endforeach
</ul>
This is all a bit verbose due to your naming not matching Laravel conventions, and your choice to store profile_id in the pivot table instead of user_id but should do what you need.
Related
I'm creating an taggable table like so:
Schema::create('taggable', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tag_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->unsignedBigInteger('company_id');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->unsignedBigInteger('created_by')->nullable();
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->timestamps();
});
As you can see, next to connecting tags to a Post, Video etc (as per the Laravel docs example), I'd also like to ensure that the row that's added is connected to a Company and User model so I can keep track who it belongs to and who created it, but even more so access properties from those models in controllers and views.
I know that in my Post model I can do:
public function tags()
{
return $this->morphedByMany(\App\Models\Tag::class, 'taggable')->withPivot('created_by', 'company_id', 'created_at');
}
The problem is that this will retrieve just the value for created_by and company_id and not the Eloquent model. Is this possible?
So what I'd like to do is access properties of those relationships in controllers and views like so:
$post = Post::findOrFail(1);
foreach($post->tags as $tag) {
$tag->created_by->name // accessing 'name' property on the `User` model
}
foreach($post->tags as $tag) {
$tag->company->address // accessing `address` property on the `Company` model
}
You must do like below:
first you must define relationship between tags and users
class Tags extends Model
{
public function taggable(): MorphTo
{
return $this->morphTo();
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}
then for achieve that you want you must:
$post = Post::first();
$users = $post->tags()->with('createdBy')->get();
I have a table of threads, and on the index page I want to display the latest comment of the thread. So far I have selected the latest comment and try to display it on the page, but it only displays the latest comment overall, not the latest for the specific post
So my ThreadsController looks like this right now, selecting all the comments and displaying the latest one first.
public function index()
{
$threads = Thread::latest()->paginate(10);
$latestComment = Comment::latest()->first();
return view('threads.index', compact('threads', 'latestComment'));
}
Thread model
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
Comment Model
public function user() {
return $this->belongsTo(User::class);
}
public function thread()
{
return $this->belongsTo(Thread::class);
}
public function commentable() {
return $this->morphTo();
}
So how do I select the latest comment from the specific thread and display it on the index?
Edit:
Controller:
public function index()
{
$threads = Thread::latest()->with('comments')->paginate(10);
return view('threads.index', compact('threads'));
}
Index blade:
#foreach($threads as $thread)
{{ $thread->latestComment->user->name }}
#endforeach
Comments table migration
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->string('body');
$table->unsignedBigInteger('commentable_id');
$table->string('commentable_type');
$table->timestamps();
});
}
You have defined your relationship between a Thread and Comment correctly, but you're not making use of that relationship in the index function of your ThreadsController. You're just grabbing the latest 10 Threads and the most recent Comment, but not the comment related to your Thread.
What you're wanting is to get all your Threads and pass them to your view with their latest Comment.
You can use eager loading to attach your Comment relationships to your models.
Thread::latest()->with('comments')->paginate(10);
That will grab your 10 latest Threads along with their Comments. So in your view you could do something like:
#foreach ($threads as $thread)
{{ $thread->comments()->latest('id')->first()->comment }}
#endforeach
Whilst this works, it's a bit verbose. So what you can do is piggy back off your comments function to return just the most recent Comment as a relationship.
class Thread extends Model
{
use HasFactory;
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
public function latestComment()
{
return $this->morphOne(Comment::class, 'commentable')->latest('id');
}
}
This gives you shorter method for accessing the latest Comment for a Thread.
So going back to your scenario;
In your ThreadsController.php
public function index()
{
$threads = Thread::latest()->with('latestComment')->paginate(10);
return view('threads.index', compact('threads'));
}
Then in your index.blade.php file
#foreach ($threads as $thread)
{{-- Access whatever properties you have on your comment --}}
{{ $thread->latestComment->id }}
#endforeach
You can create a relation between tread and comment.
And then in blade you cam do something like this:
$thread->comments->latest()->first()
Comment::all()->orderBy('id', 'DESC')->get()
There are two users in my users table user1 and user2
the user2 wants to rate user1
i have created a Rating table
which is as
Schema::create('ratings', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id');
$table->string('rating')->nullable();
$table->string('review')->nullable();
$table->integer('rated_user_id');
$table->timestamps();
});
i have also created a model named Rating and defined a realtionship to the user
such as
public function user()
{
return$this->hasMany(User::class);
}
the code in my user model is as follow
public function Rating()
{
return $this->hasOne(Rating::class);
}
the code inside Ratingcontroller store function is as follow
public function store(Request $request)
{
//
$rating = new Rating;
$rating->fill($request->all());
$rating->user_id = Auth::id();
$rating->save();
return redirect()->back();
}
the problem is that in the rating table
every things looks fine
user_id is getting the id who is trying to post comment
and rated_user_id is givving the id to which the user is giving rating too
but the user is only able to rate himself not other users ..
The image in db is correct and Your store method in RatingsController is also works as expected.
So You want from us to give You code example to ProfileController method to get ratings provided by user and provided to user.
1) You've to add to User model following relation:
public function ratingsProvidedByMe()
{
return $this->hasMany(Rating::class, 'user_id', 'id');
}
public function ratingsProvidedToMe()
{
return $this->hasMany(Rating::class, 'rated_user_id', 'id')
}
2) in Rating model:
public function ratedBy()
{
return $this->belongsTo(User::class, 'user_id');
}
public function ratedTo()
{
return $this->belongsTo(User::class, 'rated_user_id');
}
3) in Your ProfileController:
public function view(Request $request)
{
$ratingsProvidedByMe =
Rating::with('ratedTo')
->where('user_id', Auth::id())
->all();
$ratingsProvidedToMe =
Rating::with('ratedBy')
->where('rated_user_id', Auth::id())
->all();
$me = $user = Auth::user();
return view(
'profiles.view',
compact(
'ratingsProvidedByMe',
'ratingsProvidedToMe',
'user', 'me'
)
);
}
P.S. it's just an example, adapt my example to Your project Yourself.
I am attempting to return which user posted a comment, along with the time they posted the comment.
I have a model for comments
use Illuminate\Database\Eloquent\Model;
class comments extends Model
{
protected $guarded = [];
public function adjustments()
{
return $this->belongsToMany(User::class, 'adjustments')
->withTimestamps();
}
}
A pivot table which tracks which users posted which comments
public function up()
{
Schema::create('adjustments', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id')->index();
$table->unsignedInteger('comments_id')->index();
$table->timestamps();
});
}
An empty adjustments model
use Illuminate\Database\Eloquent\Model;
class adjustment extends Model
{
//
}
In php artisan tinker when $comment = App\comment::first(); and $user = App\user::first(); I'm able to successfully attach a user_id to a comment_id using $comment->adjustments()->attach($user->id) and calling App\adjustments::all(); will correctly return
=> Illuminate\Database\Eloquent\Collection {#2935
all: [
App\adjustment {#2941
id: 1,
user_id: 1,
comments_id: 1,
created_at: "2019-11-16 17:05:10",
updated_at: "2019-11-16 17:05:10",
},
],
}
When I'm trying to show the adjustments in my view, I get an empty list.
#foreach ($comment->adjustments as $user)
<li>{{$user->name}} on {{$user->pivot->updated_at}}</li>
#endforeach
In my products controller (a user makes comments on products) I have the following code in my show function
public function show(products $product, comments $comment, User $user)
{
return view ('products.show', compact('product'), compact('comment'));
}
Here you don't need a pivot table. Becasue here you has one to many relation. User can create many comment. & one comment is belongs one user.In user model add this
public function comments()
{
return $this->hasMany(Comment::class);
}
& comments table u have a foreign key user_id.
in comment model
public function user()
{
return $this->belongsTo(User::class,'user_id','id')
}
public function show(products $product, comments $comment, User $user)
{
$comments=Comment::with('user')->all();
return view ('products.show', compact(['product','comments']));
}
#foreach ($comments as $comment)
<li>{{$comment->user->name}} on {{$comment->updated_at}}</li>
#endforeach
Then you can acces all comments table data with a user
I need to make an advice platform for products. User can make advices under product section. So product has many advices. Also advices belongsTo product. But on product_advices table i have product_id and product_advice_id these both refers to id on products table.
So here is the problem. I can take advices from product_advices table which refers to product_id. But how can i take the other one as product.
product->advices to show advice and user message
each advices as advice and advice->product->name to show adviced product name
I couldn't make a relationship with eloquent between them.
//Product Model
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
//ProductAdvice Model
protected $table = 'product_advices';
public function product() {
return $this->belongsTo('App\Product', 'product_id');
}
//Product Advice Table
Schema::create('product_advices', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->text('body')->nullable();
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('product_advice_id')->unsigned();
$table->foreign('product_advice_id')->references('id')->on('products')->onDelete('cascade');
$table->timestamps();
});
For example :
Iphone has many advices by users.
Iphone->advices brings the advices from product_advices that Iphone belongs to product_id column.
When User recommends Samsung Galaxy S10 to Iphone. Samsung refers to product_advice_id column on product_advices table. But how to show Samsung Galaxy S10 as a product.
$advice->product->name returns Iphone instead of Samsung Galaxy S10.
EDIT 2
After your answer, I understood what you want.
Just update your ProductAdvice model to have another relationship, like:
class ProductAdvice extends Model
{
public function product()
{
return $this->belonsTo('App\Product', 'product_id');
}
public function related()
{
return $this->belonsTo('App\Product', 'product_advice_id');
}
}
Then you can do:
$product = Apple
$product->advices->message = 'blablabla'
$product->advices->related = 'ProductX'
If you need to to the inverse order, $advice->product->related add the same relationship on your Product model.
EDIT 1
Sorry, you edited the post after my answer...
Could you explain the 'product_advice_id' necessity? You already have the product_id.
Unless you want a pivot table, like: Product -> ProductAdvice -> Advice, which is not needed in your case, since you can just put the advice information in the Advice table and link it to the product (belongsTo), removing the necessity of the ProductAdvice pivot.
I think your structure should be like this:
Product model with all the product data
ProductAdvice model with product_id and the advice information (message, rating, etc)
Then, your Product hasMany Advices and your Advice belongsTo Product:
class Product extends Model
{
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
}
class ProductAdvice extends Model
{
public function product()
{
return $this->belonsTo('App\Product', 'product_id');
}
}
Finally, you can query the advices of a specific Product to get the advice information:
$product->advices->message
$product->advices->rating
Or query the product name if you have an advice:
$advice->product->name
So, If understand well, you want and advice from product A to return the name of product B.
You can make this by creating multiple method related to the same model.
Your models will look similar at this:
class Product extends Model
{
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
public function relations()
{
return $this->hasMany('App\ProductAdvice', 'related_id');
}
}
class ProductAdvice extends Model
{
public function product()
{
return $this->belonsTo('App\Product', 'product_id');
}
public function relation()
{
return $this->belonsTo('App\Product', 'related_id');
}
}
Then you will have to create another column in your talbe name :related_id (it could be something else, just made it match in your model. You can also change relations() and relation() method to whatever name you want.)
After this it's how you store your data. You have to make your code associate the good product model into your product_id and into related_id. This way. You can have $advice->product->name === 'iPhone' && $advice->relation->name === 'Samsung S10'
Found a solution like this for now.
//ProductAdvice Model
protected $table = 'product_advices';
public function user()
{
return $this->belongsTo('App\User');
}
public function product() {
return $this->belongsTo('App\Product', 'product_id');
}
public function advicedProduct()
{
return Product::where('id', $this->product_advice_id)->first();
}
//Product Model
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
How i show it on view
#foreach($product->advices as $advice)
<li>{{ $advice->body }} - {{ $advice->advicedProduct()->name }}</li>
#endforeach