Laravel 5.2 User/Following System: hasMany Relation - has-many

I'm stuck on a problem: I've created a simple user following system so users can follow each other. I'm using a table named follows to store the relation. I've created a hasMany relationship in my User class, and, when retrieving the results I'm getting exactly what I expect, however, I'd like to get some additional information from the users table, such as the username, avatar, etc. How do I go about this?
The follows table
// following_id is the person being followed
public function up()
{
Schema::create('follows', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('following_id')->unsigned();
$table->timestamps();
$table->unique(['user_id', 'following_id']);
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
The User class
class User extends Authenticatable
{
// a user can have many followers
public function followers()
{
return $this->hasMany(Follow::class, 'following_id');
}
// a user may be following many people
public function following()
{
return $this->hasMany(Follow::class);
}
}
The method I'm calling in UsersController to see the results
// route is /{username}/followers
public function followers($username)
{
$user = User::where('username', $username)->first();
// get the user's followers
$followers = $user->following;
return $followers;
}
The current results are
[
{
id: 24,
user_id: 3,
following_id: 1,
created_at: "2016-02-13 11:42:59",
updated_at: "2016-02-13 11:43:02"
}
]
However, I'd like them to be the following: where fredflintstone is the user with ID of 1; e.g.; the user who is following user 3
[
{
id: 24,
user_id: 3,
following_id: 1,
following_username: 'fredflintstone',
created_at: "2016-02-13 11:42:59",
updated_at: "2016-02-13 11:43:02"
}
]
I've also created a Follow model, which is currently empty. I tried adding an inverse belongsTo relationship in it, but it didn't work. Mayhaps I did it wrong?

I have been using laravel for a long time,and i suggest you use join instead if laravel built in model for 'with'
Here is the code of using join, modify it according to your use
public function followers($username)
{
$user = User::where('username', $username)->first();
//Follower your model name for the follows table
//if you dont have a model use DB::table('follows') instead
$followers=Follower::where('user_id',$user->id)
->join('users as follower','follower.id','=','follows.following_id')
->select('follows.id','follows.following_id','follows. user_id','follower.name as following_username')->get();
return $followers;
}

Figured it out. I needed to join the users table to the query.
public function following()
{
return $this->hasMany(Follow::class, 'user_id')
->join('users', 'users.id', '=', 'follows.following_id')
->select('user_id','username');
}

Related

Define additional relationship on many-to-many polymorphic relationship

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();

laravel access related model using mutator during $user->posts()->create([...])

I have User and Post models.
Would like to access the User when the mutator for the post's title is being called at the time of the post creation. Similar to a question from a few years ago.
But I am unable to access the User inside the Posts title mutator.
Tried $this->user->id (preferred). Also tried: $this->user_id, $this->user()->get()->id.
>>> User::first()->posts()->create(['title'=>'test '.now()])
PHP Warning: Attempt to read property "id" on null <<<=== when trying $this->user->id
=> App\Models\Post {#4155
title: "test 2021-05-08 11:41:55", <<<=== title is shown before user_id
user_id: 1, <<<=== but in migration user_id is defined *before* title
updated_at: "2021-05-08 11:41:55",
created_at: "2021-05-08 11:41:55",
id: 1,
user: null, <<<=== note "user" attribute is added as result of the $this->user->id
}
Would think the user is definitely known at that time, as we're using that user to add a post to.
If I make the user_id fillable, then this one works as expected (for that one: the array order matters):
Post::create(['user_id'=>User::first()->id, 'title'=>'test '.now()])
But, that's less... eloquent.
My config + test details:
// Post.model
protected $fillable = [
// 'user_id', // also tried with making user_id fillable
'title',
];
public function setTitleAttribute($value) {
// dump($this->user_id); // null
// dump($this->user); // null (& adds empty user property to this)
// dump($this->user->id); // PHP Warning: Attempt to read property "id" on null (& adds property)
// dump($this->user()->get()); // empty collection Illuminate\Database\Eloquent\Collection
// dump($this->user()->toSql()); // select * from "users" where "users"."id" is null
$this->attributes['title'] = $value;
}
public function user() {
return $this->belongsTo(User::class);
}
// User.model
public function posts() {
return $this->hasMany(Post::class);
}
// posts migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('title');
$table->timestamps();
});
Using mysql as DB.
This seems to be the safest + cleanest: use boot event closure 'creating', which is triggered after the mutators and right before data is saved to the database.
public static function boot() {
parent::boot();
static::creating(function (Post $post) {
// now we have both $post->user and $post->title
dump($post->user->name);
dump($post->title);
});
}
can you provide more detail?
try this
auth()->user()->id
if user is logged in
if u want select user from db
User::where("filed name","value")->first()->id

