issue with laravel elquent relation - laravel

I have a column name creator in the table storing the user id. When fetching records, I am using belongs to relation. I receive data, but I am not able to display it.
Note: This thread doesn't solve my issue
Category table scheme
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->string('slug')->unique();
$table->string('banner')->nullable();
$table->boolean('status')->default(false);
$table->bigInteger('creator');
$table->bigInteger('moderator');
$table->timestamps();
});
user table scheme
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Category Model:
public function creator(){
return $this->hasOne(User::class, 'id', 'creator')->select('id', 'name');
}
Category Controller code:
$records = Category::with(['creator'])->paginate(env('REC_LIMIT'));
data I get it:
"data":[{"id":1,"name":"Uncategorized","slug":"uncategorized","banner":null,"status":1,"creator":{"id":1,"name":"demon slayer"},"moderator":1,"created_at":"2019-11-03 12:08:33","updated_at":"2019-11-04 11:11:01"},
note if with clause is removed in the query, I get:
"data":[{"id":1,"name":"Uncategorized","slug":"uncategorized","banner":null,"status":1,"creator":1,"moderator":1,"created_at":"2019-11-03 12:08:33","updated_at":"2019-11-04 11:11:01"},
in blade file, I what am doing is below code to print creator user name instead of their record id.
$record->creator->name
//or
$record->creator[0]->name
currently i get this:
Facade\Ignition\Exceptions\ViewException
Trying to get property 'name' of non-object (View: /Users/dragonar/Dev/pdp/resources/views/backend/category/index.blade.php)

Have you tried instead of
public function creator(){
return $this->hasOne(User::class, 'id', 'creator')->select('id', 'name');
}
This:
public function creator(){
return $this->hasOne(User::class, 'user_id')->select('id', 'name');
}
Or change the name of the function from creator() to user().
And then in your blade $record->user->name.
This what I found on the laravel docs:
Eloquent determines the foreign key of the relationship based on the model name. In this case, the Phone model is automatically assumed to have a user_id foreign key. If you wish to override this convention, you may pass a second argument to the hasOne method: return $this->hasOne('App\Phone', 'foreign_key');
Source

I ended up renaming creator to creator_id and in model, function is creator. like this now i can access desired data $record->creator->name or $record->creator->id.
what i wanted was instead of creator returing me id from original code, i could have been able to do $record->creator->name or $record->creator->id, directly.

Related

How to take out subscription list in Larvel?

I'm creating project with Laravel. I want to pull out user->name from subscription table which also has user_id column in it. I want to show the current user's subscribers' name from subscription table. But it shows error:
Property [subs] does not exist on this collection instance
here is my code.
This is from IndexController
public function home() {
$data = User::all();
return view("amvs.index", [
'users' => $data,
]);
}
This is from index.blade.php
Subscriptions
#foreach ($users->subs as $sub)
<span class="ms-2">{{$subs->name}}</span>
#endforeach
This is from user model
public function sub(){
return $this->hasMany('App\Models\Subscription');
}
I didn't write anything in Subscription model. My subscription table
Schema::create('subscriptions', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('user_id');
$table->timestamps();
});
My user table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
I'm a beginner so enlighten me.
The issue is that you're trying to access a method on an Eloquent Collection which does not have any properties or methods relatated to your User or its sub relationship.
Additionally, consider using Eager Loading when accessing relationships, it is more performant and solves the N+1 query problem.
public function home() {
// access the authenticated User and load their subs relationship
$user = Auth::user()->with('subs')->first();
return view("amvs.index", [
'user' => $user
]);
}
Then to access the Users subs in your view:
#foreach ($user->subs as $sub)
{
<span class="ms-2">{{$sub->name}}</span>
}
However, you will want to change your subscriptions migration as the user_id field needs to be the same type as the id field on your users table.
Schema::create('subscriptions', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('user_id');
$table->timestamps();
});
Further questions
don't know why I should use foreignId?
For relationships to work, the field type (string, integer etc.) of the primary and foreign key fieds must be the same. The default primary key in Laravel is the id field and it has as type of unsigned big integer. If you define a foreign key field in a related table and its type is anything other than unsignedBigInteger the relationship will fail.
foreignId is simply a helper method provided by Laravel that creates a field with the correct type and the required foreign key reference. Previously you had to do that all yourself, for example:
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
why you used first()?
The reason for using first() is that it returns a Model rather than an Eloquent Collection. You're only interested in one User (the authenticated User) so it is more efficient to use first() rather than get() as you don't then need to loop or extract the first item in the collection.
You might want to spend some free time watching the Laravel from Scratch series. It is really detailed and covers lots of common scenarios.
First of all, In your controller you should load the subs. That will be better as it will not add extra query for loading the relations. So what you can do is following
$data = User::with('subs')->all();
you should also change the relationship as hasMany follows the following convention:
public function subs(){
return $this->hasMany('App\Models\Subscription');
}
As you are pulling all the users you have to first loop through the users first, then you can iterate over subs for each users like below:
#foreach ($users as $user)
#foreach ($user->subs as $sub)
<span class="ms-2">{{$subs->name}}</span>
#endforeach
#endforeach

Laravel 8 - Foreign key constraint is incorrectly formed

