Cakephp 3 findAuth join table return array - mysql

I am attempting to implement a role based authentication system using a custom finder.
public function findAuth(\Cake\ORM\Query $query, array $options)
{
$query
->select(['id', 'username', 'passwordHash', 'locked', 'roles.role'])
->group('username')
->join([
'table' => 'user_roles',
'conditions' => ['user_roles.userid = Users.id']])
->join([
'table' => 'roles',
'conditions' => ['roles.id = user_roles.role']])
->toArray()
;
return $query;
}
The resulting mysql query i need is:
select users.id, username, passwordHash, locked, group_concat(roles.role) role from users INNER JOIN user_roles on user_roles.userid = users.id INNER JOIN roles on roles.id = user_roles.role group by users.id

What I finally ended up going with is this:
public function findAuth(\Cake\ORM\Query $query, array $options)
{
$query
->select(['id', 'username', 'passwordHash', 'locked'])
->join([
'table' => 'user_roles',
'conditions' => ['user_roles.user_id = Users.id']])
->contain(['UserRoles.Roles'])
->join([
'table' => 'roles',
'conditions' => ['roles.id = user_roles.role']])
->group(['Users.username']);
return $query;
}
This gives me a multi-dimensional array of:
user_roles[index[id, user_id, roles[id, role]]]
in my case I have 2 entries for the index (0 and 1) and I can check the role of the user with the in_array function within a foreach loop

Related

yii1 CGridview with custom SQL

