how to make the relation between many to many and one to many? - laravel

Suppose we have 3 entities named A, B, and C. And Relations between these Entities are something like this:
A -- one to many --> B
B -- many to many --> C
Example tables:
A
| id |
| -- |
| 1 |
| 2 |
B
| id | a_id |
| -- | -- |
| 1 | 1 |
| 2 | 2 |
B_C
| b_id | c_id |
| -- | -- |
| 1 | 1 |
| 2 | 2 |
C
| id |
| -- |
| 1 |
| 2 |
If we wanna make the relationship between A and C in laravel, what should we do? is it possible at all?

For these situations, you can use Has Many Through relationships and for more information, you can follow the laravel documentation.
to get the relation between your A and C model you can use has many though like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class A extends Model
{
public function c()
{
return $this->hasManyThrough(
C::class,
B::class,
'a_id', // Foreign key on the a table...
'b_id', // Foreign key on the b table...
'id', // Local key on the a table...
'id' // Local key on the b table...);
}
}
The first argument passed to the hasManyThrough method is the name of the final model we wish to access, while the second argument is the name of the intermediate model.

Related

Laravel many to many 2 level down pivot table

I am trying to build a Survey Module in Laravel where user can create survey, assign questions and options to the questions in survey.
survey can have multiple questions
survey question can have multiple options
To meet the above requirement I created the following models & tables
Model: Question
Table: questions
| id | question |
| -------- | -------------------------|
| 1 | How is the performance? |
| 2 | How did you know about us|
Model: Option
Table: options
| id | option |
| --- | --------- |
| 1 | Good |
| 2 | Bad |
| 3 | Google |
| 2 | SMS |
Now the relationship between questions and survey will be stored in pivot table
Model: SurveyQuestion
Table: survey_questions
| id | survey_id| question_id |
| ---| -------- |-------------|
| 1 | 1 |1 |
| 1 | 1 |2 |
Upto this point I know how to store data into pivot table using attach/sync.
Now the problem is As per requirement each survey question might have multiple options so I created another pivot table survey_question_option in which I am using survey_questions primary key as foreign key.
Model: SurveyQuestionOption
Table: survey_question_options
| id | survey_question_id| option_id |
| -- | ----------------- |-----------|
| 1 | 1 |1 |
| 1 | 1 |2 |
Now my question is that is it correct to use pivot table primary key as foreign key into another table?
If yes then how can I store data into survey_question_options table using laravel relationships?
If no then what is the better solution?
In this case you can create a Custom Intermediate Table Model (Custom Pivot)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class SurveyQuestion extends Pivot
{
public function options()
{
//You need to create a pivot pivot table survey_question_option
//with (survey_question_id, option_id)
return $this->belongsToMany(Option::class, 'survey_question_option');
}
}
Your models will need to recognize this new Pivot Model with the method using(). Here is an example with Survey Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Survey extends Model
{
/**
* The users that belong to the role.
*/
public function questions()
{
return $this
->belongsToMany(Question::class)
->using(SurveyQuestion::class);
}
}
And them you will be able to access via eloquent like this
$survey->questions->pivot->options
For more information you can check the documentation here:
https://laravel.com/docs/8.x/eloquent-relationships#defining-custom-intermediate-table-models

How to write Eloquent relation for one table to another table two columns?

I am trying to join two columns with a table.
Here is my table structure for tasks table
| id | assigned_by | assigned_to |
| :--- | :---: | ---: |
| 1 | 1 | 2 |
| 2 | 1 | 3 |
Here is the table structure of users table.
| id | name | email |
| :--- | :---: | ---: |
| 1 |varun | me#gmail.com |
| 2 |mark | mark#gmail.com|
I tried below code in Task Class it didn't work
public function user()
{
return $this->belongsTo(User::class);
}
Eloquent will automatically determine the proper foreign key column on the User model. By convention, Eloquent will take the "snake case" name of the owning model and suffix it with _id. So, for your example, Eloquent will assume the foreign key on the User model is user_id.
However, your foreign_key is assigned_by and assigned_to, so you need to specify the foreign_key:
public function assignedUser()
{
return $this->belongsTo(User::class, 'assigned_to');
}
public function assignedByUser()
{
return $this->belongsTo(User::class, 'assigned_by');
}

Laravel: Proper Querying Many to Many Relationships

I have 2 tables and a third pivot table as follows:
Table 1: Files
id
title
Table 2: Pools
id
title
is_visible (1,0)
Table 3: file_pools
file_id
pool_id
All Models and Relationships are in place.
A File may belong to none, 1 or more pools
A Pool may have none,1 or more files
Now, i would like to create a scope visible in the Model File such that:
the query includes any File that belongs to none or 1,or more active Pools
should not include any File that has at least 1 invisible (is_visible=0) Pool, even if this File belongs to another visible Pool
Below is what I have tried but this includes files that are both in visible and invisible Pools
public function scopeVisible($query)
{
return $query->doesntHave('pools')->orWhereHas('pools', function ($query) {
$query->where('is_visible',1);
});
}
any help highly welcome
EDIT: Add Sample Data
table: files
| id | title |
|----------|---------------|
| 1 | File A |
| 2 | File B |
| 3 | File C |
| 4 | File D |
table: pools
| id | title | is_visible|
|----------|---------------|---------- |
| 1 | Pool 1 | 1 |
| 2 | Pool 2 | 0 |
table: file_pools
| file_id | pool_id |
|----------|---------------|
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 3 | 2 |
The Expected Result : (scope:visible)
| id | title |
|----------|---------------|
| 1 | File A |
| 4 | File D |
Hope this clarifies
If I understand your situation correctly:
public function scopeVisible($query)
{
return $query->whereDoesntHave('pools', function ($query) {
$query->where('is_visible', 0);
});
}

Laravel. How to get relationships where foreign key is an array

I am trying to retrieve database rows with their relationships. However, the local key is an array. Let me explain using an example.
Lets say I have a table of countries and a table of pages. Each country can have many pages. Each page can belong to multiple countries. I do not have the flexibility to change this schema.
pages
+-------------+-----------+
| id | name | countries |
+-------------+-----------+
| 1 | Page 1 | 1 |
+-------------+-----------+
| 2 | Page 2 | 1,2,3 |
+-------------+-----------+
| 3 | Page 3 | 4,5,6 |
+-------------+-----------+
countries
+----+----------------+
| id | name |
+----+----------------+
| 1 | United States |
+----+----------------+
| 2 | United Kingdom |
+----+----------------+
| 3 | Germany |
+----+----------------+
| 4 | France |
+----+----------------+
| 5 | Hong Kong |
+----+----------------+
| 6 | Thailand |
+----+----------------+
| 7 | Belgium |
+----+----------------+
| 8 | Singapore |
+----+----------------+
My model and controller look something like:
country
public function pages()
{
return $this->hasMany(Page::class, 'id', 'countries');
}
MemberController.php
$countries = Country::with('pages')->get();
This is returning all countries, but only Page 1 contains any relationships.
Is there a way to retrieve relationships using a whereIn approach so all three countries will return appropriate pages?
Thanks in advance
Since Page can belong to many Countries, you need to create a pivot table called country_page and remove the countries column.
Then define two belongsToMany() relationships in both models:
public function pages()
{
return $this->belongsToMany(Page::class);
}
If you're not following Laravel naming conventions listed in my repo and you gave the pivot name a custom name, define it too:
public function pages()
{
return $this->belongsToMany(Page::class, 'custom_pivot_table');
}
Something like this ?
$datas = Pages::where( ##your conditions### )->get()->inArray();
$countries = Countries::pluck('name','id'); // return array of id=>name
foreach($datas as $key=>$data) {
$c = [];
foreach(explode(',',$data['countries']) as $country_id) {
$c[]=$countries[$country_id];
//or
$c[]= ['id'=>$country_id,'name'=>$countries[$country_id]];
}
$datas[$key]['countries']= $c;
}

Laravel Eloquent How to Combine Many to Many and One to Many relationships with 3 table

I have a database like this.
+----------------+
| Tables_in_test |
+----------------+
| a_b |
| a_c |
| as |
| bs |
| cs |
+----------------+
bs and cs tables have many to many relationship with as table. So a_b table and a_c table are pivot tables.
This is as table
+----+------+
| id | name |
+----+------+
| 1 | A1 |
| 2 | A2 |
+----+------+
This is bs table
+----+------+
| id | name |
+----+------+
| 1 | B1 |
+----+------+
And this is cs table
+----+------+------+
| id | b_id | name |
+----+------+------+
| 1 | 1 | C1 |
| 2 | 1 | C2 |
+----+------+------+
this is a_b pivot table
+------+------+
| a_id | b_id |
+------+------+
| 1 | 1 |
+------+------+
And this is a_c pivot table.
+------+------+
| a_id | c_id |
+------+------+
| 1 | 1 |
| 2 | 2 |
+------+------+
And this is my A model for the as table.
class A extends Model
{
protected $table = "as";
public function b(){
return $this->belongsToMany("App\B");
}
public function c(){
return $this->belongsToMany("App\C");
}
}
And this is B model for bs table
class B extends Model
{
protected $table = "bs";
public function c(){
return $this->hasMany("App\C");
}
}
I just want to query C table values as related to A table.
I tried this query
A::where("id",1)->with("b.c")->get();
But this result also gives me C2 value which is related to A2 in "as" table. I want to get only C1 value which is related only A1 value in "as" table.
How can I do this ? Thank you for your help
In your example data A1 relates to B1 which in turn relates to C2. And you are in fact nesting the b.c eager load, so you are loading Bs and Cs related to those Bs.
Have you tried A::where("id",1)->with(["b", "c"])->get();
?

Resources