Querying with relationship not working - php

I am trying to grab an InvoiceDetails record and the matching Product record via the product foreign key.
This isn't working:
$r = InvoiceDetail::with('products')->find(52184)->toArray();
The 2 database calls are
SELECT * FROM `invoice_details` WHERE `id` = '52184' LIMIT 1
SELECT * FROM `products` WHERE `products`.`id` in ('0')
Where am I going wrong?
Table Structure of invoice details:
Schema::create('invoice_details', function (Blueprint $table) {
$table->increments('id');
$table->integer('invoice_id')->unsigned();
$table->integer('product_id')->unsigned();
$table->integer('quantity');
$table->foreign('product_id')->references('id')->on('products')->onDelete('restrict')->onUpdate('cascade');
$table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade')->onUpdate('cascade');
});
Table structure for Products:
Schema::create('products', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
});
Products Model:
class Product extends \Eloquent
{
public function products()
{
return $this->hasMany('InvoiceDetail');
}
}
Invoice Details Model:
class InvoiceDetail extends \Eloquent
{
public function details()
{
return $this->belongsTo('Invoice');
}
public function products()
{
return $this->belongsTo('Product');
}
}

Your relationships are weird. (Okay, that wasn't really any longer.)
Assuming that an Invoice can belong to many Products (with specific details about each such as quantity), and that a Product can belong to many Invoices, you have a classic pivot table scenario. In which case, you're doing extra work and making life more difficult for yourself than it has to be.
If that's the case, there are a few steps you can take to reduce your code and make life easier:
Remove the InvoiceDetails model. Laravel can handle pivot tables on its own pretty well. So unless you have something really custom that you need the pivot table model to handle, you don't need it.
Update your Product model. You have a products() method in the Product model. That doesn't really make any sense. Don't products belong to invoices? Let's fix that.
class Product extends Eloquent
{
public function invoices()
{
return $this->belongsToMany('Invoice', 'invoice_details', 'product_id', 'invoice_id');
}
}
The additional parameters indicate the pivot table name, the column name for the Product model identifier, and the column name for the Invoice model identifier, respectively.
Update your Invoice model. You didn't paste it here, but I'll assume it has a relationship for invoice details. If not, well, oops! Because an invoice can belong to many products, essentially the inverse of the products relationship we just defined, it's defined it pretty much the exact same way.
class Invoice extends Eloquent
{
public function products()
{
return $this->belongsToMany('Product', 'invoice_details', 'invoice_id', 'product_id');
}
}
You now have a many-to-many relationship between Products and Invoices, that is retrieved using intuitive relationship methods! Huzzah.
Hey wait, where's my quantity?
You'll have to figure that one out on your own. :)

Got the answer (there is 2 hours of my life I'm never getting back) I had to manually add the fk and pk. So in my InvoiceDetails model it should have looked like this
public function products()
{
return $this->belongsTo('Product', 'product_id', 'id');
}

Related

Laravel Eloquent where statement returns no attributes

So from my previous post, I was advised to start using Eloquent models, which I did.
My end goal, is to print out specific gifts, that belongs to that specific box.
Migrations:
gift_items:
public function up()
{
Schema::create('gift_items', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->float('unit_price');
$table->integer('units_owned');
});
}
gift_campaigns:
public function up()
{
Schema::create('gift_campaigns', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('user_foreignK')->constrained('users');
$table->integer('gift_item_count')->nullable();
$table->string('status');
$table->date('dispatch_date');
$table->date('delivery_date');
});
}
Pivot table:
public function up()
{
Schema::create('campaigns_gifts', function (Blueprint $table) {
$table->foreignId('gift_id')->constrained('gift_items');
$table->foreignId('campaign_id')->constrained('gift_campaigns');
});
}
Controller:
function box($id){
$data = Campaign::with('gifts')->where('id', $id)->get();
return view('DBqueries.boxView', ['data'=>$data]);
}
Error that I receive using this way:
Seems like the updated version is trying to call the gift_campaigns table id, instead of the pivots table campaign_id.
Once again, I need that Request $id would match the pivots table campaign_id, and print out all of the gifts that this specific id holds
First of all as I sense the campaigns_gifts is a pivot table for campaigns and gifts having a Many-to-Many relation. You are doing it completely against the conventions of Laravel.
You generally do not use a Model for pivot table.
You generally do not fetch data from the pivot table directly. Instead use the relation on one of the related Models.
Note: Laravel does allow a Model for a Pivot, and you can query the pivot table directly, just check the documentation.
The correct way:
Pivot
Make a pivot table (that you already have) with column gift_id and campaign_id. i.e., the convention for naming keys as [table_name_singular]_[primary_key_on_table]
Model
One each model, define relationship for the other data as:
Gift.php Model:
public function campaign() {
return $this->belongsToMany(Campaign::class, 'campaign_gift');
}
Campaign.php Model:
public function gifts() {
return $this->belongsToMany(Gift::class,'campaign_gift');
}
since gift have a hasMany relation, the gifts table must contain a foreign key to campaigns table named campaign_id (same as the one on pivot).
Controller
Now in your controller:
function box($id){
$data = Campaign::where('id',$id)->with('gifts')->get();
return view('DBqueries.boxView', ['data'=>$data]);
}
You don't need to tell Laravel which columns, tables etc are you referring to, as long as you follow the conventions, Laravel will magically do things that otherwise would have been much more painful.