i need to use yii1 CGridView with the custom SQL.
i have followed this article
inside model I've created
public function searchUserStats()
{
$count = Yii::app()->db->createCommand()
->select('count(DISTINCT user_id)')
->from('casino_sg_sessions')
->queryScalar();
$sql = '
SELECT
SUM(bet) bet_sum,
SUM(win) win_sum,
SUM(bet_count) bet_count,
user_id,
userName,
modified
FROM
(SELECT
(SELECT IFNULL(SUM(tr1.amount), 0) FROM casino_sg_transactions tr1 WHERE t.id = tr1.session_id AND tr1.action = \'bet\' ) AS bet,
(SELECT IFNULL(SUM(tr2.amount), 0) FROM casino_sg_transactions tr2 WHERE t.id = tr2.session_id AND tr2.action = \'win\' ) AS win,
(SELECT count(0) FROM casino_sg_transactions tr3 WHERE t.id = tr3.session_id) AS bet_count,
t.user_id,
u.userName,
t.modified
FROM `casino_sg_sessions` t
INNER JOIN user u ON u.id = t.user_id
ORDER BY modified DESC
) user_stats
GROUP BY user_id
ORDER BY modified DESC
';
$command = Yii::app()->db->createCommand($sql);
$dataProvider = new CSqlDataProvider($command, array(
'totalItemCount'=>$count,
'pagination'=>array(
'pageSize'=>15,
),
));
return $dataProvider;
}
in the controller:
$model = new CasinoSgSessions('searchUserStats');
$this->render('index', array(
'model' => $model,
));
inside the view i call it this way:
$this->widget('application.widgets.grid.CGridView', array(
'dataProvider' => $model->searchUserStats(),
'filter' => $model,
'columns' => array(
[
'header' => 'User ID',
'name' => 'user_id',
'value' => '$data->user_id',
'htmlOptions' => array('width'=>'150px'),
],
.....
.....
i receive paginated grid without data.
spent huge amount of time and now gave up, what i did wrong ?
sql query itself works good.
PS: i cant use mysql views and create model for the view.
PPS: query will be a bit more complex, so the only way i see is using CGridView through the sql query.
in the template instead of
$data->user_id
i had to use
$data['user_id']
absolutely not expected behavior. Seems to me they didn't hear about SOLID

Zend Framework 2 SQL Join Issue

I am trying to use two left outer joins with Zend Framework 2's SQL classes but for some reason it is not returning one result but the other one is working fine. I've ran the actual SQL in MySQL Workbench and it returns just like I want but it is not doing it with Zend Framework. Here is my code:
Pure SQL:
SELECT groups.group_name, members.username, groups.id FROM groups
LEFT OUTER JOIN group_admins ON groups.id = group_admins.group_id
LEFT OUTER JOIN members ON group_admins.user_id = members.id
WHERE group_admins.user_id = " . parent::getUserId()['id']
This returns the result I wish, (which can be seen here: http://imgur.com/8ydmn4f)
Now, here is the Zend Framework 2 code I have in place:
$select_admins = new Select();
$select_admins->from(array(
'g' => 'groups',
))
->join(array(
'ga' => 'group_admins'
), 'g.id = ga.group_id')
->join(array(
'm' => 'members'
), 'ga.user_id = m.id', array('username'))
->where(array('ga.user_id' => parent::getUserId()['id']));
$query_group_admin = parent::$sql->getAdapter()->query(parent::$sql->buildSqlString($select_admins), Adapter::QUERY_MODE_EXECUTE);
$group_admins = array();
foreach ($query_group_admin as $group_admin) {
$group_admins[] = $group_admin;
}
// get the group members
$select = new Select();
$select->from(array(
'g' => 'group_members'
))
->join(array(
'm' => 'members'
), 'g.member_id = m.id')
->join(array(
'grp' => 'groups'
), 'g.group_id = grp.id')
->where(array(
'g.group_id' => $group_id
));
$query = parent::$sql->getAdapter()->query(parent::$sql->buildSqlString($select), Adapter::QUERY_MODE_EXECUTE);
$member_username = array();
foreach ($query as $member) {
$member_username[] = $member['username'];
}
// get the rest of the group info
$fetch = $this->gateway->select(array(
'id' => $group_id
));
$row = $fetch->current();
if (!$row) {
return false;
}
return array(
'admins' => implode(", ", $group_admins),
'members' => implode(", ", $member_username),
'info' => $row
);
Controller:
public function grouphomeAction()
{
$id = $this->params()->fromRoute('id', 0);
if (0 === $id) {
return $this->redirect()->toRoute('members/groups', array('action' => 'index'));
}
if (!$this->getGroupsService()->getGroupInformation($id)) {
return $this->redirect()->toRoute('members/groups', array('action' => 'index'));
}
return new ViewModel(array('group_info' => $this->getGroupsService()->getGroupInformation($id)));
}
However, this only shows the group name, group creator and group members but leave the group admins field empty.
Here is the print_r result of the array returned:
Array ( [admins] => [members] => jimmysole, fooboy [info] => ArrayObject Object ( [storage:ArrayObject:private] => Array ( [id] => 2 [group_name] => Tim's Group [group_creator] => timlinden [group_created_date] => 2017-01-16 17:39:56 ) ) )
If it helps, here is a screenshot as well of the page - http://imgur.com/xUQMaUu
Any help would be appreciated!
Thanks.
Basically your joins are INNER JOINS...I know....you must hate Zend right now :p . By default they are INNER JOINS so i assume that is what is wrong. SO try to specify the type of join and you should be fine. You can find more examples here: examples
$select12->from('foo')->join('zac', 'm = n', array('bar', 'baz'), Select::JOIN_OUTER);

how to convert mysql subquery in yii CDbCriteria?

how to convert mysql subquery to yii CDbCriteria
table list:
1) user_group
2) user_group_data
3) user_group_data_revision
select * from (
select
ugd.group_id as group_id,
ug.user_id as user_id,
ugd.id as group_data_id,
ugd.group_name as group_name,
ugd.group_description as group_description,
ugd.email as group_admin_email,
ugd.image as group_image,
ugd.group_type as group_type,
ugd.request_type as group_request_type,
ugdr.revision_version as revision_version,
ugdr.admin_approve as revision_approve_status,
ugdr.publish as revision_publish,
ugdr.created_at as revision_created_at
from user_group_data_revision ugdr
left join user_group_data ugd on ugdr.group_data_id = ugd.id
left join user_group ug on ugdr.group_id = ug.id
order by `revision_version` desc
) rgd group by rgd.group_id
Here a small example(if I correctly understood structure/relations):
//Model UserGroupDataRevision
class UserGroupDataRevision extends CActiveRecord
{
public function relations()
{
return array(
'userGroup' => array(self::BELONGS_TO, 'UserGroup', 'group_id'),
'userGroupData' => array(self::BELONGS_TO, 'UserGroupData', 'group_data_id'),
);
}
//....
}
//model UserGroup
class UserGroup extends CActiveRecord {...}
//model UserGroupData
class UserGroupData extends CActiveRecord {...}
Criteria:
$criteria = new CDbCriteria();
$criteria->select = 'ugdr.revision_version'; // other fields
$criteria->alias = 'ugdr';
$criteria->with = array(
'userGroup' => array(
'alias' => 'ug',
'together' => true,
'select' => array('ug.user_id'), // other fields
),
'userGroupData' => array(
'alias'=> 'ugd',
'together' => true,
'select'=>array('ugd.group_id, ugd.group_name'), // other fields
),
);
$criteria->group = 'rgd.group_id';
$criteria->order = 'rgd.group_id'; // or something else
Data checking:
$dataProvider = new CActiveDataProvider('UserGroupDataRevision', array(
'criteria' => $criteria,
)
);
var_dump($dataProvider->getData());
die();

CakePHP counterCache joining irrelevant tables to update counter

I have a User model and a Message model.
The Message model is linked to the User model twice like this:
public $belongsTo = array(
'UserSender' => array(
'className' => 'User',
'foreignKey' => 'sender_id',
'counterCache' => array(
'messages_sent_count' => array(
'is_deleted' => FALSE
)
)
),
'UserRecipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id',
'counterCache' => array(
'messages_received_count' => array(
'is_deleted' => FALSE
),
'messages_unread_count' => array(
'is_deleted' => FALSE,
'is_read' => FALSE
)
)
),
'Operator' => array(
'className' => 'Operator',
'foreignKey' => 'operator_id'
)
);
Besides the User model, the Message model also $belongsTo the Operator model. The Operator model is irrelevant to the message count for the users, but its table is still being joined in the count query, as debug shows:
'query' => 'SELECT COUNT(*) AS `count` FROM `database`.`messages` AS `Message` LEFT JOIN `database`.`operators` AS `Operator` ON (`Message`.`operator_id` = `Operator`.`id`) LEFT JOIN `database`.`users` AS `UserSender` ON (`Message`.`sender_id` = `UserSender`.`id`) LEFT JOIN `database`.`users` AS `UserRecipient` ON (`Message`.`recipient_id` = `UserRecipient`.`id`) WHERE `Message`.`is_deleted` = '0' AND `Message`.`sender_id` = 389',
'params' => array(),
'affected' => (int) 1,
'numRows' => (int) 1,
'took' => (float) 394
For the sake of simplicity I've actually excluded one more model that the Message model $belongsTo, but the above query shows the problem.
The counterCache function does a quite expensive query just to update the counter. Is there a way to maybe override or adjust the counterCache method to not join irrelevant tables in the query?
I can't test it right now, but since the recursive setting used by Model::updateCounterCache() is hard-coded based on whether conditions are defined for the counter cache field, the only way to change this (besides completely reimplementing Model::updateCounterCache()) is probably to modify the count query in Model::_findCount() or Model::beforeFind() of your Message model.
public function beforeFind($query) {
// ... figure whether this is the count query for updateCounterCache,
// maybe even try to analyze whether the passed conditions require
// joins or not.
if(/* ... */) {
$query['recursive'] = -1;
}
return $query;
}
Depending on how much control you'll actually need the containable behavior might do the trick too, it sets recursive to -1 in case no containments are being passed
$Message->contain(); // === recursive is being set to -1 in before find callback
$Message->delete(123);

CakePHP drop down- list usernames and company names

Hey have got a page which allows businesses to send invoices to customers (both individuals and other businesses). The Receiver input field allows the user to select which customer to send the invoice to. At the moment the drop down lists businesses, but we want that list to include individuals (User model/table) too. So if the account (Account model/table) doesn't have a company_name then display the user's username.
Below is what I have tried to try and get it working, doesn't give errors, but doesn't show anything on the drop down list. How could I achieve what we want?
Invoice Controller:
$name = "";
if ('Account.company_name' != null) {
$name = 'Account.company_name';
} else {
$name = 'User.username';
}
$accounts2 = $this->User->find('list', array(
'fields' => array('account_id'), 'conditions' => array(
'id' => $this->Auth->user('id'))));
$accounts = $this->User->Relationship->find('list', array(
'fields' => array('receiver_id'),
'conditions' => array('sender_id' => $accounts2)));
$receivername = $this->Account->find('list', array(
'fields' => array($name),
'conditions' => array(
'id' => $accounts)));
View:
echo $this->Form->input('receiver_id', array(
'label' => 'Receiver: ',
'type' => 'select',
'options' => $receivername));
'Account.company_name' != null
is obviously always true, as a string is not null. So
if ('Account.company_name' != null) {
$name = 'Account.company_name';
} else {
$name = 'User.username';
}
always do $name = 'Account.company_name';
you can define a temporary virtual field 'display_name' and code like below
$this->Account->virtualFields['display_name'] = "IF(Account.company_name='' OR Account.company_name IS NULL, (SELECT u.name FROM users u WHERE u.account_id = Account.id),Account.company_name)";
$list = $this->Account->find('list',array(
'fields' => array('Account.id','Account.display_name')
));