View is not returning attributes stored in database

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

How to properly implement this polymorphic relationship in Laravel?

I am trying to build an inventory for users in Laravel 5.8, however the items have their own properties, therefore I needed to set up a polymorphic relationship. When attaching items to users, it tries to add the model User to the table on itemable_type and the user's ID to itemable_id aswell as add the User's ID to user_id, something I could workaround by passing the models I need, but when I try to retrieve them it tries to find item with itemable_type = 'App\Models\User', which makes me think something's completely wrong here. Can I have some orientation on how to solve it?
class User extends Model
{
public function inventory()
{
return $this->morhpToMany(InventoryItem::class, 'itemable', 'user_inventories', null, 'itemable_id')
->withPivot('amount', 'notes');
}
}
class InventoryItem extends Model
{
public $timestamps = false;
protected $table = 'character_inventories';
protected $fillable = [
'character_id', 'itemable_type', 'amount', 'parent_id', 'notes'
];
public function cloth()
{
return $this->mophedByMany(Cloth::class, 'itemable');
}
public function food()
{
return $this->morphedByMany(Food::class, 'itemable');
}
// Other similar relations
}
// The Inventory migration:
Schema::create('user_inventories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->unsignedInteger('itemable_id');
$table->string('itemable_type');
$table->unsignedInteger('amount')->default(0);
$table->text('notes', 65535)->nullable();
$table->foreign('character_id')->references('id')->on('characters');
});
The expected result is the User model to have different items in his inventory, but the relation is trying to query by joinning to itself and filtering by user type instead of actual items.
The error:
Syntax error or access violation: 1066 Not unique table/alias: 'user_inventories' (SQL:
select `user_inventories`.*,
`user_inventories`.`itemable_id` as `pivot_itemable_id`,
`user_inventories`.`itemable_type` as `pivot_itemable_type`,
`user_inventories`.`amount` as `pivot_amount`,
`user_inventories`.`parent_id` as `pivot_parent_id`,
`user_inventories`.`notes` as `pivot_notes`
from `user_inventories`
inner join `user_inventories` on `user_inventories`.`id` = `user_inventories`.`itemable_id`
where `user_inventories`.`itemable_id` in (4)
and `user_inventories`.`itemable_type` = App\Models\User)
I highly suspect that you have to references the user table in the inventory relation. In general it is a million times easier just following the Laravel convention for naming.
public function inventory()
{
return $this->morhpToMany(InventoryItem::class, 'itemable', 'users', null, 'itemable_id')
->withPivot('amount', 'notes');
}

Laravel 5.6 eager load nested childrelations

I'm having an issue with getting child relations from a Card object to a User object. The relations are as following:
'User' -> hasOne 'Company'
'Company' -> hasMany 'Card'
So the other way around is:
'Card' -> belongsTo 'Company'
'Company' -> belongsTo 'User'
My Card model has got this:
public function company()
{
return $this->belongsTo('App\Company');
}
My Company model has got this:
public function user()
{
return $this->belongsTo('App\User');
}
public function cards()
{
return $this->hasMany('App\Card');
}
My User model has got this:
public function company()
{
return $this->hasOne('App\Company');
}
What I want to do is: in the User model I want to eager load all cards from that user. So my code looks like this:
$cards = Card::with('company.user')->get();
But it's constantly returning me all the card records in the database, not the ones that are from the logged in user itself. There definitely
is a userID, cause when I dump $this->id in the User model, I'm getting the ID '1'. In the database I've configured all foreign keys so that won't be the problem I assume.
Table 'cards' has a foreign key to 'company_id', and table 'companies' has a foreign key to 'user_id', they are all set by the migration script, which looks like this:
Schema::create('cards', function (Blueprint $table) {
$table->increments('id');
$table->integer('amount');
$table->string('status');
$table->unsignedInteger('company_id');
$table->timestamp('expires_at')->nullable();
$table->boolean('is_debit_allowed')->default(1);
$table->string('cancelled_by')->nullable();
$table->timestamp('cancelled_at')->nullable();
$table->timestamps();
$table->foreign('company_id')->references('id')->on('companies');
});
What am I doing wrong guys?
In the User model, you can add it to the $with array:
// this will eager load the company and cards for every user query, so beware!
protected $with = ['company.cards'];
Or create a new function cards:
public function cards()
{
return $this->company->cards;
}
$cards = $user->cards();
// use the auth helper to get logged in user's cards
$cards = auth()->user()->cards();
This should work for accessing via Card:
$cards = Card::whereHas('company.user', function ($query) {
$query->whereKey(auth()->id());
})->get();

Resources