Yii2 join relations with 4 tables - php

I have 4 tables, say table A, B, C, D.
Somewhat like:
Table
A - a_id, a_name.
B - b_id, a_id, b_name.
C - c_id, b_id, d_id.
D - d_id, d_name
How can I get the table D object for a corresponding primary key value of A ie. if value of id in A is 20, I need all data from d table corresponding to that in yii2.
A -> B is one to many.
Someone please help!

First of all, it's better to give real world example for better understanding. And I think naming primary key of the same table as id (not a_id for a table) is better practice.
This can be done using relation chain. For example if we have 3 models: Test, Question and Answer (test has many questions and question has many answers), having an answer model we can access test model like so:
$answer = Answer:findOne(20); // id must exist
$test = $answer->question->test;
But it doesn't work vice versa with has many, because we need to know specific model to continue chain, so we can't write something like:
$answer = $test->question->answer
Only $test->questions and $question->answers are acceptable.
This is exactly your case. Read Working with Relational Data article in official docs for better understanding.

I got a 4 or even more relation situation same as you, table as these:
Link - id, SalesItemId.
SalesItem - id, SoId, ItemName
SalesOrder - id, SalesId, OrderNo
User - id, ManagerId, Name
User - id, Name
** the User table I have related 2 times as I need to get the manager name of the user
In Link model, I got these methods:
public function getLinkSalesitem() {
return $this->hasOne(Salesitem::className(), ['id' => 'SalesItemId']);
}
public function getLinkSalesorder() {
return $this->hasOne(Salesorder::className(), ['id' => 'SoId'])
->via('linkSalesitem');
// ->viaTable('salesitem', ['id' => 'SalesItemId']); //either this line or above are the same
}
public function getLinkSales() {
return $this->hasOne(User::className(), ['id' => 'SalesId'])
->via('linkSalesorder');
}
public function getLinkManager() {
return $this->hasOne(User::className(), ['id' => 'ManagerId'])
->via('linkSales');
}
In the view file, I just retrieve the the Manager name by
$model->linkManager->Name;

if you want to use relations then first you have to make sure that table A is Connect to table D through some table in you case table A is connected to table B with a_id,table B is connected with table C with b_id and table C and table D are connected with through d_id in table c.
Now if you want data from table D then you have to make relations
First make relations
public function getB(){
return $this->hasOne(B::className(),['a_id' => 'a_id'])
}
public function getC(){
return $this->hasOne(C::className(),['b_id' => 'b_id'])
}
Now you want data from A and D which is connected through relation C so you have to make relation like
public function getD(){
return $this->hasOne(D::className(),['d_id' => 'a_id'])->via('c')
}
First find A
$model = A::findOne(20);
echo isset($model->c)?$model->c->d_name:'';

Related

Get table records except the ones that has foreign key values in another table

I have two tables:
goods: id | name
imported_goods: id | good_id | amount
imported_goods has foreign key values from table goods, And what i'm trying to do is:
Display all goods records Except the records that has foreign
key values in imported_goods.
Example (CSV):
goods
1,orange
2,apple
3,bannana
4,mango
imported_goods
1,1,20 kg
2,2,40 kg
3,3,60 kg
Expected Result:
4,mango
And all other records discarded
Good Model
class Good extends Model
{
/**
* Get the Imported Goods for this Good.
*/
public function imported_goods()
{
return $this->hasMany('App\Models\Imported_good','good_id');
}
}
Imported_Good Model
class Imported_good extends Model
{
/**
* Get the Good info for this Imported Good.
*/
public function good()
{
return $this->belongsTo('App\Models\Good','good_id');
}
}
If you followed good practice and recommendation of naming convention between table names, model names, keys, fields etc (like here for example), you can do it just with:
// Parent.php model
public function children()
{
return $this->hasMany(Child::class);
}
// than somewhere in controller
$parents = Parent::doesntHave('children')->get();
This is generic example. If you share actual names and model classes code as much as table structure instead of some x/y placeholders, I'll be able to tell more for your particular case.
You can utilize a subquery:
SELECT * FROM X WHERE id NOT IN (SELECT x_id FROM Y);
Or you can perform a join:
SELECT X.* FROM X LEFT JOIN Y ON X.id = Y.x_id WHERE y.x_id IS NULL;
If you don't have to worry about performance, I'd suggest the first option (it's more readable and easier to understand). If you need to, consider that joins are generally faster than subqueries.

Yii2 GridView shows "(not set)" if dataProvider contains GROUP BY and INNER JOIN

I have show (only show, not compleate CRUD) the result of a query which is built as following:
SELECT SUM(a) AS ab, b, COUNT(*) as C
FROM x
INNER JOIN y
ON y.a = x.a
WHERE b=123
GROUP BY b
so I built this query with ActiveRecord in SearchModels search() method.
In the model of table a I added a hasOne()-relation.
To display the data of this query, I'm using GridView. In it's columns array I use y.b and so on...
My problem: The columns from table x are displayed correct, but for every "joined column" from table y it displays (not set).If I print the by ActiveRecord builded query, and execute it in my sql client, it displays all data. I guess this is depending on the Models primaryKey() function, but I can't change it to get the table work properly. Does somebody know a solution for my problem or why dataProvider/GridView takes care of the selected model's (in this case model of table x) primaryKey() method (or how to make dataProvider/GridView ignore the primaryKey()?
In model you should create relation method with relation model.
For example:
class Patient extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::class, ['patient_id' => 'id']);
}
}
class Order extends ActiveRecord
{
public function getPatient()
{
return $this->hasOne(Patient::class, ['id' => 'patient_id']);
}
}
For access data:
// SELECT * FROM `patient` WHERE `id` = 1
$patient = Patient::findOne(1);
$orders = $patient->orders;

