Is it possible to set condition in join section in viaTable?
Currently I got this:
return $this->hasMany(User::className(), ['id' => 'id_user'])
->from(User::tableName())
->viaTable(RoomActiveUser::tableName(), ['id_room' => 'id'],
function($query) {
return $query->andWhere(['id_role' =>
RoleHelper::getConsultantRole()->id]);
});
But it's not a good solution. Why?
When you do a left join the id_role condition will make it inner join actually. The id_role condition should be placed inside ON section of the join.
I was searching the web plus inspecting the code but I don't see how it could be solved.
I got the answer from Qiang Xue - $query->onCondition() should be used for what I need. Result code:
return $this->hasMany(User::className(), ['id' => 'id_user'])
->from(User::tableName())
->viaTable(RoomActiveUser::tableName(), ['id_room' => 'id'],
function($query) {
$query->onCondition(['id_role' =>
RoleHelper::getConsultantRole()->id]);
});
Have you tried doing it like
->viaTable(RoomActiveUser::tableName(), ['id_room' => 'id', 'id_role' => RoleHelper::getConsultantRole()->id])
This should create a JOIN X on 'id_room' = 'id' AND 'id_role' = '$ConsultantRole_id'
Related
I use MadelineProtoDocs to create a userbot that do tasks I want. What I want to do is that I want to Join more than one group at once!
I have used
$MadelineProto->messages->importChatInvite(['hash' => 'HASH_CODE']);
It works when I put a hashcode for one group, but when I put more than one like this:
$MadelineProto->messages->importChatInvite(['hash' => ['HASH_CODE', 'HASH_CODE2']);
it doesn’t work
I also tried:
$MadelineProto->channels->joinChannel(['channel' => [InputChannel, InputChannel], ]);
it also doesn’t work!
I found the solution.
It’s easy to do that
So when you join a channel or group you do this:
$join = yield $MadelineProto->channels->joinChannel(['channel' => '#channelusername', ]);
By this you will join only one channel/group,
so how to add another channel/group in the same code?
It’s easy:
$join =
yield $MadelineProto->channels->joinChannel(['channel' => '#channelusername', ]);
yield $MadelineProto->channels->joinChannel(['channel' => '#channelusername1', ]);
yield $MadelineProto->channels->joinChannel(['channel' => '#channelusername2', ]);
and you will join all the channels you added!
I'm rebuilding a vanilla PHP/MySQL application using CakePHP 3.5.13.
One of the queries in the original application needs to build a JOIN condition on the same table and does so by using separate table aliases. The query is as follows - and gives the correct results that we expect:
SELECT DISTINCT(s.id) FROM substances s
JOIN display_substances AS dsUses
ON (s.id = dsUses.substance_id AND dsUses.display_id = 128 AND (dsUses.value LIKE '%dye%') )
JOIN display_substances AS displays
ON (s.id = displays.substance_id AND displays.display_id NOT IN (1,2,3,4,6,128) AND (displays.value LIKE '%bpr%'))
The reason this is needed is because the query is doing a search for 2 separate items of user input - within the same table (display_substances) but against different display_substances .display_id fields. In terms of the query above it means:
Search for "%dye%" where display_id = 128
Search for "%bpr%" where display_id is not 1,2,3,4,6 or 128
In Cake I have written it like this in a Controller that handles the search functionality:
$query = $Substances->find()->select(['id' => 'Substances.id'])->distinct();
// Search for "dye"
$query = $query->matching('DisplaySubstances', function ($q) use ($uses_summary) {
return $q->where([
'DisplaySubstances.value LIKE' => '%dye%', // "dye" is dynamic and comes from $uses_summary
'DisplaySubstances.display_id' => 128
]);
});
// Search for "bpr"
$query = $query->matching('DisplaySubstances', function ($q) use ($regulatory_information) {
return $q->where([
'DisplaySubstances.value LIKE' => '%bpr%', // "bpr" is dynamic and comes from $regulatory_information
'DisplaySubstances.display_id NOT IN' => [1,2,3,4,6,128]
]);
});
This produces the wrong SQL, because when I debug $query->sql(); it gives a different JOIN condition:
INNER JOIN display_substances DisplaySubstances ON
(
DisplaySubstances.value like "%dye%" AND DisplaySubstances.display_id = 128
AND DisplaySubstances.value like "%bpr%"
AND DisplaySubstances.display_id not in (1,2,3,4,6,128)
AND Substances.id = (DisplaySubstances.substance_id)
)
I'm not sure how to rewrite this query such that it will treat each of the two search inputs as a JOIN condition, as per the original query.
The main thing I've noticed is that the original query has separate aliases for the same table (AS dsUses and AS displays). I'm not sure if this is relevant?
I'm trying to use the ORM as opposed to writing the SQL manually so would prefer to know how to use it to do this.
It's not yet possible to specify custom aliases for matching() calls, currently multiple matching() calls on the same association will merge the conditions as can be seen in your generated SQL snippet.
For now you'd have to either create additional associations (ie additionally to your DisplaySubstances association in your SubstancesTable class) with different aliases:
$this->hasMany('DsUses', [
'className' => 'DisplaySubstances'
]);
$this->hasMany('Displays', [
'className' => 'DisplaySubstances'
]);
on which you can then match:
$query->matching('DsUses', function ($q) use ($regulatory_information) {
return $q->where([
'DsUses.value LIKE' => '%dye%',
'DsUses.display_id' => 128
]);
});
$query->matching('Displays', function ($q) use ($regulatory_information) {
return $q->where([
'Displays.value LIKE' => '%bpr%',
'Displays.display_id NOT IN' => [1, 2, 3, 4, 6, 128]
]);
});
or build the joins manually, for example using innerJoin():
$query->innerJoin(
[
'DsUses' => 'display_substances'
],
[
'Substances.id' => new \Cake\Database\Expression\IdentifierExpression('DsUses.substance_id'),
'DsUses.display_id' => 128,
'DsUses.value LIKE' => '%dye%'
],
[
'DsUses.display_id' => 'integer',
'DsUses.value' => 'string'
]
);
$query->innerJoin(
[
'Displays' => 'display_substances'
],
[
'Substances.id' => new \Cake\Database\Expression\IdentifierExpression('Displays.substance_id'),
'Displays.display_id NOT IN' => [1, 2, 3, 4, 6, 128],
'Displays.value LIKE' => '%bpr%'
],
[
'Displays.display_id' => 'integer',
'Displays.value' => 'string'
]
);
See also
cakephp/cakephp#9499 : Improve association data fetching
Cookbook > Database Access & ORM > Query Builder > Adding Joins
This is the query I have:
select * from order_shipping_date osd inner join
(SELECT MAX(osd.id) as id FROM order_shipping_date osd
group by osd.order_id) osdi ON osd.id = osdi.id
I'm fine with keeping it so, but would like to make it possible to define it as relations
This is more comfy to use such code later
Is this doable at all? I can't find any examples.
Additional options can be specified in relationship declaration.
public function relations()
{
return array(
'orderShippingDate' => array(
// define you relation
'join' => '(/* subquery here*/) osdi ON osdi.id=orderShippingDate.id',
'joinType' => 'INNER JOIN'
),
);
}
I have to Tables: Users hasmany Memberhip. I would like to build a query to get ALL Users but EACH User should only contain the FIRST Membership (after ordering).
$users = $this->Users->find()->contain([
'Membership' => function ($q) {
return $q->order([
'year' => 'ASC'
])->limit(1);
},
'Countries',
'Board',
]);
Seems good so far. The problem is, that this query only gets a single Membership alltogether. So in the end of all Users that are beeing fetched, only one User has one Membership.
How do I get CakePHP to fetch ONE Membership for EACH User?
Thanks!
Sort key for hasOne option doesn't exist (see : CakePhp3 issue 5007)
Anyway I had the same problem and thanks to ndm : here his solution
I ended up using this:
$this->hasOne('FirstPhoto', [
'className' => 'Photos',
'foreignKey' => false,
'conditions' => function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) {
$subquery = $query
->connection()
->newQuery()
->select(['Photos.id'])
->from(['Photos' => 'photos'])
->where(['Photos.report_id = Reports.id'])
->order(['Photos.id' => 'DESC'])
->limit(1);
return $exp->add(['FirstPhoto.id' => $subquery]);
}
]);
Reference: How to limit contained associations per record/group?
You can do this is by creating another association (Make sure you don't include a limit you don't need one as the hasOne association creates the limit):
$this->hasOne('FirstMembership', [
'className' => 'Memberships',
'foreignKey' => 'membership_id',
'strategy' => 'select',
'sort' => ['FirstMembership.created' => 'DESC'],
'conditions' => function ($e, $query) {
return [];
}]);
This works for limiting to 1 but if you want to limit to 10 there still seems to be no good solution.
This is my view
'promoter_id' => [
'type' => Form::INPUT_DROPDOWN_LIST,
'options' => ['prompt' => '--'.Yii::t ( 'app', 'Ventor Type' ).'--',],
'items' => ArrayHelper::map (
User::find ()->orderBy ( 'username')->where(['user_type_id' => [13]])->asArray ()->all (),
'id',
'username'
)
]
What i did?
show the users filtered by user_type_id = 13.
user_type_id=13 means the users are promoters.
What i want?
I want to show the users like below sql query using ArrayHelper::map.
SELECT u.username
FROM tbl_user u,tbl_user_type t
where u.user_type_id = t.id and t.type='promoter';
Since you are using mysql you can simply use an innerJoin:
User::find()
->select(['user.id', 'user.username'])
->innerJoin('user_type', 'user_type.id = user.id')
->where(['user_type.type' => 'promoter'])
->orderBy('username')
->asArray()
->all(),
If you have a relation between user and user_type you can use joinWith instead, which handles the join type and the join condition for you:
User::find()
->select(['user.id', 'user.username'])
->joinWith('userType', true)
->where(['userType.type' => 'promoter'])
->orderBy('username')
->asArray ()
->all (),