Many to Many Relationship with 2 FK referencing from same PK Laravel 8

Hello I am working on a Laravel project, that i have to assign for one Mentorship «Mentoria», one Mentor «Mentor» and one student «Mentorando». The data of the student and the mentor, came from the Users table (i assigned them roles, using Spatie) , and the other table is called «Mentoria» Since there exists a many to many relation i created the pivot table that is called «utilizador_mentoria» and has ID_Mentor, ID_Mentorando (both are FKs coming from the users table),and ID_mentoria (coming from Mentoria table). I defined both models as this:
User Model:
protected $casts = [
'email_verified_at' => 'datetime',
];
public function interesses(){
return $this->belongsToMany(AreaInteresse::class, 'utilizador_interesse', 'id_utilizador', 'id_interesse');
}
public function mentorias(){
return $this->belongsToMany(Mentoria::class, 'utilizador_mentoria', 'id_mentoria', 'id_mentorando', 'id_mentor');
}
ps: I have interesses function with other model, that is working properly. my problem is with the «mentorias»
Mentoria Model:
public function users(){
return $this->belongsToMany(User::class, 'utilizador_mentoria','id_mentor','id_mentorando','id_mentoria');
}
With this, i am trying to get the data from all Mentorias, and the data of the Mentor that is assigned to that that Mentoria, however when i am doing this code on the controller, the data coming from the user appears empty, despite i have the DB filled with data. I tried a echo for testing, and it only shows the data of the Mentoria, and where it should appear the data of the Mentor assigned to that MEntoria, it is empty
the code from the controller:
public function mentorias(){
$mentorias = Mentoria::with('users')->get();
echo $mentorias;
return view('admin/mentorias/admin_mentorias', ['mentorias' => $mentorias]);
}
the output of the echo
[{"id":2,"titulo":"teste","titulo_en":"test","descricao":"fe","descricao_en":"ewfwe","created_at":"2021-12-28T01:32:10.000000Z","updated_at":"2021-12-28T01:32:10.000000Z","users":[]}]
Since as i already said, i already used data from 2 tables with Many to Many relation, however with only 1 FK per PK, and it is working properly, i have no idea why it is not working this way . I already checked for similar questions, however with no luck
Edit:
For testing purposes, i removed the column of one of the two FK that reference from the same PK, and i managed to work, however with this aditional FK i am not managing to make it work . I believe that the problem is with the relation, in the models but i have no idea how to make it work
I rearranged the funcitons in the models as they are now
User Model:
public function mentorias(){
return $this->belongsToMany(Mentoria::class, 'utilizador_mentoria', 'id_mentor', 'id_mentorando', 'id_mentoria');
}
Mentoria Model:
public function users(){
return $this->belongsToMany(User::class, 'utilizador_mentoria','id_mentoria','id_mentorando','id_mentor');
i also tried to took out,example «id_entorando» from the main () and put it after with the «withPivot» method, but it still didn't worked
I don't know if I properly understood the problem but your relationship is not a single "many to many", but two "one to many". In the end I'll show why it's convenient to consider them as two separated relationships.
First: If a user can have multiple mentorships but a mentorship can have only 1 mentor (which I suppose is what's happening), then you should use the "hasMany/belongsTo" pair:
User Model:
public function mentorias(){
return $this->hasMany(Mentoria::class);
}
Mentoria Model:
public function user(){
return $this->belongsTo(User::class);
}
Second: Complete the scenario with the other relationship bewteen the mentorship and the students:
User Model:
public function mentorias(){
return $this->hasMany(Mentoria::class);
}
public function mentoria(){
return $this->belongsTo(Mentoria::class);
}
Mentoria Model:
public function user(){
return $this->belongsTo(User::class);
}
public function mentorandos(){
return $this->hasMany(User::class);
}
There should be a user_id column in the mentorias table that represents the Teacher which the mentorships belong to:
Schema::table('mentorias', function (Blueprint $table) {
$table->unsignedInteger('user_id')->nullable();
});
And there should also be a mentor_id column in the users table that represents the mentorship which the students belong to:
Schema::table('users', function (Blueprint $table) {
$table->unsignedInteger('mentoria_id')->nullable();
});
Third: This double representation of the User Model could lead to confusion, since two different objects (teacher and student) are using the same model but are not meant to have both relationships simultaneously: A teacher shouldn't be mentored and a student shouldn't lead mentorships.
In order to roperly manage Teachers and Students and prevent confusion, you could create additional models that inherit the User Model and define the relationships for them (instead of the User Model), so you can limit the fields and the relationships for each one of them.
User Model:
// No relationships
Teacher Model:
// Extending from User allows you to have all the User Model functionality
class Teacher extends User
{
public function mentorias(){
return $this->hasMany(Mentoria::class)->with('students');
}
}
Student Model:
// Extending from User allows you to have all the User Model functionality
class Student extends User
{
public function mentoria(){
return $this->belongsTo(Mentoria::class)->with('teacher');
}
}
Mentoria Model:
public function teacher(){
return $this->belongsTo(Teacher::class);
}
public function students(){
return $this->hasMany(Student::class);
}
In the end, you'll have all the info you need from the mentorship when you call objects like this:
Teacher::with('mentorias')->get();
// This will show the Teacher's mentorias and the students in each one
Student::with('mentoria')->get();
// This will show the Student's mentoria and its teacher
Mentoria::with(['teacher', 'students'])->get();
// This will show the teacher and the students for each mentoria

pivot table relationship Laravel

I have the following relation:
RAB hasMany products.
products belongsToMany RAB
but this products also hasMany currency that belongsToOne RAB itself.
it looks like this.
in RAB model:
public function products()
{
return $this->belongsToMany('App\ModelKeuangan\ProductsModel', 'rab_products', 'rab_id', 'products_id');
}
in products model:
public function rab()
{
return $this->belongsToMany('App\ModelUnitKerja\Perencanaan\RabModel', 'rab_products', 'products_id', 'rab_id');
}
rab_products is my intermediate/join table.
it works just fine when im syncing products data for RAB. but i cant get how to make eloquent model for syncing Currency data to rab and products.
do i need to make model for currency too? if yes, how do i define it?
my plan is make a pivot like this, but can i make relation inside pivot table?
class RabProducts extends Pivot {
//relation function to currency
}
and change my products model something like:
public function rab(){
return $this->belongsToMany('App\ModelUnitKerja\Perencanaan\RabModel')->using('App\RabProducts');
}
Create the currency model and add foreign_key product_id:
php artisan make:model Currency
Build the one-to-many between Product and Currency
and you need to add product_id in your currencies table.
In your Currency Model:
public function product() {
return $this->belongsTo('App\Product', 'product_id');
}
In your Product Model:
public function currencies()
{
return $this->hasMany(\App\Currency::class, 'product_id', 'id');
}
So that you can call it like this:
RAB::with(['products' => function($query) {
$query->with('currencies');
}])->get();
Unfortunately, the foreign_key between products and rabs is in pivot table, you cannot create the hasmanythrough with currency directly.
You can call the currency by products. Or you can create a pivot model, and then use hasmanythrough.

Three-way pivot table or complex relationship in Laravel

I have two tables, shipments and customers. In the real world, a customer can be related to a shipment in three ways: as the biller, the destination and/or the origin.
So my question here is, do I have a pivot table with three columns, one for the shipment_id, one for the customer_id, and one for the relationship_type id? Or do I have separate tables? I'm not sure how best to approach this as it's the first of it's kind that I've run up against.
I faced this couple weeks ago and I came up with a solution.
Assuming that one customer can have different relations to different
shipments.
First of all you need a new model for customer roles obviously that model it will be Relation model.
First approach: You could solve this by using more than one pivot table which works but its not a good database design. I solved it first like this but realized its not optimal choice when it comes to db.
Second approach: You could solve this by defining pivot table as a model, but I havent tried that way even though I know it works and its a solution.
Better approach: use one pivot table for three models. In that case you have to define pivot table when you define a relationship example :
Customer model:
public function relations()
{
return $this->belongsToMany(Relation::class, 'customer_relation_shippment');
}
Relation model:
public function customers()
{
return $this->belongsToMany(Relation::class, 'customer_relation_shippment');
}
and the other model as well.
now lets say you want to add a relation to a customer.
Lets grab first customer and first shipment and say we want to add a relation as a biller:
$customer = Customer::first();
$shipment = Shipment::first();
$relation = Relation::where('name','biller')->get();
$customer->relations()->attach($shipment->id, ['relationship_type'=>$relation->id]);
By using only one pivot table of course its a bit more complex to perform operations towards those models like CRUD, but when it comes to database design/optimazation of course it is the right choice! Note that I came to this conclusion after dealing with a similar real world issue and it turned way more faster db interaction then using more than one pivot.
Here is how I would design your project.
I don't think you even need a pivot table or many-to-many relationship.
Note: For clarity and avoiding confusion with the User, I will use Account to refer to what you call a Customer. At the end you used customer account in your comments.
You have a shipment that relates to three different entities. However, those entities are represented by the same data model in your database: the Account model.
A basic one-to-many relationship will suffice.
An account can have many shipments. And the shipment belongs to one account.
Now, how to add the "Type" of the relationship? We don't need a pivot table, we just add another one-to-many relationship.
An Account as biller may have many shipments, and the shipment belongs to one biller.
An Account as origin may have many shipments, and the shipment belongs to one origin.
An Account as destination may have many shipments, and the shipment belongs to one origin.
To explain, here is an example code:
We have three models: User, Account, and Shipment
Let's start with the schema:
Schema::create('accounts', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('shipments', function (Blueprint $table) {
$table->increments('id');
$table->string('from');
$table->string('to');
$table->unsignedInteger('biller_id');
$table->unsignedInteger('origin_id');
$table->unsignedInteger('destination_id');
$table->foreign('biller_id')
->references('id')->on('accounts');
$table->foreign('origin_id')
->references('id')->on('accounts');
$table->foreign('destination_id')
->references('id')->on('accounts');
$table->timestamps();
});
We have three columns referencing the id on the accounts table.
For the models and the relationships:
Account Model:
class Account extends Model
{
public function billerShipments()
{
return $this->hasMany(Shipment::class, 'biller_id');
}
public function originShipments()
{
return $this->hasMany(Shipment::class, 'origin_id');
}
public function destinationShipments()
{
return $this->hasMany(Shipment::class, 'destination_id');
}
public function users()
{
return $this->belongsToMany(User::class);
}
}
Shipment Model:
class Shipment extends Model
{
public function billerAccount()
{
return $this->belongsTo(Account::class, 'biller_id');
}
public function originAccount()
{
return $this->belongsTo(Account::class, 'origin_id');
}
public function destinationAccount()
{
return $this->belongsTo(Account::class, 'destination_id');
}
}
Example to create a shipment
$billerAccount = \App\Account::create(['name' => 'account b']);
$originAccount = \App\Account::create(['name' => 'account a']);
$destinationAccount = \App\Account::create(['name' => 'account c']);
$newShipment = \App\Shipment::create([
'from' => 'city 1',
'to' => 'city 2',
'biller_id' => $billerAccount->id,
'origin_id' => $originAccount->id,
'destination_id' => $destinationAccount->id,
]);
echo $billerAccount->billerShipments()->count(); // 1
echo $originAccount->originShipments()->count(); // 1
echo $destinationAccount->destinationShipments()->count(); // 1
echo $newShipment->billerAccount->name === $billerAccount->name; // 1
echo $newShipment->originAccount->name === $originAccount->name; // 1
echo $newShipment->destinationAccount->name === $destinationAccount->name; // 1
For the account-user relationships, it can be many-to-many or one-to-many depending on your requirements.

laravel eloquent relationships issue

i'm a litle bit confused and i need some help since im new in laravel !
i have 3 table !! questions , category and theme
a question have a category and a theme
a theme have many categories
a category belong to one theme
what im asked to do is when i will add a question i only choose a category from the list and it will be added with her correspondant theme in the question table !! i hope i explained good my question :)
the category migration
Schema::create('category', function(Blueprint $table)
{
$table->increments('categoryId');
$table->string('categoryName');
$table->integer('themeId')->unsigned();
$table->foreign('themeId')->references('themeId')->on('theme');
});
the theme migration
Schema::create('theme', function(Blueprint $table)
{
$table->increments('themeId');
$table->string('themeName');
});
the questio migration i didn't make relation since i didn't find a way to do it
Schema::create('question', function(Blueprint $table)
{
$table->increments('questionId');
$table->string('question', 200);
$table->string('rightAnswer', 50);
$table->string('explanation', 500);
$table->string('wrongAnswer1', 50);
$table->string('wrongAnswer2', 50);
$table->string('wrongAnswer3', 50);
$table->string('theme');
$table->string('category');
$table->integer('difficulty');
$table->timestamps();
});
Theme Model
class Theme extends Eloquent
{
protected $primaryKey = 'themeId';
protected $guarded = array();
public function category()
{
return $this->hasMany('Category');
}
}
Category Model
class Category extends Eloquent
{
protected $primaryKey = 'categoryId';
protected $guarded = array();
public function theme()
{
return $this->belongsTo('Theme');
}
}
Questions Model
class Question extends Eloquent
{
protected $primaryKey = 'questionId';
protected $guarded = array();
public function category()
{
return $this->belongsTo('Category');
}
}
You do not make the Eloquent relations in the migrations. Migrations are only for creating the database table structure. Rather, the relations in Eloquent are defined in the models you make:
// Theme Model
public function categories()
{
return $this->hasMany('Category');
}
// Category Model
public function theme()
{
return $this->belongsTo('Theme');
}
public function questions()
{
return $this->hasMany('Question');
}
// Question Model
public function category()
{
return $this->belongsTo('Category');
}
These methods in the model define the relationship in Eloquent, and let you do things like this:
// Given an instance of a theme
foreach($theme->categories as $category)
// ...
// Given an instance of a question
echo $question->category->theme->themeName;
That being said, the methods above won't precisely work given your table structure. Eloquent relies also on convention, and the convention is that foreign keys should be named in a specific manner, namely theme_id and category_id (vs themeId as you have in your categories table). You can override this per the documentation using this format:
return $this->belongsTo('Theme', 'themeId');
Though you're probably better off sticking to convention. This convention also states that the primary key of each table should be named id.
As for your question table, you can create a relationship to the category the same as you did for the relationship between the themes and categories: add a column to your questions table that references the category id:
// Your migration:
$table->integer('category_id');
// Add a foreign key as well if you wish, though it is not
// required for the relationship in Eloquent
Then in your Question model place the category method I outlined above. Simple as that.

Categories