I don't know what's wrong because I'm very new to this.
// Product Model
class Product extends Model
{
use HasFactory;
public function store()
{
return $this->belongsTo(Store::class);
}
}
// Store Model
class Store extends Model
{
use HasFactory;
public function products()
{
return $this->hasMany(Product::class);
}
}
// Products table migration
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->float('price');
$table->string('description');
$table->timestamps();
$table->foreignId('store_id')->constrained()->onDelete('cascade');
});
// Stores table migration
Schema::create('stores', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('image_url');
$table->string('phone');
$table->timestamps();
});
When I run the migration, it gives me this error
I've tried changing the data type of the 'id' but still not working. I've also tried with
$table->foreign('store_id')->references('id')->on('stores')->onDelete('cascade');
but still not working.
What I want is a relation so that when I delete a store, all products that belong the store are also deleted.
Thanks 🙏
Change the name of the stores migration file to a date prior to 2021-07-28 so the table stores is migrated before the table products
Example: 2021_07_27_004700_create_stores_table
Laravel uses the name of the migration files for the order of migration. With the format of the date as the start of the file name, it is dependant on the date of the creation of the file.

Call to a member function post() on null on many to many relationship

I have created two table to store post information:
Posts table where I store overall information except categories.
Migration file of each table looks like this:
posts
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->string('slug');
$table->longText('excerpt');
$table->longText('description');
$table->tinyInteger('feature')->default(1);
$table->enum('status',['publish','draft']);
$table->string('image');
$table->timestamps();
});
post_categories
Schema::create('post_categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('category_id')->nullable();
$table->unsignedBigInteger('post_id')->nullable();
$table->foreign('category_id')->references('id')->on('categories');
$table->foreign('post_id')->references('id')->on('posts');
$table->timestamps();
});
Now, I have following function in PostCategory model:
public function post(){
return $this->belongsToMany(Post::class);
}
Now, when I try to fetch data using following:
\App\Models\PostCategory::find(5)->post()->orderBy('created_at')->get();
I get call to member function post() on null
What should I do to get post with the category 5?
You misunderstood some concepts.
Here your relationship is between post and categories.
In post Model
public function categories(){
return $this->belongsToMany(Category::class,'post_categories','post_id','category_id');
}
In the Category model
public function posts(){
return $this->belongsToMany(Post::class,'post_categories','category_id','post_id');
}
Now, You're free to access posts from category and categories from post.
$post = Post::with('categories')->where('id',5)->first();
By this, you'll get a post with its categories.
$category= Category::with('posts')->where('id',5)->first();
By this, you'll get category with its posts.
Check more detail how many to many work
Edit:-
If by using post you want to make a condition in the categories then.
$post = Post::with('categories')->whereHas('categories', function($q){
$q->where('id',5);
})->get();

Relationship to users table without using the standard 'user_id' column name

I have 2 tables, one has different columns to record different users names based on authorisation level. but i would like to link to two together. at the moment i have tried the following:
User.php
public function approvals()
{
return $this->hasMany(Approval::class);
}
Approval.php
public function qs() {
return $this->belongsTo(User::class, 'id', 'qs');
}
index.blade.php
<td>{{ $approval->qs->name }}</td>
approvals db structure
Schema::create('approvals', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('project_id');
$table->integer('stage');
$table->unsignedBigInteger('qs')->nullable();
$table->unsignedBigInteger('pm')->nullable();
$table->unsignedBigInteger('rcm')->nullable();
$table->unsignedBigInteger('doc')->nullable();
$table->unsignedBigInteger('vpoc')->nullable();
$table->unsignedBigInteger('vpof')->nullable();
$table->timestamps();
});
users db structure
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email', 100)->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Am i going about this all wrong, the qs table column needs to be linking to the users.id?
It seems qs is the user id of the User model. So the relation to the Approval model is
public function qs()
{
return $this->belongsTo(User::class, 'qs');
}
And in User model
public function approvals()
{
return $this->hasMany(Approval::class, 'qs');
}
Now you can use
{{ $approval->qs->name }}
Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with a _ followed by the name of the primary key column. However, if the foreign key on the Model is not parent_id, you may pass a custom key name as the second argument to the belongsTo method.
Laravel Documentation
If a parent model does not use id as its primary key, or you want to join the child model to a different column, you may pass a third argument to the belongsTo method:
public function qs() {
return $this->belongsTo(User::class, 'foreign_key_here_from_child_table', 'custom_column_from_parent_table');
}

Laravel hasOne (one to one relationship)

I am trying to learn Laravel-> one to one relationship.
In given code link(join) should be dependent on name(user2s table) and title(post2s table) but the link(join) is dependent on my_id(user2s table) and title(post2s table)
My full codes
Migrations:-
user2s table
Schema::create('user2s', function (Blueprint $table) {
$table->increments('my_id');
$table->string('name');
$table->string('email');
$table->string('password');
$table->string('remember_token');
$table->timestamps();
});
post2u table:
Schema::create('post2s', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->string('title');
$table->text('content');
$table->timestamps();
$table->tinyInteger('is_admin');
});
Model User2
protected $primaryKey = 'my_id';
public function postx(){
return $this->hasOne(Post2::class, 'title', 'name');
}
My Route Code
Route::get('user/{id}/post', function($id){
return User2::find($id)->postx;
});
http://localhost:8000/user/abc/post
error: Trying to get property of non-object
user2s table
post2s table
Let me explain what is your issue.
error: Trying to get property of non-object, it means it can't find the result. The result object is null, so when you looking for null->postx, it can't get them anything.
You search for User2::find($id), when you use find(), it is looking for primary key. And you User2 Model primary key is my_id, and you are looking for Post2->title. It not able to find it.
More infor about find()
https://laravel.com/docs/5.5/eloquent#retrieving-single-models
Since you are looking for the Post2 title. And you are referencing from User2. It is not correct.
What you should do is
In your route.php
Route::get('user/{title}/post', function($title){
//return Post2::all();
$post = Post2::with('userx')->where('title', $title)->first();
dump($post);
dump($post->userx)//<- you can get user info via
});
In Post2 Model.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post2 extends Model
{
public function userx(){
return $this->belongsTo(User2::class, 'user_id');
}
}

Resources