One-to-one relations that only go one way in Laravel - laravel

Is it possible/right to have a a relation between tables that only goes one way? I have an invoices table that I need to reference in other tables likes commission_payments and membership_payments but the invoices table does not need a commission_payment_id or a membership_payment_id. In other words, there different types of transactions that can happen and they all may have a invoice attached, but the invoice does not need to reference these transaction tables.
invoices commission_payments membership_payments
--------------- --------------------- ---------------------
-id -id -id
... -invoice_id -invoice_id
... ...
I have created Eloquent models for each table. I added a hasOne relation to invoices on the other two models.
class CommissionPayment extends Model{
public function invoice(){
return $this->hasOne('App\Models\Invoice');
}
}
I then tried accessing the Comission Payment's attached invoice like this:
$com = CommissionPayment::first();
$com->invoice->id;
I then get this error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'invoices.commission_payment_id' in 'where clause' (SQL: select * from `invoices`
where `invoices`.`commission_payment_id` = 15 and `invoices`.`commission_payment_id` is not
null limit 1)
Why is it looking for commission_payment_id field in the invoices table? I would expect a query sort of like this:
SELECT * FROM `invoices` WHERE `id` = 23
/* id is fetched from the `invoice_id` field in the `commission_payments` table */
Do I HAVE to add a column for each table that will reference invoice_id? At the moment it's two but this could grow. Also, when an invoice was generated for a commission payment, it won't need the membership payment field so I don't think it should go there.

A one-to-one relationship is setup using either the hasOne or belongsTo relationship. The hasOne side of the relationship assumes the foreign key is stored on the other table. The belongsTo side of the relationship has the foreign key.
Therefore, since your foreign key to the invoices are stored on the commission_payments and membership_payments tables, those models are said to belong to the invoices table.
Change your relationship from hasOne to belongsTo and you should be good to go:
class CommissionPayment extends Model{
public function invoice() {
return $this->belongsTo('App\Models\Invoice');
}
}

Related

How to get data from third table in eloquent relationships eloquent many to many relationship

I am using Eloquent ORM and I have Book model which connect to BookCategory model and BookCategory connect to Category. The l'm problem facing is how to include data from third table in eloquent relationships?
Book
id
name
Category
id
name
type
BookCategory
id
book_id
category_id
Lets say for example you want to get all the books of a certain category:
assuming your pivot table name is Book_Category
in your Category model:
public function books()
{
return $this->belongsToMany('App\Models\Book', 'Book_Category', 'category_id', 'book_id');
}
and you can eager load category books like :
$categories = Category::get()->load('books');
//OR
$categories = Category::with('books')->get();
If I understand you right, you are looking for the pivot attribute. This makes additional columns of the intermediate table available.
https://laravel.com/docs/8.x/eloquent-relationships#retrieving-intermediate-table-columns

Laravel: how to sort a collection with hasMany field

I have five tables:
details having fields id, name and has hasMany relation with contributions.
purpose having fields id, name and has hasMany relation with contributions.
period having fields id, name and has hasMany relation with contributions.
user having fields id, name and has hasMany relation with contributions.
and
contributions having id, amount and "belongsTo" relation with user_id, detail_id, purpose_id, period_id
Now, I want to get all contributions of a specific user (filtered by user_id) and then sort the result by period.name, detail.name and purpose.name.
Any suggestions?
The question has been identified as possible duplicate of Laravel orderBy on a relationship . However there is a basic difference.
there, there is relation between user and comment & user and post. however in my case, there isn't any relation between user and details/purposes/periods tables.
<?php
class User
{
public function comments()
{
return $this->hasMany('Comment');
}
}
is possible in that case, but by same analogy, in my case, class User doesn't have any details() function to get hasMany relations. My user has relation with only contributions table and contributions table has relations with details purpose etc tables.
hope I am able to clarify the difference.

Invalid argument supplied for foreach() in laravel when use Eloquent: Relationships

I wanna get the class name of a user via the user ID. When I input the ID of a user so I will wanna get the class name. I have three tables such as users table, classes table, and class_users table. The class_users table is born from two users table and classes table.
A users table has an id, name, email, password.
A classes table has an id, class_code, class_name.
A class_users table has an id, class_id, user_id
And this problem relates to Eloquent Relationships.
Thank you for help.
My Route:
Route::get('/find_classes/{id}','StudentController#find_classes');
My Controller function:
public function find_classes($id)
{
$users = User::find($id);
foreach($users->classes as $class)
{
echo $class->name . '<br';
dd($class);
}
}
My User Model:
public function classes()
{
return $this->belongsTo('App\Classes','class_users','user_id','class_id');
}
Looks like you might have the wrong relationship set up on your User model. You have a one to many set up, but your DB is setup to handle a many to many. I suggest you change your User model relationship:
public function classes()
{
return $this->belongsToMany('App\Classes');
}
Note, you may need to name the FK on that relation, as I see you have class_id on the table, but your actual class is named 'Classes'. Check through your relationships to ensure this is explicit on the FK where it doesn't follow Laravel convention exactly.
With this relationship, your foreach loop should work. It would be a good idea for efficiency, as mare96 noted, to eager load the classes on the $users collection when you query:
$users = User::with('classes')->find($id);

Laravel Where Condition on Grand Parent Eloquent Relation

I have 4 tables:
Account (id, name)
Type (id, account_id, name)
Category (id, type_id, name)
Money (id, category_id, date, amount)
And I defined the relations at model
But my problem is how to get money data with account id 2?
Account::find(2)-> type()-> category()->money
Is not working
Assuming you created your relationships, you can do it this way:
$account = Account::with('type.category.money')->find(2);
and to display money you can now use:
echo $account->type->category->money->amount;
In above echo I of course assume for each record you have data in all those tables. If not, you'll need to add extra checking to make sure you don't display property for null
You can also go about this from the other direction if you only need the final result of 'money' and have the inverse relationships setup on the models.
$money = Money::whereHas('category.type.account', function ($q) use ($id) {
$q->where('id', $id);
})->get();
// get() or first() depending whether these relationships return many
Laravel Docs - Eloquent Relationships - Querying Relationships - Querying Relationship Existence

Laravel - Many to Many Polymorphic Relation with Extra Fields

How can I define a many to many polymorphic relation with extra fields?
I have three (or more, as it is a polymorphic relation) tables.
tags table: id, name
tagged table: id, tag_id, taggable_id, taggable_type, user_id
posts table: id, record, timestamps
users table: id, name, email
The user_id on the tagged table referes to the users table on column id.
In my Post model I have:
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable','tagged');
}
and in my Tag model I have:
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable','tagged');
}
Then when I am try this in my controller:
$tag = new \App\Tag(
array(
'tag'=>"someTag"
));
$tag->save()
$post = \App\Post::find($id);
$post->tags()->save($tag);
I get Integrity Constraint Violation for not having a user_id:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (hb.tagged, CONSTRAINT tagged_user_id_foreign FOREIGN KEY (user_id) REFERENCES users (id)) (SQL: insert into tagged (tag_id, taggable_id, taggable_type) values (26, 2, App\Resource)).
Which is somewhat expected, as I have never had the chance to define or declare the user_id field.
Also, I have tried withPivot() on tags relation as follows, to no avail:
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable','tagged')->withPivot('user_id');
}
Like in the comment: withPivot has nothing to do with saving/creating. If you want to pass additional pivot data when saving, do this:
$post->tags()->save($tag, ['user_id' => $userId]);

Resources