Creating self referencing relationships

I want to be able to make relationships between a captain and his referrals. They both belong to the same table. I have this in my model
public function captain() {
$this->belongsTo('User', 'referral_id') ;
}
public function captain() {
$this->hasMany('User', 'referral_id') ;
}
My users table has the following columns
id name referral_id referred_by
1 xyz 1223 null
2 Abc 4525 1223
How do I create the relationship better? And I want to know if I can and how I can use this to get the referral of a referral of the captain
I'd create a second table for your referrals - then you create a relationship between your captain ID in table 1 over in table 2 where all the referers can be stored. If you setup the relationship, you then simply call something like
$captains = App\Captain::all();
foreach ($captains as $captain) {
echo $captain->referrals->name;
}
ref
using simple eager loading... or ->with using other methods (or join etc)

Laravel Eloquent HasManyThrough returning empty array

I am having trouble getting a query from HasManyThrough relation in Eloquent.
These are my tables
PAGES
id
columns
slideshow_fk
SLIDESHOWS
id
columns
SLIDES
id
columns
slideshow_id
My Page model:
class Page extends Model
{
public function slideshow_id(){
return $this->belongsTo(Slideshow::class);
}
public function slides(){
return $this->hasManyThrough('App\Slide','App\Slideshow','id','slideshow_id','slideshow_fk');
}
}
Controller
$page=Page::where("slug","=",$slug)->with('slides')->first();
Query log: I am not Page ID:3 with slideshow_fk:1, [? = 1]
select `slides`.*, `slideshows`.`id` from `slides` inner join `slideshows` on `slideshows`.`id` = `slides`.`slideshow_id` where `slideshows`.`id` in (?)
page->slides array:
[]
PhpMyAdmin SQL copy/paste:
http://prntscr.com/e0hy4e
Which are the correct 3 slides I need for my page.
Why do I get an empty array?
You cannot you hasManyThrough() here as your implementation doesn't support the need for one.
Say you have three models A, B and C and A want to connect to C via B.
Then if you want to use hasManyThrough()
B needs to have A's primary key as a foreign key
C needs to have B's primary key as a foreign key
That is A <- B <- C
If I put this to your example, what you have is
A have B's primary key as a foreign key
C have B's primary key as a foreign key
Which is A -> B <- C.
What you can do here is define models like this.
class Pages extends Model
{
public function slideshow()
{
return $this->belongsTo(Slideshow::class);
}
}
class Slideshow extends Model
{
public function slides()
{
return $this->hasMany(Slides::class);
}
}
class Slides extends Model
{
}
And query like
$pags = Pages::with('slideshow.slides')->where('slug', $slug)->first();
I suggest using proper naming conventions.

How to update a row in DB without overwriting the existing data with laravel 5.2

I have a table which stores the user's interested topics along with the user id and topic id. The table will look like
Topic name topic id User-id
Sports 1 1
Education 2 3
Family 3 1
What i want to do is when a new user , say user-id '3' adds his interest as topic sports , then the first row should look like this
Topic name topic id User-id
Sports 1 1,3
When i try to update it overwrites the whole column like
Topic name topic id User-id
Sports 1 3
What is the right approach to do this ?
The right approach is to use Many-To-Many relationship:
Tables:
users (id, ....);
topics (id, ....);
topic_user (topic_id, user_id);
PHP:
class User extends Model {
public function topics() {
return $this->belongsToMany('App\Topic');
}
}
and :
class Topic extends Model {
public function users() {
return $this->belongsToMany('App\User');
}
}
For more details please refer to https://laravel.com/docs/5.2/eloquent-relationships.
I hope this will help.

Categories