For tables with two (or more) foreign keys referencing the same related table you need a way to define an alias. The joinWith()
function doesn't have this ability.
So there is no way to handle ambiugate column names for the related table.
Using an alias would also eleminate the need of using the table name.
Suggestion:
$orders = Order::find()->joinWith('customer as cu')->orderBy('cu.id, cu.id')->all();
instead of
$orders = Order::find()->joinWith('customer')->orderBy('tbl_customer.id, tbl_order.id')->all();
Currently you can do it using ->from('customer cu')
Yeap.
But this doesn't helps when you want to re-use defined relations.
Use this:
joinWith([
'customer' => function ($q) {
$q->from('customer cu');
},
])
We may consider adding the suggested shortcut.
@qiangxue It would be great!
We may consider adding the suggested shortcut.
this can confuse users, since relation name is not always table name, so example with $query
is just the case.
That's an advantage as you don't even need to know the table name (or might change it without effecting these joins). You only need the relation name what should almost be the same as the model class name.
Any news about this one ?
In Yii1 it was quite easy to use relation name as table alias.
it is set for the RC milestone, so we will handle it before RC release.
Ok, thanks
Milestone set for 2.0.1, can be in 2.0 if someone is making a pull request.
Added property $alias
to ActiveQuery
is look OK for me.
MyAr::find()
->alias('t')
->joinWith([
'customer' => function($q){
$q->alias('c');
}
]);
Or better syntax (if can) :D
joinWith(['c' => 'customer'])
private function getQueryTableName($query)
{
...
if (preg_match('/^(.*?)\s+({{\w+}}|\w+)$/', $tableName, $matches)) {
$alias = $matches[2];
} else {
$alias = $query->alias ? : $tableName;
}
return [$tableName, $alias];
}
Voting for both suggestions from @MDMunir :+1:
For now I'm using this _workaround_ for such situations:
class Order extends ActiveRecord
{
...
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id'])
->from(['cu' => Customer::tableName()]);
}
...
}
But this won't help in @twinni's situation.
related to #4972 and #5137
Not sure if it's in a suitable place but I've updated getRelation
in BaseActiveRecord
to allow for alias names.
e.g.
Model::find()->joinWith('relation AS alias')
or
Model::find()->joinWith('relation(alias)')
The latter example with brackets is to try and keep it clean if you have multiple relations.
e.g.
Model::find()->joinWith('relation1.relation2(alias)')
https://github.com/Alex-Code/yii2/blob/relation-alias/framework/db/BaseActiveRecord.php#L1097
this problem is about how to use the relation better, hope it will become true soon. Thanks for work.
Will look into this after 2.0.1 is released. Have no time to do it this week.
is this implemented by yii2 now?
no, as you see in this issue, the milestone is set for 2.0.3, I am going to work on that for this milestone and if all goes well it should be released with it.
currently I'm doing it like this, let me know when it would be released
public static function with($r, $t = null) { $relations = array(); if (is_array($r)) { foreach ($r as $v => $tn) { $i = strripos($v, '.'); $alias = $v; if ($i != false) $alias = substr($alias, $i + 1); $relations[$v] = function ($q) use ($tn, $alias) { $q->from($tn . ' ' . $alias); }; } } else { $alias = $v; if ($i != false) $alias = substr($alias, $i + 1); $relations[$r] = function ($q) { $q->from($t . ' ' . $alias); }; } return static::find()->from(static::tableName() . ' t')->joinWith($relations); } public static function find() { return parent::find()->from(static::tableName() . ' t'); }
馃槩
@callmez I am sorry but my time is limited. I'd love to work on this but it turns out there is more to be done in the background than it looks like on first sight.
it's ok, still thanks for you work.
@cebe SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'title' in where clause is ambiguous
can you help me:
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query ->joinWith('location');
$query ->joinWith('programCategory');
$query ->joinWith('duration');
$query ->joinWith('airport');
$query->andFilterWhere([
'program_id' => $this->program_id,
]);
$query->andFilterWhere(['like', 'title', $this->title])
->andFilterWhere(['like', 'location.city', $this->location_id])
->andFilterWhere(['like', 'program_category.program_category_name', $this->category_id])
->andFilterWhere(['like', 'duration.type', $this->duration_id])
->andFilterWhere(['like', 'airport.city', $this->airport_id]);
return $dataProvider;
}
}
@rahulsinh this is not related, please use the forum to ask questions.
As I understand in Yii 2.0.3, I must write next code:
$query = Billing::find()
->joinWith([
'state'
]);
$query->andFilterWhere([
Billing::tableName() . '.id' => $this->id,
BillingState::tableName() . '.id' => $this->stateId
]);
if both tables Billing
and BillingState
have column id
?
:+1: @MaXL-ru It is always the problem when you extend SearchModel.
I often use a table prefix and I need to use tableName function many times.
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = Employee::find()
->innerJoinWith('user', false)
->innerJoinWith('company', false);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
[...]
'company_id' => $this->company_id,
'user_id' => $this->user_id,
]);
// Here I must use tableName function
$query->andFilterWhere(['like', User::tableName() . .firstname', $this->user_firstname]);
return $dataProvider;
}
But when I use
joinWith([
'user' => function ($q) {
$q->from('{{%user}} user');
},
])
My order in GridView does not work (for firstname column).
You need to standardize using relation name in Query, because sometimes we can use name of relation and sometimes we need to use full table name. It is very confusing.
@mklemarczyk: you should configure sort
property of dataProvider.
see ActiveDataProvider::setSort()
Thank you @lynicidn, I miss it. But now I also need to reorganize all methods that use joinWith in my EmployeeQuery class.
I think that, instead of modifying joinWith method we should resolve relation names in other methods?
Using aliases can join may times the same table in our Query.
But can you handle this?
$query = Experiment::find()
->joinWith(['ownerEmployee.user' => function($q){$q->from(['ownerUser' => '{{%user}}']);}])
->joinWith(['executeEmployee.user' => function($q){$q->from(['executeUser' => '{{%user}}']);}]);
Tables definition:
user
table (from advanced template)
employee
table (id
, lab_id
, superior_id
, user_id
)
experiment
table (id
, owner_employee_id
, execute_employee_id
, done_date
)
And Yii give an exception:
Not unique table/alias: 'tex03_employee'
So to solve this problem I will need to create join manually. :disappointed:
+
Not in 2.0.4 :-1:
Hope this will be supported in 2.0.5
am I correct when assuming that the use of the same junction table is not supported yet?
Ie.:
$query = Article::find();
$query->joinWith('category.article');
In which:
Example SQL:
SELECT `article`.*
FROM `article`
LEFT JOIN `article_x_article_category` ON `article`.`id` = `article_x_article_category`.`owner_id`
LEFT JOIN `article_category` `category` ON `article_x_article_category`.`category_id` = `category`.`id`
LEFT JOIN `article_x_article_category` ON `category`.`id` = `article_x_article_category`.`owner_id`
LEFT JOIN `article` ON `article_x_article_category`.`category_id` = `article`.`id`
WHERE `article`.`id`=10948
Gives:
Currently, to bypass this issue, I am not using the joinWiths but instead I am using Query::innerJoin.
+
+1 :+1:
+
+
+
+1
+1
I would love this functionality!
+1
Me too, but, guys, instead of waiting - take a look of other frameworks implementations on this, because you can wait for a loooong time.
It is already in work: https://github.com/yiisoft/yii2/issues/10253
PR is merged.
Most helpful comment
Use this: